Files
alkblobs/CHANGELOG.md
T
glm-5.3-flash 9504893dc8 docs(review): wave 14 gate green — the family-wire surface verified against ADR-020's invariant
- §Decision 1's invariant walked clean per surface (facade write seam /
  key decode / wire ingress); the grep audit found no length-routed,
  default-derivation, or option-typed constructor; §Decisions 2/4/5/6/7
  verified line by line; the §Decision 8 litmus walked with one named,
  dispositioned residue (the schema gate's width enumeration rides a
  novel-width member's admission ADR — the ADR's own literal text, not
  the defect class)
- the Key::derive decomposition-time reading confirmed (the fold into
  the explicit Algorithm::derive; Key::from(Digest) the only
  Key-from-digest route)
- inline fixes: put_known_as's mangled duplicate doc sentences removed;
  the CHANGELOG's [0.2.0] section reordered newest-first (alkcall's
  convention)
- stamps: wave 14 completed in the implementation plan; AGENTS.md's
  status line updated (the superseded-shape warning retired) and its
  fuzzing paragraph's key_encoding target description aligned to
  ADR-020's shrunk surface
- the review task completed with the gate record in its notes

Verification: cargo test default 156/54/7/2; cargo test --all-features
server-less 341/125/7/3 (clean skips) and against the live pglo-poc
harness (the pg columns ran for real); cargo clippy --all-targets --
-D warnings and --all-features --all-targets -- -D warnings; cargo fmt
--check; cargo doc --no-deps; the fuzz corpus-replay gate 7/7; cargo
publish --dry-run --allow-dirty (0.2.0 packages)
2026-10-19 01:29:01 +00:00

13 KiB

Changelog

All notable changes to this crate are documented here. The format is based on Keep a Changelog, and this crate adheres to Semantic Versioning.

0.2.0 - 2026-10-19

The family-wire correction (ADR-020): the wire speaks keys and the facade folds to one derivation-explicit put seam. Two breaks — the wire's and the facade's — shipped together under 0.x's minor-bump semantics, with no shim and no dual-form period. Resolves review-003's coherence finding: the ops surface's addressable classes are now exactly the store's — every registered algorithm, not one family.

Changed

  • The digest wire form is tag-prefixed key hex (ADR-020 §Decision 2) — every digest-bearing field on the ops surface (blobs/fetch, blobs/stat, blobs/have, blobs/delete, blobs/put offers) carries the physical key encoding verbatim: [registry byte] + digest bytes, hex-encoded (42 hex for 21-byte keys, 66 hex for 33-byte keys), decoded by the persistence boundary's rejects-never-guesses parse (the tag validated, the digest length checked per tag, never inferred). Bare digest hex is no longer a wire form — no implicit default-family reading survives; the schema gate is ^[0-9a-f]{42}$|^[0-9a-f]{66}$.
  • The put offer's claim is mandatory (ADR-020 §Decision 3) — blobs/put takes {namespace, key, len} with both key and len required; the wire never discovers addresses. The claim check has one moment — pre-EOF — for every family; the deferred post-check arm is gone.
  • The put response echoes the key (ADR-020 §Decision 4, amending ADR-012 §2) — {token, key} replaces {token, digest}, so the caller addresses what landed in the vocabulary it offered. Renewal, confirm, and have semantics are unchanged.
  • The op set stays closed (ADR-020 §Decision 7) — payload parameterization under unchanged op names; family support is never family-suffixed op variants.

Added

  • put_as(algorithm, len: Option<u64>, stream) — the one derivation-explicit put seam (ADR-020 §Decision 5) and the facade's entire write surface: the caller names the derivation, the same bytes have multiple equally valid addresses, one per registry member, and the crate never picks one for its caller. put_in_batch takes the algorithm parameter. The unknown-length arm (None) is retained on the facade — "stream in, get the key back" stays an in-process posture; only the wire's unknown-length put dies.

