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.
5.1 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-21 |
alkgit-core: Storage
What it is
The repository storage layer: repo registry, object database wrappers, ref store, pack generation/ingestion, and access-rule input types. It is transport-agnostic and front-door-blind (ADR-001): it knows nothing about pkt-lines, http, or ssh.
Why this shape
gitoxide provides the primitives (odb, packs, refs, fsck, objects); the
generation-pipeline decision is ADR-004 (POC-2-validated). The crate
exists to (a) wrap gix with a coherent session-safe API (gix_odb::Cache
is not Sync — the sharing rules are enforced by the API in this crate),
(b) own the registry (ADR-008), and (c) hold the ACL input types the
adapters and alkcall share.
Components
Repository registry
- Authoritative (repo id → storage root, visibility, ACL scope) mapping.
- Wire repo names are registry IDs, never paths (ADR-008); resolution failure is indistinguishable from authorization failure (ADR-007).
- Backing store: open — OQ-06. Core defines the trait; alkgitd picks the backing. Metadata holds no secrets (vault references at most).
- Storage roots are server-configured; the registry maps IDs onto them.
Repository access (Repo)
- Opens a (storage root, hash algo) into an object database + ref store.
- Wraps
gix_odb::Storesharing:Arc<Store>shared across sessions, per-session handles (to_handle_arc()+prevent_pack_unload()+ignore_replacements = true— the POC-2 prerequisites). The public API hands out handles, never the rawCache. - Ref listing for advertisement: refs + peeled tags + symref targets, the ls-refs response data.
- Ref transactions: CAS apply for receive-pack (
gix-reftransaction module), with name validation (git ref rules + reserved-namespace deny-list;docs/research/git-protocol.md§security notes).
Pack generation (fetch side)
- Type: (odb handle, wants, haves, limits) → streaming pack (
io::Writeconsumer). Negotiation-agnostic: negotiation produces the boundary sets, the writer streams them (ADR-004). - Encodes the POC-2 composition: tip peeling → commit-ancestry walk →
TreeContentscount → entries → bytes; missing objects abort (never emit a broken pack); entry statistics surface as metrics. - Memory profile is O(counts) — the budget model (ADR-009) bounds aggregate work, not internal buffering.
Pack ingestion (receive side)
- Client pack stream → indexed pack + fsck/connectivity report + per-ref
CAS application. Uses
gix-packstreaming-input +gix-fsck+gix-reftransactions. Shape is designed (ADR-004); validation against realgit pushis pending — OQ-04. - Received pack size is budgeted (ADR-009 max pack size); ingestion runs on blocking threads like generation.
Access-rule input types
- The types alkcall
AccessControl::checkconsumes for repos: visibility (public/private), identity-based read/write. Deliberately input types: the evaluation function lives in alkcall (single source of authorization logic); invocation/wiring happens in the adapters (ADR-007). Core defines what a repo's rule set looks like. - v1 scope: repo visibility + identity read/write, nothing richer (vision §"Primary deployment target").
Concurrency model
Store/registry structures:parking_lotshort-held locks (project convention 3).- odb handles: per-session, owned, moved into
spawn_blockingtasks. - Ref transactions: serialized per-ref by gix-ref's lock files; the registry mapping's read-mostly synchronization (ArcSwap-class or equivalent) is part of OQ-06's backing decision.
- Poisoned locks:
unwrap_or_else(|e| e.into_inner())per convention 2.
Public API surface (v1)
lib.rs re-exports (module structure per convention 14): registry trait +
types, Repo/handle types, pack generate/ingest types, ref-transaction
API, ACL input types, error enums (thiserror, no panics, no unwrap
outside tests). The embedder-facing freeze point is tracked in OQ-03.
Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| 001 | Crate decomposition | core = storage, transport-blind |
| 004 | Pack pipeline | data::output generation, data::input streaming ingestion |
| 007 | ACL before advertisement | core supplies rule inputs, adapters enforce |
| 008 | Repo identity | wire names are registry IDs |
| 009 | Budgets | limits flow into generation/ingestion |
Open Questions
- OQ-06: registry backing store (deferred(scope) — blocked on metadata-scale requirements).
- OQ-04: receive-pack ingestion validation (deferred(unclear) — pieces decided, push state machine needs a walkthrough/POC).
- OQ-03: embedder-facing API freeze (deferred(scope)).
References
docs/research/gitoxide.md(the API contract notes are normative here)docs/research/poc2-findings.md(generation pipeline + prerequisites)docs/research/git-protocol.md§"Server-side pack generation"