Resolves review 001 finding A-1 (critical): ADR-012 §3's "scope
git:admin OR ownership" gate is not expressible in alkcall's
AccessControl (AND-composition). Resolution is the review's option (a)
shape with the OR-term generalized: the per-repo grant action set gains
manage, authorize(record, identity, read|write|manage) becomes the
single policy function for git access and repo administration, and the
delete/update/get gate is admin scope OR manage grant (handler-side,
generic FORBIDDEN, unknown-repo = unauthorized per ADR-008). Repo
create seeds the creator's {read, write, manage} grants —
administration is grantable, so collaborators/bots/app-compiled roles
work without global scopes. Ownership stays as alkcall spawn-tracking
(mint at create unchanged); "ownership never implies git access" is
superseded.
- ADR-015 (new): manage grant tier, op gate, flat-grants-as-replication-
substrate, opaque grant-key rule
- ADR-011: action set + policy domain amended, references updated
- ADR-012 §3: gate table replaced, two-tier paragraph superseded
- backend.md/doors.md/overview.md: gate + grant restatements, ADR tables
- OQ-16 (new, deferred(scope)): grant-key identity namespace —
globally-comparable ids for cross-assembly/replicator grant state;
tracker task tasks/architecture/oq-16-grant-identity-namespace.md
- review 001: A-1 marked resolved (ADR-015)
- vision.md: supersession notes (Internal-ops framing, v1 grant set)
Verification: cargo test, clippy -D warnings, fmt --check, doc --no-deps
all clean.
6.7 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-25 |
Doors: how alkgit is exposed
What this is
alkgit (per ADR-010) owns no front doors. This document records the two door mappings that exist or are planned in the family, and the requirements alkgit places on each. The protocol crate itself is door-blind (ADR-002): every door converges on the same two substrates.
The door pattern
Doors are family infrastructure: alkhttp (exists), alkssh (planned, after alksocks), the alknet rewrite (coming). A door wraps alkcall's producer/consumer in its wire protocol; services like git, tty, tunnels, and socks5 are payloads doors optionally expose. Downstream consumers (our platform deployment, a future gitea-like app) assemble the doors they want with the payloads they want.
Auth semantics are the door's auth (http: the door's token mechanism; ssh:
the door's key-based identity) resolving an alkcall Identity (the
identity-extractor seam that ADR-006 needed exists only in the door, where
it belongs). alkgit consumes the resolved identity and the registry
record: the per-repo check is alkgit-core's authorize policy function
(ADR-011, ADR-015 — public+read anonymous-first-class, write always
authenticated+granted, manage for repo administration), run at
ADR-007's step-3 position by every door and by the channels open-op
gate. There is no door-specific auth surface
in alkgit and no scope constant — per-repo grants replaced the
tty:open-style gate (the single-scope shape cannot express
anonymous-public fetch).
alkhttp git feature (http mounting)
Scope: an alkhttp feature that maps two route shapes onto alkgit's stateless substrate (ADR-005):
| Route | Service |
|---|---|
GET /{repo}/info/refs?service=git-{upload,receive}-pack |
advertisement (smart prefix + capability dump + flush) |
POST /{repo}/git-upload-pack |
V2 fetch commands (one command per POST) |
POST /{repo}/git-receive-pack |
push (streaming ingestion, budgeted body) |
GET /{repo}/info/refs?service=git-upload-archive |
honest refusal (not served) |
{repo} is the registry id (ADR-008); resolution + ACL run before any
response byte (ADR-007).
Sequencing: the feature requires alkgit on crates.io (optional
dependencies must resolve), so it lands in alkhttp 0.6 after alkgit's
first publish. Until then, git-over-http is served by any downstream that
mounts the stateless substrate directly — POC-3's httpservice.rs is the
reference implementation of exactly that mapping.
Framing facts the feature must honor (all POC-3-validated, encoded in
the substrate, not re-decided): responses end at flush (never 0002);
top-level flush is one-shot per response (the receive-pack sideband
report's inner flush lives inside band-1 and is part of the report
framing — ADR-013 §8; the rule governs protocol-level sections only);
flush-only POSTs are probes answered
200-empty; request bodies stream (no accumulation); response bodies
stream under back pressure (bounded mpsc → Body::from_stream); request
bodies carry a budget (ADR-009 — alkhttp custom routes are unbounded by
default). Push framing per ADR-013 §12 (probe/Content-Length/chunked,
request/result content types).
alkssh (git-over-ssh, future)
alkgit's requirement on alkssh (to record in alkssh's spec when it
exists): parse the exec-request string with a fixed grammar —
git-upload-pack '<repo>' / git-receive-pack '<repo>' — never shell-
interpret it (ADR-008's never-execute rule), map the door's key-based
identity to the alkcall identity space, resolve the repo id against the
registry, run ACL, and hand (identity, repo, post-auth stream, limits)
to alkgit's duplex session. git-upload-archive gets a fixed refusal.
V2 is expected (ADR-003); GIT_PROTOCOL=version=2 rides the ssh env
mechanism.
Interim: no git-over-ssh path ships with alkgit. A downstream that needs it before alkssh lands can terminate wire-ssh itself (russh or otherwise) and consume the duplex session — that is exactly the embedder path ADR-002 defines, and it works today against the POC-1 shape. It is an embedder assembly concern, not alkgit scope.
The alkcall-native path (no adapter at all)
The alk/git ALPN producer and the channels open-op (repo id in the
open-op params) need zero door code — POC-1 is that shape verbatim. Any
alkcall-speaking client (including alkgit's own consumer half,
GitSession) can use it. This is the baseline path; the http/ssh doors
are conveniences layered on top for stock git clients.
Assembly (downstream responsibility)
There is no alkgit binary (ADR-010). A deployment assembles: alkcall
connection sources (alkhttp/alktls for http, alkssh later, raw ALPN for
the native path) + GitAdapter + a backend implementation (the gix
feature's, or its own) + Limits from config + vault for any credential
material. Reference sequence for our platform deployment lives in that
deployment's docs, not here.
Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| 002 | Session boundary | every door consumes the same tuple |
| 003 | V2-first | honest advertisement per door |
| 007 | ACL first | before any protocol byte |
| 008 | Repo identity | wire names are registry ids |
| 009 | Budgets | limits on every session |
| 010 | Pure protocol crate | doors are family infrastructure |
| 011 | Per-repo authorization | authorize policy, grants in records (amended: ADR-015) |
| 012 | Registry + ops | op registration surface, two op kinds (gate amended: ADR-015) |
| 013 | receive-pack | V0-framed push advertisement per door, report framing |
| 014 | Negotiation | one round per POST; ack section is per-round, stateless |
| 015 | Manage grant + op gate | manage tier, admin-OR-manage op gate |
Open Questions
- None. (OQ-08 resolved by ADR-011 — door auth mechanics stay here, registry identity model settled; ADR-012 §3 pins the op registration surface, its gate amended by ADR-015. OQ-16 — grant-key identity namespace — lives at the identity-provider seam, not the door.)
References
docs/research/poc-1-findings.md(duplex shape),docs/research/poc3-findings.md(http mounting evidence, framing facts)- alktty/alktunnels architecture docs (the template this follows)
- ADR-010 (supersedes ADR-001/006; this file replaces http.md, ssh.md, alkgitd.md)