Files
alkgit/docs/architecture/decisions/007-acl-before-advertisement.md
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

3.4 KiB
Raw Permalink Blame History

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)