- C-1 verified end to end: the one-way door's bytes read against ADR-018 line by line and exact (registry 0x03/0x04, preimage-identity derivation, fleet threading per §Decision 7); interop green against the real git CLI - C-2 verified: ADR-019's mint-boundary cap + the typed MINT_REFUSED answer; the attack shape on paper; "not silence" exceeded - C-3/C-4 verified as chartered (busy_timeout=5000 pinned; KeyBytes) - review-002 stamped closed with disposition pointers; the prototype branch poc-o-git-objects deleted after parity confirmation - Minor inline fixes: the CHANGELOG [0.1.0] entry absorbs wave 13 (verified counts 218 default / 473 all-features, ADR-018/019 refs), the README names the git-object family honestly - Stamps: the plan's wave-13 row + frontmatter completed, AGENTS.md now publish-ready Verification: cargo test default 155+54+7+2; cargo test --all-features vs the live pglo-poc harness 338+125+7+3; both clippy variants -D warnings clean; cargo fmt --check clean; cargo doc --no-deps clean; fuzz corpus replay 7/7; cargo publish --dry-run --allow-dirty green (94 files).
17 KiB
status, last_updated, dispositions, reviewed_artifacts, tool, reviewer, scope_note
| status | last_updated | dispositions | reviewed_artifacts | tool | reviewer | scope_note | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| closed | Closed 2026-10-18 — the wave-13 review gate stamps the round dispositioned: C-1 by [ADR-018] + the landed family (5a34759, 9134d44), C-2 by [ADR-019] + the landed cap/posture (61966b6), C-3 by the pinned busy_timeout (a5bebc6), C-4 by the KeyBytes/overloads (9c33d79), the alkcall bump (a575588); the prototype branch deleted after parity confirmation. Original scope note: report-only evidence; each finding was recommendation-shape, the addressing sessions re-verified the anchors and re-derived the decisions. |
|
|
desktop review of external POC evidence + targeted source verification (grep/sed) of this session's anchors; no full source re-read (review 001's pass is the standing whole-surface record) | alkgit consumer seat — the first consumer of the store's LFS (POC-L), git-object (POC-O), and registry-reactivity (POC-R) surfaces; findings/asks only, resolutions belong to their own sessions | pre-publish consumer review. Covers only what the POC rounds surfaced; review 001 (the storage-surface general review) remains the crate's own full-surface record. Everything here is a finding-to-verify, not a decided change. |
Review 002 — The First Consumer's Evidence (alkgit's recomposition POCs, pre-publish)
Purpose
Record the findings/asks alkgit's three recomposition POCs surfaced against this crate, before publication, while every still-private change is cheap. C-1 and C-2 are the publish-hold reasons; C-3 rides the same round; C-4 is a non-blocking ride-along candidate.
The three POCs were full end-to-end consumption of real surfaces:
- POC-L — a real
git-lfs3.7.0 client (push/pull/fresh-consumer clone) against theopssurface (blobs/stat|havefor the batch,blobs/fetch|putfor transfer) over mem / dual_tier_sqlite_local / dual_tier_postgres_local; ranged 45 MiB fetch with per-window slice digests; 70 MiB whole fetch; sweep with an external liveness table. - POC-O — this crate as a git object store:
git clone/git push2.43.0 end-to-end from substrate-only storage, thin-pack ingest with bases resolving through a customgix_pack::Findover the store, packs stored as large-tier blobs served throughread_rangewindows, GC reclaim of cached packs and orphans. - POC-R — the reactive/GC machinery consumed by a co-tenant registry composition: pin + bridge + sweep through alkstore's scheduler/queue/leader-lock chain, cross-process.
The consumption itself validated the published design surfaces (verification-on-get survives real ingest; the ranged arm's establish-time bounds held; the pin/bridge/sweep composition worked under a real receive flow; ADR-017's global-pool caveat is the one posture the consumer had to design around). What follows is what the consumption wants changed.
C-1 [publish-decision] — The git-object derivation family: extend the hash registry to non-blob git forms (DQ-1a)
Finding. alkblobs' canonical hash is the blob-only derivation
H("blob <len>\0" + content) — trees/commits/tags derive with
"<type> <len>\0" prefixes, so git's non-blob objects cannot be
stored under their own oids. git's oid function is uniform across
kinds, so the extension is one derivation family, not a kind
matrix: key = [tag] [digest] with two new registry bytes
(0x03 git-object-sha1, 0x04 git-object-sha256), and the stored
preimage is the loose-object form itself (<type> <len>\0 +
content — the preamble lives inside the value). Because a digest
lookup cannot carry a kind, read-side verification re-derives with
key.algorithm() over the stored preimage byte-identically —
verification-on-get, dedup-by-construction, and external-oid
addressing (key = tag + oid, no map) survive unbroken.
Evidence. Working reference: the throwaway prototype branch
poc-o-git-objects (f0b045c) — Algorithm members, derivation arms +
streaming hasher arm, put_git_object(algorithm, len, stream) (the
known-length put path; a git object's length is always known up
front), key decode of the new tags, and tests/git_interop.rs
validated byte-exact against real git hash-object -t (tree/commit/
tag round-trips, dedup, and the negative: the same oid is unreachable
through the blob tags — no cross-family collision). End-to-end: a
custom Find over the extended store drove real clone and push
(PACK-O). main carries none of this (0 hits for the family in
main's src/hashing.rs); the extension is branch-only today.
The parts the prototype does NOT carry (the addressing session's work):
- Fleet put paths — the fleet branches' hasher sites still
hardcode the canonical blob derivation (representative anchor,
verified this session:
src/local.rs:899—Key::derive(content, content.len() as u64)). The derivation must thread through the fleet/non-fleet hasher sites uniformly. Mechanical per the POC, but it is the merge-time work. - The ops surface's answer to the kind question.
Digest::from_git_oid_hex(and any40-hex → which family?constructor) cannot be answered by digest length alone once non-blob forms share the digest space. The ADR owns this decision; unprototyped. - The
statsemantic — in the extended registry,statreturns the stored preimage length (the loose form, preamble included), not the content length. A deliberate, documented difference from the blob family; pin it in the ADR. - ADR-002's domain-separation invariant — this family is preimage-identity hashing (no store-added separation; git's own header is the separation, inside the preimage). It fits ADR-002 only as an annotated exception; the ADR text should carry the annotation explicitly, not leave the invariant silently bent. The invariants that survive verbatim: digest length fixed per algorithm, rejects-never-guesses key decode, total order (0x03/0x04 sort after the blob members), verification-on-get.
- Namespaces — git oids are global by nature; the global-pool caveat (ADR-017) applies unchanged. The consumer accepted that posture for git objects on the same terms as its LFS acceptance; the ADR should state the posture for the git-object family explicitly.
Decision framing. One-way door — key bytes persist in kv rows, local files, and pg-lo companions. Pre-publish is the cheap window: after publishing, this is a 0.2 registry extension with stored-shape migration weight. The store's own ADR conversation owns the merge; the prototype branch is a scratch reference to re-implement from, not to merge.
Verification status: anchors checked this session (branch state, main's absence of the family, the local.rs derive site); the byte-exact claims rest on the branch's CLI-validated tests and the POC-O e2e — re-verify by running the branch's tests during addressing.
C-2 [security posture, publish-decision] — The S-5 admission cap: the concrete attack shape now exists
Finding. Review 001's S-5 named the unbounded capability
resources family and fixed the wire-schema part (the token field's
maxLength/pattern, 437ab7f); the admission cap on the token
registry was judged there as "a mint-contract policy decision, not
security-round material" and recorded as known future hardening
(module doc at src/ops/tokens.rs:182-191 — process-lifetime
retention posture + the deferred admission cap). The consumer's POC
supplies the missing input that judgment said it was waiting for: a
named, client-reachable attack shape.
The shape (POC-L, tests/sweep.rs + the loopback binding):
mint_custodial(namespace, key, guard) binds a put's guard into the
token; the confirm act is left to the caller. On the fleet shape
a TTL bounds the held pin. On a non-fleet store an unconfirmed
token holds its pin indefinitely — custody that never expires,
registered per mint, with no cap on mints. The LFS deployment shape
makes the loop realistic: a hostile client POSTs uploads whose
content never matches the claimed oid (or abandons the confirm step
after each mint) — each minted token is a held pin and a registry
entry, so the loop is unbounded pin-holding plus unbounded registry
growth on any embedder whose door mints tokens per request without
confirming.
Recommended shape recorded (to be re-derived, not adopted as-is): an admission cap at the mint boundary (registry entry count and/or held-pin count, engine of the cap a registry-level knob), and/or the fleet-form TTL posture made available (and loud in the docs) for non-fleet embedded deployments whose door cannot prove confirm. The review 001 judgment ("a refused mint answers a committed put") still stands as the counter-consideration: the cap must answer the already-verified put honestly, which is exactly why it is a mint-contract decision and needs its own session.
Decision framing. Publish-decision: either land the cap pre- publish, or the ADR/docs record the non-fleet no-confirm custody posture as an explicit embedder responsibility with the attack shape on paper. What should not happen is publish with silence — the current state is the worst of both (deferred-and-undocumented).
Verification status: the attack shape is POC-L's recorded,
deliberately-open custody leak (it was NOT patched in the POC — put
on record instead); the anchors (tokens.rs mint/custody/registry
sites) were re-read this session. Re-verify during addressing by
reproducing the loop against a mem store.
C-3 [contract, ride-along] — sqlite busy_timeout is coincidence, not contract
Finding. The sqlite engine sets its durability posture
explicitly — PRAGMA journal_mode = WAL + PRAGMA synchronous = NORMAL (src/sqlite.rs:156-163, verified) — but sets no
busy_timeout. Contention behavior rides rusqlite's implicit
5-second default, which happens to equal alkstore's explicit 5000
ms (alkstore-sqlite sets its own). The first consumer's deployment
shape is a co-tenant assembly on one sqlite file (alkstore
machinery + alkblobs kv + consumer-own tables), where this store's
writers serialize behind every other co-tenant writer — the
durability/contention posture is load-bearing there and should be a
visible contract, not a library-default coincident.
Recommended shape (to be re-derived): an explicit pragma (a pinned constant, or a knob on the engine options alongside the durability posture) + the module doc line. Small; rides the publication round.
Verification status: anchor verified this session (the pragma block; absence of any busy_timeout in the engine). The co-tenancy claims are POC-O's/DQ-7's measured record (no contention observable at POC scale) — independent of this ask's merit.
C-4 [nit, non-blocking] — &Key borrowed-key overloads on serving paths
Finding. Key::as_bytes() allocates (src/keys.rs:55 — returns
Vec<u8>), and hot-path callers reach for it per call
(src/local.rs:899 is a serving-path derive+as_bytes site). The
consumer's serving loops (custom-Find pack generation, ranged
serving) pay it once per object lookup — measured harmless at POC
scale, which is exactly why this is a nit. Borrowed-key overloads
(&self -> &[u8] slices where ownership is not needed) would drop
the per-call allocation on serving paths.
Recommended shape: ride along with the next touching change; not worth its own session.
Verification status: anchor verified this session. The "non-load-bearing" judgment is the substrate-recomposition ledger's, not independently re-measured.
What this review asks for, in one line each
| # | Ask | When | Blocking publish? |
|---|---|---|---|
| C-1 | git-object derivation family (0x03/0x04; loose form as stored preimage) + fleet threading + ops kind decision + ADR-002 annotation | own ADR + implementation session | the held-open reason |
| C-2 | token-registry admission cap (or the non-fleet custody posture on paper with the attack shape recorded) | own session, pre-publish | yes in substance (posture must be documented before publish even if the cap defers) |
| C-3 | explicit sqlite busy_timeout (knob or pinned constant) |
ride-along | no |
| C-4 | &Key borrowed overloads |
ride-along | no |
Disposition space (for the addressing sessions)
Per the operator's framing: this review records findings and recommendations only. Each C-item gets its own addressing session that re-verifies the anchors, re-derives the decision, and either implements or dispositions with a pointer to where the record lives (ADR, module doc, or the upstream-ask ledger in alkgit's substrate-recomposition.md). The publication gate for the crate is C-1's and C-2's outcomes.
Disposition (2026-10-18 — the wave-13 review gate's stamp)
Closed with every ask addressed. The one-line-per-ask table's outcomes:
- C-1 — [ADR-018] accepted (all five decisions owned with rejected
alternatives named; fleet threading fixed as Decision 7, not a
follow-on) and the family landed (c1-git-object-family-impl:
registry/derivation/hasher arms per
src/hashing.rs, key decode/order persrc/keys.rs,put_git_objectvia the derivation-parameterizedput_known_as, fleet threading with exact-count sweep-outcome tests, ops-surface kind delta as docs made honest, interop green against the real git CLI). The one-way door's bytes read against the ADR line by line at this gate: registry bytes0x03/0x04, the preimage-identity derivation, the inertlenparameter, and the blob-family-onlyfrom_git_oid_hexcontract are exactly as ADR-018 pins them. - C-2 — [ADR-019] accepted and landed (c2-token-custody-posture):
the (a)+(c) split — the mint-boundary admission cap over live
entries, refusing-not-evicting, typed as the sixth error-family
member
RegistryFulland answered on the wire as the declaredMINT_REFUSED(committed digest in the details, no token, no custody); the non-fleet custody posture documented embedder-facing with the attack shape; the non-confirming loop reproduced against a mem store at both altitudes; (b) the non-fleet TTL rejected on the record. - C-3 — landed as chartered (c3-sqlite-busy-timeout):
busy_timeout = 5000pinned at open, the contention posture joining the durability posture as stated contract, verified by the pragma test. - C-4 — landed as chartered (c4-borrowed-key-overloads):
Key::key_bytes()+ theKeyBytesCopy wrapper; the serving-path borrow temporaries converted, zero behavior change. Anchor correction recorded: the review'ssrc/local.rs:899is a#[cfg(test)]helper, not a serving path. - alkcall 0.10 — the manifest bump landed (
a575588); the survey's no-impact conclusion held (no code adaptation). - The prototype branch —
poc-o-git-objects(f0b045c) deleted after parity confirmation: a scratch reference re-implemented from, never merged; the reference material now lives in the ADR and the landed, CLI-validated surface.
The publish hold lifts; publication runs manually, coordinator-side.