ADR-010 (new): the Backend trait's I/O seams pinned before any code — 'get' returns a crate-internal read cursor (kv engines materialize; 'local' pread loop; pg-lo lo_get windows), 'put' has two named forms (whole-value kv / staged-put large); GC state is store-core-owned via a crate-internal contract-tested engine-state seam on the SQL-backed engines (backends never learn liveness — ADR-005 verbatim); the store core holds the joint entry+pin transaction (corrects ADR-009 §2's ownership statement); kv-tier fleet-validity rule added (mirror of ADR-008 §3); ops pin-token TTL race resolved (the token IS the pin; renewal rides have/re-put; pause-past-TTL falls to delete-then-recover); feature graph pinned (pg-lo does not imply postgres). ADR-011 (new): vocabulary pinned — tier (contract, exactly two) vs engine (concrete impl, exactly five); store instance / node / fleet split (fleet = pool-sharing, not node count — fixes requirements.md's self-contradiction with REQ-2); mem demoted from 'backend'/'testing tier' to the kv tier's third engine (contract-reference engine); fs tier renamed 'large' including the feature name (last free moment before code exists); constructor modes pinned (dual-tier default, kv-only, mem-only) plus the composite fleet-validity predicate. Consistency round across all specs and ADR-003/004/007/008/009: stale pg-lo passages resolved per ADR-009's supersession; ADR-008 owner 'node id' → store-instance id; threshold wording corrected (conservative edge, not midpoint, of the 128-256 KiB crossover zone); SweepReport shape specced; mem classification unified; 008/009 ADR files renamed to match the tier rename; README deferral-policy recap aligned (third category = decided-but-sequenced work, not a parking kind); POC crate list completed. Verification: two independent architecture review rounds (the first found 4 criticals — unpinned trait I/O shapes, missing fleet-state seam, node/fleet self-contradiction, pin-token/TTL conflict — all resolved; final round: zero criticals); all markdown links resolve; ADR tables complete (11 ADRs).
101 lines
4.5 KiB
Markdown
101 lines
4.5 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 2026-10-01
|
|
---
|
|
|
|
# Hashing and keys
|
|
|
|
## What this is
|
|
|
|
The content-address layer: the canonical hash derivation every entry is
|
|
keyed under, the tolerated legacy algorithm, the typed `Key`/`Digest`
|
|
surface the store API speaks, and the physical byte encoding of keys
|
|
inside backends. This is the crate's one-way door: key byte layouts are
|
|
persisted in backend rows and filenames, survive digest-layer evolution,
|
|
and must precede any wire consumer (AGENTS convention 5; phase-0 OQ-BL-03
|
|
residual).
|
|
|
|
## The hash family
|
|
|
|
Three hash-policy classes (ADR-002 §Decision; resolved from OQ-BL-03 after the
|
|
"multi-hash" premise dissolved — nothing forces BLAKE3 once iroh-blobs'
|
|
inheritance is rejected):
|
|
|
|
1. **Canonical — `git-blob-sha-256`:** the git oid derivation
|
|
`H("blob <len>\0" + content)` with SHA-256, uncompressed content.
|
|
Chosen because alkgit *forces* git's derivation by protocol
|
|
definition, and every other consumer (workspaces, appfile) sees
|
|
addresses, not algorithms — so one algorithm domain makes the pool
|
|
one address space and cross-consumer dedup free by construction
|
|
(ADR-005).
|
|
2. **Tolerated legacy — `git-blob-sha-1`:** existing git repositories
|
|
are overwhelmingly SHA-1; the store accepts them under the same
|
|
preamble discipline. Git's own collision-hardened SHA-1 threat model
|
|
is inherited verbatim; the store adds nothing and weakens nothing.
|
|
3. **Not present — BLAKE3:** demoted out of the crate entirely. If a
|
|
chunk-tree transfer encoding is ever adopted, it is a new decision at
|
|
a new layer (ADR-006); its verified whole-contents would register as
|
|
ordinary `git-blob-sha-256` entries.
|
|
|
|
Security posture: one algorithm across consumers is safe here — SHA-256
|
|
has no practical cross-protocol ambiguity at these input shapes, and the
|
|
preamble domain-separates the derivation (ADR-002 §Consequences).
|
|
|
|
## Key encoding (the one-way door)
|
|
|
|
Physical key bytes inside backends (POC #1 finding 5, validated in the
|
|
interop test where an oid produced by *external git* addresses the same
|
|
pool entry the store puts):
|
|
|
|
- **Algorithm identity is part of the key itself** — a per-store
|
|
single-algorithm configuration cannot express git-sha-256 and
|
|
git-sha-1 coexisting in one flat pool, which the external-oid test
|
|
proves is required.
|
|
- **Fixed-length, enum-tagged:** `1 algorithm byte + digest bytes`
|
|
(32 for sha-256, 20 for sha-1). The POC's extra "kind" tag byte is
|
|
dropped — there is exactly one key kind. Algorithm bytes are assigned
|
|
from a single registry constant in ADR-002 so external producers can
|
|
compute them.
|
|
- **Sort stability:** fixed-length enum-tagged keys sort cleanly in a
|
|
total order — required for the sweep's live-set diffs (list() walks
|
|
are plain range scans; ADR-005).
|
|
- **Opaque to backends:** the `Backend` trait speaks opaque byte keys
|
|
(ADR-003); only the store core constructs and interprets typed keys.
|
|
|
|
## The Key/Digest surface (WHAT the API exposes)
|
|
|
|
- `Digest` — an algorithm + digest pair; construction via the canonical
|
|
derivation from content (known length) or from a pre-computed pair
|
|
(interop: `from_git_oid_hex` shape validated in POC #1 finding 4).
|
|
- `Key` — the typed wrapper; `as_bytes()` / `from_bytes()` are the only
|
|
conversions across the backend boundary. Malformed key bytes
|
|
(truncated, unknown algorithm) are rejects, never guesses — the POC's
|
|
length-checking test is the standard.
|
|
- Verification semantics ride the whole-blob posture (ADR-006): put and
|
|
get paths hash-check against the canonical derivation; there is no
|
|
per-range hash relationship under this derivation (preamble includes
|
|
the length), which is the constraint ADR-006 documents rather than
|
|
works around.
|
|
|
|
## Design Decisions
|
|
|
|
| ADR | Decision | Summary |
|
|
|---|---|---|
|
|
| [002](decisions/002-canonical-hash-and-key-encoding.md) | Canonical hash + key encoding | three-tier hash policy; algorithm-in-key byte layout; the declared wire-format ADR |
|
|
|
|
## Open Questions
|
|
|
|
None owned by this document. The interop surface alkgit ultimately
|
|
needs (oid formats, hex/binary conventions at *its* boundary) is OQ-07
|
|
— alkgit's question, not a key-encoding question.
|
|
|
|
## References
|
|
|
|
- `docs/research/phase-0.md` OQ-BL-03 (resolution + key-encoding
|
|
mechanics)
|
|
- `docs/research/poc-trait-dispatch-findings.md` findings 4/5
|
|
(byte-exact git CLI validation; tagged-key coexistence)
|
|
- gix-odb (`/workspace/gitoxide/gix-odb`) — the consumer-side baseline
|
|
for object reads/writes
|
|
- ADR-002; ADR-003 (opaque byte keys at the backend boundary); ADR-006
|
|
(what verification is possible under this derivation) |