--- status: draft last_updated: 2026-09-21 --- # alkgit: Git Smart Protocol (wire layer) ## What it is The wire half of the `alkgit` protocol crate: pkt-line session substrates, protocol V2 state machines (advertisement, ls-refs, fetch, receive-pack), and the substrate types that keep doors and handlers from touching `futures_io` or raw packetline APIs. Depends on alkcall and — only under the `gix` feature — the backend implementations (ADR-010). The wire layer itself is backend-trait-only, which is what makes `default-features = false` compile without gix. ## Substrate layer (ADR-005) Two session entry points over one state-machine core. ADR-005 owns the full decision (what each substrate owns and why); the surface is: - **Duplex session** (the `alk/git` ALPN producer, channels-opened git sessions, embedder stream doors) — input: (peer identity, resolved repo id, duplex stream, `Limits`). Encapsulates the split/compat/packetline bridge, the request reader (delim-aware parsing, `reset()` discipline, break-on-error), and the sideband writer. - **Stateless session** (smart-http doors, e.g. alkhttp's future `git` feature) — input: (peer identity, resolved repo id, request-reader, response-writer, `Limits`) per http POST; adds the http-framing rules (capability-dump skip, flush-only responses, probe handling). IO-abstract: the door supplies reader/writer; see [doors.md](doors.md) for the mounting. Both substrates feed the same V2 state machines; statelessness is a substrate property (per-request state), not a protocol fork. ## Protocol core (ADR-003: V2-first) ### Advertisement - Emitted once per duplex session (never between commands — real git hangs on re-advertisement); per-request-set on http (stateless: the client re-sends the dump). - Honest capability list: exactly what we serve (`ls-refs=unborn`, `fetch=wait-for-done` for the v1 done-path policy — ADR-003 pins the values; OQ-02 may extend them when multi-round lands, `object-format=sha1`). Unimplemented features are declined by omission (validated against real git, POC-1). `git-upload-archive` is not served (fixed refusal — ADR-008's never-execute rule, ssh analog in doors.md). ### ls-refs - Parse `command=ls-refs` (peel, symrefs, ref-prefix), stream ref lines from the backend's listing, flush. `ref-prefix` filtering is client-driven. ### fetch - Parse wants/haves/done/args; object-format check (reject mismatches — POC-1 to-do). - Negotiation policy (v1 initial): full-closure pack on `done` (POC-validated). Multi-round ack/NAK negotiation: **OQ-02**. - Pack generation via `GitPackGen` (ADR-004), streamed over sideband on duplex / sideband-in-response on http; generation runs on `spawn_blocking` with the owned handle moved in (POC-2's shape: store shared, handle per session, generation on blocking threads). - Round/haves budgets enforced here (ADR-009). ### receive-pack (push) - Parse update requests (` ` + shallow lines), ingest the pack stream (POC-3-verified: request bodies stream), apply CAS transactions, emit status report. The capability set, shallow policy, CAS timing, and exact status-report shape are OQ-04's investigation target (design intent: shape per OQ-04's resolution). - Ingestion via `GitPackIngest` (ADR-004); validation pending — **OQ-04** (includes the receive-pack version surface, which ADR-003's fetch-V2 decision does not cover). ### Error taxonomy - io errors are terminal (session ends); protocol errors map to pkt-line error bands (duplex) or http status + body (stateless). Substrate-level `thiserror` enum; no panics in library code (convention 2). ## Limits Every session carries `Limits` (ADR-009): negotiation rounds, haves per round, receive-pack max size, wall clock, sideband chunk size (fixed 65000), advertisement ref cap. Missing `Limits` is a type-level error. ## Public API surface (v1) Crate-root re-exports (alktty pattern; the full list in [backend.md](backend.md) §public API): `GitAdapter` + `register_openable`, `GitSession`, substrate types, `Limits`, the backend traits (ADR-010's seam), protocol error enums; gix-feature types under the feature. Publish-freeze point: **OQ-03**. ## Design Decisions | ADR | Decision | Summary | |---|---|---| | [002](decisions/002-front-door-blind-core.md) | Session boundary | duplex + stateless entry points | | [003](decisions/003-protocol-v2-first.md) | V2-first | honest advertisement, V0/V1 declined | | [004](decisions/004-pack-pipeline.md) | Pack pipeline | generation on blocking threads, O(counts) | | [005](decisions/005-session-substrate-types.md) | Substrate types | request reader, sideband sink, http framing rules | | [009](decisions/009-bounded-resources-budget.md) | Budgets | `Limits` in every session tuple | | [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | wire layer is backend-trait-only | ## Open Questions - **OQ-02**: V2 multi-round negotiation (open — efficiency, not correctness). - **OQ-04**: receive-pack state machine + validation (deferred(unclear)). - **OQ-03**: publish/API freeze (partially resolved — single-crate shape settled by ADR-010). - **OQ-05**: sha256 policy (deferred(scope)). ## References - `docs/research/poc-1-findings.md`, `docs/research/poc2-findings.md`, `docs/research/poc3-findings.md` (the normative wire behavior — observed against real git, not docs' grammar) - `docs/research/git-protocol.md` (inventory + observed corrections) - `docs/research/gitoxide.md` §"Wire format" (packetline contracts) - alktty `wire.rs`/`session.rs`/`adapter.rs` (the template's half shapes)