ADR-011 (resolves OQ-08): per-repo authorization — grants live in repo records keyed on the stable logical identity id (alkcall ADR-025, referenced); policy is alkgit-core's authorize() function (public+read anonymous-first-class, write always authenticated+granted); alkgit stores no identity records; vault placement resolved as nothing to place in v1. ADR-012 (resolves OQ-06/OQ-07): registry backing + write surface — GitRegistryStore write supertrait (alknet ADR-035 read/write split shape); registry-file default (per-repo record files + in-memory index, config-seeded, op-mutable, no gitoxide); git/repo/* CRUD ops shipped External with scope+ownership ACL (create mints ownership and seeds creator grants; ownership never implies git access); the two-op-kind classification recorded (open op + call ops from one crate, per alkcall ADR-047); recorded split trigger for a downstream platform crate. Doc sync: backend.md (five-trait family, feature model split, two-op-kinds), doors.md + overview.md (authorize policy, dual-kind crate map), open-questions.md (OQ-06/07/08 resolved), README (ADR table, current state), oq-06 tracker task closed (resolved early). Verification: cargo test (default + --no-default-features), clippy -D warnings, fmt --check.
7.5 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-21 |
Backend traits: the storage seam
What this is
The payload side of the protocol crate: the traits a service deployer
implements (or consumes via the feature-gated gix implementation) so the
git protocol can talk to storage. Per ADR-010 this mirrors alktty's
TtyBackend pattern — traits in-crate, real implementation behind a
feature. Unlike alktunnels (no backend trait), git needs the seam: pack
generation/ingestion is too heavy to hard-wire.
The trait family (ADR-010 sub-decision 4, ADR-011/012)
Five traits, kept small and orthogonal — the protocol crate never sees gix types:
GitRegistry— repo id →RepoRecord(storage root, visibility, grants keyed on the stable logical identity id — ADR-011). Asyncresolve; the authoritative mapping (ADR-008); resolution failure is indistinguishable from authorization failure (ADR-007). Authorization is alkgit-core's policy function evaluated on the record (data in the backend, policy in core — an embedder with a foreign permission system maps its ACL into grants at resolve time).GitRegistryStore: GitRegistry— the write supertrait (put_repo/update_repo/remove_repo; alknet ADR-035's read/write split shape). Thegit/repo/*ops wrap it (ADR-012 §3). Grants ride the record, so grant mutation isupdate_repo(convenience wrappers additive later, two-way).GitRefs— listing for advertisement (refs + peeled tags + symref targets, the ls-refs response data) and ref transactions (CAS apply for receive-pack, name validation per git ref rules + reserved- namespace deny-list).GitPackGen— (repo, wants, haves, limits) → streaming pack (io::Writeconsumer). Negotiation-agnostic. Missing objects abort with an error, never a broken pack (ADR-004).GitPackIngest— client pack stream → indexed pack + fsck/ connectivity report. It prepares the validated ref updates; the transaction itself is applied byGitRefs(single CAS home — ingest validates, refs commits). Budgeted (ADR-009 max pack size); blocking-thread friendly.
Minimal-vs-full was the open sub-question; resolved as full family —
the four traits are each one or two methods plus types, and collapsing
them (e.g. refs into the registry) would force one impl block per
downstream where independent seams are cheaper to satisfy. The gix
feature implements all four; a downstream with its own object store
implements 3–4 and reuses 1–2, or none of it.
Feature model (ADR-012 §4, amends ADR-010's single-gix story)
Two independent seams, two default-on features:
| Feature | Contents | gitoxide |
|---|---|---|
gix |
GitRefs/GitPackGen/GitPackIngest impls — the local-disk object-storage engine (odb/pack/ref/object/fsck components) |
yes |
registry-file |
GitRegistry/GitRegistryStore default impl (per-repo record files + in-memory index, config-seeded, op-mutable, atomic writes) |
no |
default-features = false— wire/protocol layer only (an embedder brings its own backends). Both features default-on (batteries included); they are independent seams — DB-registry + gix-engine and S3-storage + simple-registry combinations both exist downstream.- The
gixfacade crate is not a dependency (ADR-012 §4): the backends drivegix-odb/gix-pack/gix-refat the component level (POC-2's shape); repo opening from a registry-resolved root isgix-discover+gix-odb::Store::at. Verified at implementation. - The registry-file store needs no gitoxide; persistence adapters (SQLite et al.) are future, separate, additive — ADR-012 §2.
- Hash:
sha1pinned (the compile-time-rejected invariant fromdocs/research/gitoxide.md);sha256passthrough feature (OQ-05 policy unchanged). - Encodes the POC-2 prerequisites by construction: odb handle sharing
(
Arc<Store>shared, per-session handles,prevent_pack_unload()+ignore_replacements = true), generation on blocking threads, O(counts) memory, missing-objects abort. - Received-pack ingestion via
gix-pack::data::input(streaming-input)gix-fsck+gix-reftransactions (ADR-004). Validation against realgit pushis OQ-04.
Concurrency model
gixstructures:parking_lotshort-held locks; per-session handles moved intospawn_blockingtasks (POC-2's shape: store shared, handle per session, generation on blocking threads).- Poisoned locks:
unwrap_or_else(|e| e.into_inner())(convention 2). - The traits are
Send + Syncobject-safe; impls run under the adapter's tokio context.GitRegistry::resolveis async (one call per session/request, before the first protocol byte — ADR-011 §6); no sync-hot-path constraint exists here, unlike alkcall's accept loop.
Public API surface
Crate-root re-exports (the alktty pattern): backend traits + types
(including RepoRecord, AccessAction, the authorize policy
function, and the git/repo/* op spec+handler pairs), GitAdapter/
register_openable (producer), GitSession (consumer), substrate
types, Limits, protocol error enums; feature types (GixBackend
family under gix, the file registry under registry-file) exported
under their features. The embedder-facing freeze point remains OQ-03
(narrowed: it is now this crate's own publish).
The two op kinds (ADR-012 §3, the classification)
alkgit ships both alkcall op kinds from one crate — the first family payload to do so (alktty/alktunnels: open ops only; alknet-docker, planned: call ops only):
- Open op (
alk/gitviaregister_openable; repo id in the open-op params) — the binary half: negotiation + ACL point (ADR-007), yields the duplex git session. - Call ops (
git/repo/{create,delete,update,get}) — the JSON half: thinOperationSpec+handler pairs overArc<dyn GitRegistryStore>,Visibility::External, always-on (no gitoxide), gated byrequired_scopes(global tier) + ownership (self-owned tier, alkcall ADR-011 mint-and-own at create). Registered by the assembler wherever it wants them exposed. Managing records (op ACL) and git access (repo grants) are different capabilities — ownership never implies git access (ADR-011, ADR-012 §3).
Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| 004 | Pack pipeline | data::output gen / data::input ingestion |
| 007 | ACL first | registry returns rule inputs |
| 008 | Repo identity | wire names are registry ids |
| 009 | Budgets | limits flow into gen/ingest |
| 010 | Pure protocol crate | traits in-crate, impls behind features |
| 011 | Per-repo authorization | grants in records, policy in core |
| 012 | Registry + ops | write trait, file default, CRUD ops, feature split |
Open Questions
- OQ-04: pack ingestion validation (deferred(unclear)).
- OQ-05: sha256 policy (deferred(scope)).
- OQ-03: publish-time API freeze inventory (the op set enters it).
References
docs/research/gitoxide.md(API contract notes — normative for the gix impl)docs/research/poc2-findings.md(generation pipeline + prerequisites)- alktty
backend.rs/localmodule (the trait + feature template) - alknet ADR-033/035 (the repo/adapter + read/write-split pattern)
- ADR-010 (the structural decision), ADR-011/012 (the seam this doc specifies)