--- status: draft last_updated: 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::Store` sharing: `Arc` 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 raw `Cache`. - Ref listing for advertisement: refs + peeled tags + symref targets, the ls-refs response data. - Ref transactions: CAS apply for receive-pack (`gix-ref` transaction 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::Write` consumer). Negotiation-agnostic: negotiation produces the boundary sets, the writer streams them (ADR-004). - Encodes the POC-2 composition: tip peeling → commit-ancestry walk → `TreeContents` count → 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-pack` streaming-input + `gix-fsck` + `gix-ref` transactions. Shape is designed (ADR-004); validation against real `git push` is 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::check` consumes 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_lot` short-held locks (project convention 3). - odb handles: per-session, owned, moved into `spawn_blocking` tasks. - 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](decisions/001-crate-decomposition.md) | Crate decomposition | core = storage, transport-blind | | [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` generation, `data::input` streaming ingestion | | [007](decisions/007-acl-before-advertisement.md) | ACL before advertisement | core supplies rule inputs, adapters enforce | | [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs | | [009](decisions/009-bounded-resources-budget.md) | 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"