--- status: reviewed last_updated: 2026-09-30 --- # Overview: alkgit ## Purpose alkgit is the git payload service of the alk family: a pure protocol crate (per ADR-010, following the alktty/alktunnels template) implementing the git smart protocol over alkcall channels — the `alk/git` ALPN. It provides repository storage as backend traits (with a feature-gated gitoxide implementation), the git smart protocol as producer/consumer halves, and nothing else: no binary, no front doors. The original framing of this repo (a monorepo with an `alkgitd` binary and its own http/ssh crates) was an init-agent artifact corrected by OQ-09/ADR-010; `docs/research/vision.md` is amended accordingly. ## The one-line architecture **One protocol crate: producer + consumer + backend traits, gix behind a feature.** Doors (alkhttp, alkssh, alknet) expose it; assembly belongs to downstream consumers. ## Crate map Single crate `alkgit`: | Half | Contents | POC evidence | |---|---|---| | Producer | `GitAdapter` (`alk/git` ALPN via alkcall `ProtocolHandler`; parses the ADR-016 request-line preamble), channels `register_openable` (`channels/git/sub` — open-op params `{repo, service}`, the negotiation + service selector + ACL point; ADR-016) | POC-1 verbatim | | Consumer | `GitSession` typed client (`connect_direct`, `open_via_channels`) — ADR-017: `ls_refs`/`fetch`/`push`; fetch via gitoxide client machinery over a custom alkcall Transport, push hand-rolled to ADR-013's shapes; the replication/mirroring primitive for alknet | new, specified (grammar capture-grounded: ADR-013/014 + captures) | | Substrate | duplex session + stateless request/response layer (ADR-005); wire framing, V2 state machines (ADR-003) | POC-1, POC-3 | | Backends | `GitRegistry` (+ write supertrait), `GitRefs`, `GitPackGen`, `GitPackIngest` traits (signatures pinned: ADR-018); impls behind the default-on `gix` (engine) and `registry-file` (records) features | POC-2 (gix impl) | | Management ops | `git/repo/*` call ops over `GitRegistryStore` (ADR-012 §3) — the JSON half alongside the `alk/git` open op (first dual-kind payload; ADR-012 §5); types/schemas pinned in backend.md §"Registry types and schemas" | thin over the store trait | Feature model: `gix` (engine impls) and `registry-file` (record store + ops) are independent default-on seams (ADR-012 §4); `default-features = false` gives the wire/protocol layer without either (wasm-clean as a side effect, not a goal); the `sha256` passthrough and (eventually, in alkhttp) the `git` door feature ride the same pattern. Doors live in the door crates — see [doors.md](doors.md). ## Security invariants (spec-level, carried from vision/principles) 1. **Authenticated by default** — anonymous fetch only on explicitly- public repos; push always authenticated. 2. **Visible-surface = authorized-surface** — alkcall ACL end-to-end; internal ops never wire-callable. 3. **ACL before advertisement** — nothing is emitted before the check (ADR-007); the channels open-op carrying the repo id is the natural enforcement point on the native path. 4. **Registry-resolved repo identity** — wire names are ids, never paths (ADR-008). 5. **No secret material on the wire or at rest outside alkvault** — metadata holds vault references; no env-var credential reads. (In v1 alkgit's metadata holds no credential-shaped material at all, so nothing is vault-placed — ADR-011 §5.) 6. **No shelling out to `git`** — pure Rust on gix primitives. 7. **Bounded resources** — every session carries `Limits` (ADR-009). 8. **Honest capability advertisement** — advertise exactly what we serve (ADR-003). ## What is already validated (POC-backed) - pkt-line over alkcall `BiStream` end-to-end (POC-1 → producer half). - Pack generation pipeline streaming with O(counts) memory (POC-2 → gix backend impl). - Smart-http streaming both ways (POC-3 → the stateless substrate that alkhttp's future `git` feature maps onto; `docs/research/poc3-findings.md` §alkhttp fit is the mounting reference). The full V2 fetch path against real git 2.43 is proven; receive-pack and multi-round negotiation are design-complete against real-client captures (ADR-013, ADR-014 — `push-captures.md`, `negotiation-captures.md`). ## Design Decisions | ADR | Decision | Summary | |---|---|---| | [001](decisions/001-crate-decomposition.md) | Crate decomposition | **superseded by ADR-010** | | [002](decisions/002-front-door-blind-core.md) | Session boundary | (identity, repo, service, stream, limits) — unchanged, load-bearing; service per ADR-016 | | [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only fetch both doors; push is V0-framed by upstream design (ADR-013); honest advertisement | | [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` gen / `data::input` ingestion | | [005](decisions/005-session-substrate-types.md) | Substrate types | duplex + stateless APIs over one state machine | | [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition | **superseded by ADR-010** (mounting → alkhttp feature) | | [007](decisions/007-acl-before-advertisement.md) | ACL first | no ref/capability line before ACL passes | | [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs | | [009](decisions/009-bounded-resources-budget.md) | Budgets | every session carries limits | | [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | single crate, producer/consumer halves, no doors/binary | | [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in repo records, policy in core, vault-nil (amended: ADR-015) | | [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | read/write split, file default, CRUD ops, feature split | | [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push machine, thin-pack acceptance, unpack-first CAS | | [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` | | [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage gate, create seeds manage | | [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, custom alkcall Transport + gix-protocol fetch, hand-rolled push | | [018](decisions/018-backend-trait-signatures-and-storage-error-model.md) | Trait signatures + storage errors | object-storage trait signatures pinned, `StorageError` for traits 3–5, `&RepoRecord` repo param | ## Open Questions Key questions tracked in [open-questions.md](open-questions.md): - **OQ-03**: publish-time API freeze inventory (the `git/repo/*` op set and the trait family enter it; ADR-012, ADR-013/014's trait additions; the ADR-015 three-action grant shape must land in it; the ADR-016 native preamble wire shapes — `{repo, service}` params schema and the request-line grammar — entered it; ADR-017's `GitSession` public API enters it; ADR-018's trait signatures + shared types + `StorageError` complete the backend-seam inventory). - **OQ-05**: sha256 policy (deferred(scope), low). - **OQ-16**: grant-key identity namespace (deferred(scope); blocks nothing in v1 — ADR-015 §7). Resolved this cycle: OQ-04 (ADR-013 — receive-pack state machine, capture-grounded), OQ-02 (ADR-014 — negotiation ack loop). Earlier: OQ-08 (ADR-011 — per-repo authorization, grants in records, vault-nil), OQ-06 (ADR-012 — `registry-file` default, persistence adapters additive), OQ-07 (ADR-012 — CRUD ops shipped External with scope+grant ACL, gate amended by ADR-015), OQ-09/OQ-01 (ADR-010 — pure protocol crate).