refactor(architecture): ADR-010 — pure protocol crate (alktty template)

Structural decision (OQ-09 resolved): alkgit follows the alktty/
alktunnels template — a single published protocol crate on alkcall
channels, no binary, no front doors.

- ADR-010 supersedes ADR-001 (crate decomposition) and ADR-006
  (http router factory); both marked Superseded
- Single crate at repo root: Cargo.toml with gix feature (default-on
  backend implementations; wire layer compiles without it —
  gix-hash always-on with sha1 per the compile-time-rejected
  invariant), crates/ workspace deleted, src/lib.rs stub in place
- doors.md replaces http.md/ssh.md/alkgitd.md: alkhttp git-feature
  sequencing (after first publish), alkssh requirement (fixed-grammar
  exec dispatch), native alk/git path, downstream assembly
- backend.md replaces storage.md: GitRegistry/GitRefs/GitPackGen/
  GitPackIngest traits (ingest validates, refs commits — single CAS
  home), gix feature encodes POC-2 prerequisites
- transport.md reframed for the single crate; backend traits replace
  hook traits in the public API
- OQ-09 resolved (all five sub-decisions in ADR-010), OQ-01 resolved
  (subsumed), OQ-03 narrowed to publish-freeze, OQ-08 narrowed to
  registry identity + vault placement, OQ-07 rescoped to the gix
  feature's registry impl
- vision.md v2: single-binary/monorepo framing corrected as
  init-agent artifact; POC checklist marked complete
- AGENTS.md + .opencode agent specs updated to the new shape

Verification: cargo build (default + no-default-features), cargo test
--all-features, clippy --all-features -D warnings, fmt --check all
pass. Third review round: zero critical, all warnings/suggestions
addressed (GitPackGen signature amended in ADR-004, stale anchors
fixed, ADR-006 body tense normalized, CAS split stated, vision
residuals cleaned).
This commit is contained in:
glm-5.3-flash committed 2026-09-21 10:54:03 +00:00
1 parent de922253a4
commit 86bf5a0cf0
37 files changed
+730 -947

No files matched your search