Removed

  • The canonical-default put and the family-named put_git_object (ADR-020 §Decision 5) — one seam replaces both; no canonical default is blessed.
  • The length-routed constructor Digest::from_git_oid_hex — family-explicit oid-bytes construction is Key::from_bytes over the physical encoding.
  • The silent canonical constructors Digest::derive and Key::derive — derivation is the explicit Algorithm::derive.

Verified

  • The full verification block against the live harness (postgres:16-alpine on localhost:15432): 219 tests pass on the default feature set (mem + sqlite — unit 156, conformance 54, git-interop 7, README examples 2); 476 pass under --all-features against the live harness (unit 341, conformance 125, git-interop 7, README examples 3 — the pg columns' server-less default is a clean skip); both clippy configurations (--all-targets, and --all-features — -D warnings), cargo fmt --check, and cargo doc --no-deps are clean; cargo publish --dry-run --allow-dirty packages 0.2.0; the fuzz corpus-replay gate passes (cargo test --manifest-path fuzz/shared/Cargo.toml, 7/7) — campaigns run via fuzz/run-detached.sh per fuzz/README.md. The wire's family-explicit payloads are pinned end to end by the wave-14 ops tests (a facade-published family entry fetchable, stat-able, and have-servable over the ops surface; a wire-offered family put landing under its own oid).

0.1.0 - 2026-10-18

Initial release of the alkblobs family: content-addressed blob storage with one canonical content hash, tiered pluggable engines, GC built in, and the network ops surface riding alkcall as a feature.

The crate is the store layer only — no transport, no wire protocol in-core (ADR-001). Store never learns where bytes came from; transport, framing, and serialization live above the facade (in the feature-gated ops module) or nowhere. Keyed under the git-family oid derivation ("blob <len>\0" + content) — SHA-256 canonically, SHA-1 tolerated for existing repos (ADR-002) — with the git-object derivation family for git's non-blob objects (ADR-018) — and addressed through an algorithm-tagged Key encoding that is the crate's one-way door: parsers validate and reject, never guess.

Added

  • The Store facade — put/get/stat/read_range/has/list, with the write verifying the stored artifact as a whole before it publishes and verification-on-get on the kv tier's materialized reads (the windows themselves carry no check; read_range returns the ADR-006 slice digest for the caller to carry out-of-band) (ADR-006). Known-length and unknown-length puts share one signature (the unknown-length path buffers past the threshold in memory, then continues into the large tier's stage — a non-restartable source is a normal case, never an error). No new: the store is built through named constructors declaring one of three ADR-pinned modes — mem_only, kv_only, or dual_tier — with size-threshold dispatch the pure function routing between a store's kv tier and its large tier (default threshold 128 KiB, constructor-tunable; ADR-003).
  • Pins and batches — pin-before-publish: an entry cannot appear in a sweep's view without already carrying its pin. The put's RAII Pin guard releases on drop; Batch groups a multi-put write (manifest writes are the shape) under one batch-scoped pin group, committed or dropped together (ADR-005).
  • The GC protocol — sweep (mark phase / delete window / commit phase, aborts report as data with nothing deleted), direct delete, and liveness registration in two forms (ADR-005, ADR-008): the in-process callback and, for fleets, the named liveness table the constructor declares.
  • Five engines over two tiers (ADR-003, ADR-007, ADR-009) — features gate engines, each pulling only its own driver cluster (ADR-013); tiers are constructor composition, not features:
    • kv sqlite (feature sqlite, default-on): WAL + synchronous=NORMAL, prepared statements, read paths tolerate the no-tables-yet database; the durable single-host tier.
    • kv postgres (feature postgres): pooled connections, prepared statements, ON CONFLICT DO NOTHING CAS — the fleet host.
    • kv mem (feature mem, default-on): dependency-free ephemeral implementation; never a production story, never fleet-valid — the contract-reference engine every other engine's conformance tests mirror.
    • large local (ungated, dependency-free): flat sharded files, stage-then-commit-rename, pread reads; the publish is fsync- durable and no-replace (ADR-015).
    • large pg-lo (feature pg-lo): postgres Large Objects — descriptorless windowed reads, LO creation transactional (zero crash orphans), staged into a held transaction; one large-object consumer per database is the co-tenancy constraint the orphan_reap_permitted declaration carries (ADR-016).
  • Fleet GC — DB-backed pins, sweep-window rows, and the sweep lease host on the fleet's kv-tier engine (postgres): the lease is an atomic test-and-set advisory lock (at most one sweep in flight per pool; ADR-008, ADR-013); deletes are windowed and re-arbitrated at execution so the sweep-vs-put race resolves without data loss; constructors declare fleet: true explicitly (never inferred) against the composite fleet-validity predicate (ADR-012).
  • The ops module (feature ops, default-off) — the network surface riding alkcall (ADR-001): blobs/fetch (the binary data-plane Sub op, ranged-fetch addressing over the fanout registry) and blobs/put (the verified offer/stream/commit Sink op), plus the JSON control-op family (stat, have, delete) and the pin-token layer: the token is a CSPRNG capability over the pin, minted custodial (non-fleet) or renewable (fleet), renewed and confirmed through the store core's renewal access — the one crate-internal seam the module consumes. Authorization rides alkcall's AccessControl seam — no in-band invented scheme (ADR-014, ADR-017).
  • Fuzzing — fuzz/, a sidecar workspace excluded from the root workspace, pinning nightly inside itself: two libFuzzer targets over the parse surfaces (key_encoding over the key/algorithm parser cluster, ops_ingress over the ranged-fetch bounds ingress and token decode), and a stable-toolchain companion crate (fuzz/shared) replaying the committed corpora as a plain cargo test gate.
  • The git-object derivation family — the hash registry extends with 0x03 (git-object-sha-1) and 0x04 (git-object-sha-256) (ADR-018): the preimage-identity derivation over git's own loose-object form (<type> <len>\0 + content stored as the value), so the digest is byte-identical to git's oid of the object — trees, commits, and tags included — with verification-on-get, dedup, and external-oid addressing intact. Lands as the public put_git_object(algorithm, len, stream) put seam (known-length only, family members only), threaded through the fleet put paths uniformly, and CLI-validated byte-exact against real git hash-object.
  • The token registry's admission posture (ADR-019) — an admission cap at the mint boundary (OpsConfig::token_registry_max_entries, default 65,536 entries, usize::MAX the uncapped form), refusing — never evicting — with the new Error::RegistryFull refusal member and the blobs/put declared answer MINT_REFUSED (the committed digest in the details; no token, no custody taken). Bounds the non-confirming upload loop (unbounded registry growth plus never-expiring custody on a non-fleet store) at the store; the non-fleet custody posture is documented embedder-facing, the fleet TTL named as the safe posture for doors that cannot prove confirm.
  • Borrowed-key overloads — Key::key_bytes() returning the fixed-capacity KeyBytes (a stack Copy wrapper deref-coercing to &[u8], byte-identical to as_bytes) drops the per-call heap allocation from the serving paths' re-encode sites.

Changed

  • sqlite contention posture explicit — PRAGMA busy_timeout = 5000 is set at open as a pinned constant (matching the family's explicit alkstore-sqlite value), joining the durability posture as stated contract: co-tenant assemblies on one sqlite file no longer ride rusqlite's implicit default by coincidence.
  • alkcall bumped to 0.10 (the gateway ownership-seam release; additive for this crate's native-wire use — no code changes).

Verified

  • The full verification block against the live harness (postgres:16-alpine on localhost:15432): 218 tests pass on the default feature set (mem + sqlite — unit 155, conformance 54, git-interop 7, README examples 2); 473 pass under --all-features — the pg columns run against the live harness (their server-less default is a clean skip); both clippy configurations (--all-targets --all-features, -D warnings), cargo fmt --check, and cargo doc --no-deps are clean; the fuzz corpus-replay gate passes (cargo test --manifest-path fuzz/shared/Cargo.toml, 7/7) — campaigns run via fuzz/run-detached.sh per fuzz/README.md.