Architecture documentation structure per sdd_process phase 1: - README index (doc table, ADR table, lifecycle), overview with crate map, dependency rules, and security invariants - Component specs: storage, transport, http, ssh, alkgitd (all draft) - ADRs 001-009: crate decomposition, front-door-blind core, V2-first protocol, pack pipeline (data::output generation / data::input ingestion), session substrate types, http adapter composition (proposed, OQ-01), ACL-before-advertisement, registry-resolved repo identity, bounded-resources budgets - open-questions.md: OQ-01..08 with two deferred(scope), one deferred(unclear), door-type definitions, blocker tracker tasks in tasks/architecture/ - v1 ssh-door decision recorded: russh terminates wire SSH in alkgitd; alkcall channels stay the internal substrate (OQ-03 partially resolved) Two review rounds (fresh-context subagent): 4 critical + 17 warnings fixed in round one; zero critical + 4 warnings + 5 suggestions fixed in round two. All ADR/OQ cross-references verified resolving.
6.3 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-21 |
Overview: alkgit
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.
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.
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 | — |
Dependency rules (ADR-001, ADR-002):
alkgit-core+alkgit-transportare 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.alkgit-httpandalkgit-sshnever depend on each other.alkgitdis the only crate allowed to know the whole graph.- 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.
Security invariants (spec-level, all components honor)
These are the load-bearing rules from docs/research/vision.md and
docs/research/alk-stack.md; each component doc references them where
concrete:
- Authenticated by default — anonymous fetch exists only on explicitly-public repos; push is always authenticated.
- Visible-surface = authorized-surface — via alkcall ACL; ops with
Visibility::Internalare never wire-callable; admin ops ride an admin surface, never the git traffic surface. - ACL before advertisement — access is checked before any ref line or capability line is emitted (ref names leak repo existence) (ADR-007).
- Registry-resolved repo identity — wire-supplied repo names are IDs resolved to server-configured storage roots; never used as paths (ADR-008).
- 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). - No shelling out to
git— serving path is pure Rust on gix primitives (GPL hygiene + no process-injection surface). - Bounded resources — every session carries wall-clock, size, and round limits (ADR-009); unbounded loops/buffers are bugs.
- 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.
What is already validated (POC-backed)
- pkt-line over alkcall
BiStreamend-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). 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).
Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| 001 | Crate decomposition | 5 crates: core, transport, http, ssh, alkgitd |
| 002 | Front-door-blind core | Session boundary = (identity, repo, stream, limits) |
| 003 | V2-first protocol | V2-only fetch on both doors; honest advertisement; multi-round negotiation sequenced (OQ-02); push surface OQ-04 |
| 004 | Pack pipeline | gitoxide data::output generation, streaming-input ingestion |
| 005 | Substrate types | Duplex + stateless session APIs over one state machine |
| 006 | HTTP adapter composition | Router factory in alkgit-http (proposed, OQ-01) |
| 007 | ACL first | No ref/capability line before ACL passes |
| 008 | Repo identity | Wire names are registry IDs |
| 009 | Budgets | Every session carries limits |
Open Questions
Key cross-cutting questions tracked in 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).