+61 -85
View File
@@ -7,113 +7,89 @@ last_updated: 2026-09-21
## Purpose
alkgit is a self-hosted git server: repository storage, the git smart
protocol served over http and ssh interfaces, and the `alkgitd` binary that
assembles it all. It exists to provide a small, security-first git platform
whose exposure model inverts the mainstream pattern (visible-surface =
authorized-surface, authenticated-by-default, no plaintext secrets, no
plugin execution). See `docs/research/vision.md` for the full WHY.
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
**Storage + ACL + protocol adapters.** `alkgit-core` owns repository storage
and access-rule types; `alkgit-transport` owns the git protocol state
machines; `alkgit-http` and `alkgit-ssh` are thin front doors that
authenticate, resolve the repo, and hand a session to the transport;
`alkgitd` assembles everything with config, TLS/ACME, and the vault.
**One protocol crate: producer + consumer + backend traits, gix behind a
feature.** Doors (alkhttp, alkssh, alknet) expose it; assembly belongs to
downstream consumers.
## Crate map
| Crate | Owns | Depends on | Never depends on |
|---|---|---|---|
| `alkgit-core` | repo registry/storage roots, odb wrappers, ref store, pack generate/ingest, fsck, ACL input types | gix crates, alkcall (ACL types only) | any front-door crate |
| `alkgit-transport` | pkt-line sessions, V2 advertisement/state machine, ls-refs, fetch, receive-pack | `alkgit-core`, alkcall, gix-packetline | alkhttp, alkgit-ssh |
| `alkgit-http` | smart-http endpoints over alkhttp | transport, core, alkhttp | alkgit-ssh, alkgitd |
| `alkgit-ssh` | git-command dispatch for exec requests over alkcall channels | transport, core, alkcall | alkhttp, alkgitd |
| `alkgitd` | binary: config, assembly, TLS/ACME, listeners, vault wiring | everything | — |
Single crate `alkgit`:
Dependency rules (ADR-001, ADR-002):
| Half | Contents | POC evidence |
|---|---|---|
| Producer | `GitAdapter` (`alk/git` ALPN via alkcall `ProtocolHandler`), channels `register_openable` (repo id in open-op params — the negotiation + ACL point) | POC-1 verbatim |
| Consumer | `GitSession` typed client (`connect_direct`, `open_via_channels`) — the replication/mirroring primitive for alknet | new, small (TtySession analog) |
| Substrate | duplex session + stateless request/response layer (ADR-005); wire framing, V2 state machines (ADR-003) | POC-1, POC-3 |
| Backends | `GitRegistry`, `GitRefs`, `GitPackGen`, `GitPackIngest` traits; gix impl behind the default-on `gix` feature | POC-2 (gix impl) |
1. `alkgit-core` + `alkgit-transport` are **front-door-blind**: they consume
(identity, repo id, duplex stream, limits) and depend on alkcall types
only. No http types, no ssh channel types below the stream.
2. `alkgit-http` and `alkgit-ssh` never depend on each other.
3. `alkgitd` is the only crate allowed to know the whole graph.
4. A downstream application (gitea-like) embeds core + transport and brings
its own front doors; the admin API is a set of alkcall ops it may use or
replace.
Feature model: `default-features = false` gives the wire/protocol layer
without gix (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, all components honor)
## Security invariants (spec-level, carried from vision/principles)
These are the load-bearing rules from `docs/research/vision.md` and
`docs/research/alk-stack.md`; each component doc references them where
concrete:
1. **Authenticated by default** — anonymous fetch exists only on
explicitly-public repos; push is always authenticated.
2. **Visible-surface = authorized-surface** — via alkcall ACL; ops with
`Visibility::Internal` are never wire-callable; admin ops ride an admin
surface, never the git traffic surface.
3. **ACL before advertisement** — access is checked before any ref line or
capability line is emitted (ref names leak repo existence) (ADR-007).
4. **Registry-resolved repo identity** — wire-supplied repo names are IDs
resolved to server-configured storage roots; never used as paths (ADR-008).
5. **No secret material on the wire or at rest outside alkvault** — metadata
holds vault references only; outbound credentials flow through alkcall
`Capabilities` (alkcall ADR-010), and no handler reads credentials from
env or files (the no-env-vars invariant).
6. **No shelling out to `git`** — serving path is pure Rust on gix
primitives (GPL hygiene + no process-injection surface).
7. **Bounded resources** — every session carries wall-clock, size, and round
limits (ADR-009); unbounded loops/buffers are bugs.
8. **Honest capability advertisement** — the protocol advertises exactly
what we serve (ADR-003).
## Interfaces (the boundary shape)
The core boundary, from POC-1 (`docs/research/poc-1-findings.md`, follow-up 4) and the
"ALPN as a service" rule in `docs/research/vision.md`:
- **Transport input**: one session = (peer identity from alkcall
`AuthContext`, repo id resolved against the registry, a duplex byte
stream, session limits). For the stateless http door this becomes
(identity, repo, request-reader, response-writer, limits) — the same state
machines, a different substrate (ADR-005).
- **Storage input**: the transport asks core for (a) ref advertisement data,
(b) pack generation for a want/have set, (c) pack ingestion + ref
transactions for receive-pack. Storage never sees pkt-lines.
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.
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).
- Pack generation pipeline streaming with O(counts) memory (POC-2).
- Smart-http streaming both ways through alkhttp custom routes (POC-3).
- 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 is
designed but not yet exercised (OQ-04 tracks the POC/validation gap).
designed but not yet exercised (OQ-04).
## Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | 5 crates: core, transport, http, ssh, alkgitd |
| [002](decisions/002-front-door-blind-core.md) | Front-door-blind core | Session boundary = (identity, repo, stream, limits) |
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only fetch on both doors; honest advertisement; multi-round negotiation sequenced (OQ-02); push surface OQ-04 |
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | gitoxide `data::output` generation, streaming-input ingestion |
| [005](decisions/005-session-substrate-types.md) | Substrate types | Duplex + stateless session APIs over one state machine |
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition | Router factory in alkgit-http (**proposed**, OQ-01) |
| [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 |
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | **superseded by ADR-010** |
| [002](decisions/002-front-door-blind-core.md) | Session boundary | (identity, repo, stream, limits) — unchanged, load-bearing |
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only both doors; 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 |
## Open Questions
Key cross-cutting questions tracked in [open-questions.md](open-questions.md):
Key questions tracked in [open-questions.md](open-questions.md):
- **OQ-01**: http adapter composability (where the smart-http routes live
for downstream embedding) — affects http.md and ADR-006.
- **OQ-04**: receive-pack (push) validation gap (high — the
always-authenticated half of the wire surface).
- **OQ-08**: identity sources per front door (how http and ssh authenticate
peers in v1).
- **OQ-06**: metadata store backing for the registry (config-file vs
embedded store vs alkcall-hosted).
- **OQ-06**: registry backing store for the gix feature (deferred on
scale requirements).
- **OQ-08**: registry identity space + vault placement (narrowed by
ADR-010).