- new ADR-017: GitSession is a real typed client in v1 (ls_refs/fetch/ push), grounded in the two deployment use cases — the p2p replicator is the named downstream and needs the client protocol layer; the thin-wrapper reading is superseded - fetch client reuses gix-protocol (async-client) over a custom alkcall gix_transport::client::Transport impl (handshake writes the ADR-016 request line on the direct path; the channels open-op params carry it otherwise); push hand-rolled to ADR-013's shapes (gitoxide has no send-pack client) - storage-agnostic: packs stream both ways to caller-owned consumers; no in-session credentials (alkcall transport authenticates); client sessions carry ADR-009 Limits (client is also internet-facing) - manifest: gix-protocol/gix-transport gain async-client features (verified against published tree, MSRV 1.88) - amend ADR-010 (consumer-half bullet) and ADR-012 §4 (the deferred gix-protocol call — resolved; rider superseded); backend/transport/ overview/doors wording; review 001 A-5 marked resolved verification: cargo check (default, --all-features, --no-default-features, --features sha256), cargo +1.88 check, cargo test, clippy -D warnings, fmt --check — clean across the matrix
144 lines
7.5 KiB
Markdown
144 lines
7.5 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 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 (`channels/git/sub`,
|
|
params `{repo, service}` — ADR-016: the params are the negotiation, the
|
|
service selector, and the open-time ACL point; the establisher resolves
|
|
the repo and runs `authorize` with the action the service selects) need
|
|
zero door code — POC-1 is that shape verbatim. On the direct-ALPN path
|
|
`GitAdapter` parses the git-daemon request line
|
|
(`git-upload-pack <repo>\0host=…\0\0version=2\0` /
|
|
`git-receive-pack <repo>\0host=…\0`) as the in-band preamble (ADR-016).
|
|
Any alkcall-speaking client (including alkgit's own consumer half,
|
|
`GitSession` — ADR-017's typed fetch/push client) can use either path.
|
|
This is the baseline path; the http/ssh doors are conveniences layered
|
|
on top for stock git clients, and the replicator path of the p2p
|
|
deployment (direct push/pull between replicators) rides it directly.
|
|
|
|
## 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](decisions/002-front-door-blind-core.md) | Session boundary | every door consumes the same tuple |
|
|
| [003](decisions/003-protocol-v2-first.md) | V2-first | honest advertisement per door |
|
|
| [007](decisions/007-acl-before-advertisement.md) | ACL first | before any protocol byte |
|
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits on every session |
|
|
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | doors are family infrastructure |
|
|
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | `authorize` policy, grants in records (amended: ADR-015) |
|
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | op registration surface, two op kinds (gate amended: ADR-015) |
|
|
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push advertisement per door, report framing |
|
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | one round per POST; ack section is per-round, stateless |
|
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage op gate |
|
|
| [016](decisions/016-native-session-preamble.md) | Native session preamble | `{repo, service}` open-op params, request-line preamble, service in the tuple |
|
|
| [017](decisions/017-consumer-half-git-session.md) | Consumer half | `GitSession` typed client — the direct-connection push/pull primitive |
|
|
|
|
## 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) |