Files
alkgit/docs/architecture/decisions/007-acl-before-advertisement.md
T
glm-5.3-flash 85bde4c241 docs(arch): review-001 doc batch — A-4, D-2, D-3, N-1, N-2
Amendment batch (no new decisions, all doc-level):

- A-4: done-round boundary set is the recognized subset — request
  haves filtered through common_haves, the same honest-boundary rule
  as the ack rounds (never honor an unverified have); amendment clause
  in ADR-014 §2, same rule restated in transport.md §fetch.
- D-2: amendment note on ADR-007 step 3 — the per-repo check is
  ADR-011's authorize policy function (static ACL engine fails closed
  on None identity); step order unchanged.
- D-3: authorized-repo marker added to both substrate input tuples in
  transport.md and to backend.md's public-API list (ADR-007's
  type-level enforcement promise is now findable from the transport
  spec).
- N-1: advertisement ref cap is fail-closed (breach is an error, never
  a silent truncation) — transport.md §Limits.
- N-2: RegistryError::NotFound and authorization failure collapse to
  the same wire error at the variant→wire mapping — transport.md
  §error taxonomy.
- review 001: A-4/D-2/D-3/N-1/N-2 marked resolved.

Verification: cargo doc --no-deps, cargo test — clean.
2026-09-29 08:30:26 +00:00

71 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-007: ACL runs before any advertisement or ref line
## Status
Accepted
## Context
Ref names leak repository existence (and structure). The advertisement is
the first thing a client sees; if ACL runs after advertisement begins, a
denied caller has already learned that a repo (or a ref within it) exists.
The vision's visible-surface = authorized-surface invariant requires the
check to run before the first byte of git protocol content.
POC-1 observed the natural enforcement point on the git:// path: the repo
name arrives in-band in the first request line, *before* any ref data — so
the resolve-then-authorize step sits structurally ahead of the first
emitted line. POC-3 noted the http extra-routes are registered permissive
by default (the gateway bearer layer does not cover them) — making the
http-side check an explicit door responsibility (alkhttp `git` feature), not an inherited
one.
## Decision
Every session/request performs, in order, before any protocol output:
1. Extract the wire repo name (git:// first-request line; ssh exec command
string; http path segment).
2. Resolve it against the registry to (repo id, storage root) — reject
unknown repos with the same error as unauthorized ones (no existence
oracle) (ADR-008).
3. Run alkcall `AccessControl::check(peer_identity)` against the repo's
required access. Read access covers the *whole fetch surface*:
advertisement, ref listing, and pack transfer alike — anonymous access
on explicitly-public repos grants the same full read path
(vision §"Primary deployment target" makes anonymous clone first-class;
the "advertisement is the only anonymous surface" line in the same
doc's principle 1 is read as the *minimum* boundary, and this decision
sets the operative rule).
*(Mechanism amended by ADR-011 §3 (review 001 D-2): the static ACL
engine fails closed on `identity: None`, so it cannot express
anonymous-public fetch. The per-repo check is alkgit-core's
`authorize` policy function evaluated on the registry record; the
alkcall registry gate still applies where the op has scopes. The
step *order* — resolve, then authorize, before any protocol output —
is unchanged.)*
4. Only then hand the session to transport.
Adapters embed this sequence; transport asserts it (the session entry
points take an authorized-repo marker — a type the adapter constructs only
after step 3 passes — so skipping the check is a type error, not a runtime
log) but does not re-check — authorization evaluation lives in one place,
alkcall, and invocation/wiring lives in one place, the adapter.
Push is always authenticated on every repo, no exceptions (vision §
"Primary deployment target").
## Consequences
- No ref/capability line is ever emitted for a repo a caller cannot see;
the gitea-class "enforcement elsewhere" bug is structurally excluded.
- Unknown-repo and unauthorized errors are indistinguishable to callers.
- The check is cheap (registry lookup + ACL check) and runs before any
expensive protocol work.
- http adapters must wire ACL explicitly for their routes (POC-3 showed
alkhttp extra routes default permissive) — doors.md encodes this.
## References
- `docs/research/vision.md` §"Guiding principles" 1–2, §"Immediate threat-model notes"
- `docs/research/poc-1-findings.md` follow-up 4; `docs/research/poc3-findings.md` §"does NOT settle" (auth)
- alkcall ADR-017 (privilege model)
- ADR-008 (repo identity), doors.md (door auth mechanics)