The ops pin raises from alkcall = "0.10" to alkcall = "0.10.1": the adopt-window ordering fix (alkcall review 013, routed from alkgit's wave-2 gate) closes a race condition, so a graph resolving alkblobs against alkcall 0.10.0 is not acceptable even where the open range would unify fresh — a stale consumer lock could keep 0.10.0 alive. alkgit depends on alkblobs and alkcall together and requires 0.10.1 strictly. Zero source change, zero test change; CHANGELOG gains the [0.2.2] entry. Verified: cargo test (default) green, cargo test --all-features green (server-less), clippy --all-features --all-targets -D warnings clean, fmt --check clean, cargo publish --dry-run --allow-dirty packages 0.2.2.
16 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.2 - 2026-10-22
The requirement raise: Cargo.toml's ops pin moves from
alkcall = { version = "0.10" } to alkcall = { version = "0.10.1" },
forcing every graph that carries alkblobs to resolve alkcall at 0.10.1
or above — the adopt-window ordering fix (alkcall review 013, routed
from alkgit's wave-2 gate, as described in the 0.2.1 entry below).
The raise exists because the downstream consumer (alkgit) depends on
alkblobs and alkcall together, and alkgit requires 0.10.1 strictly: the
fix closes a race condition, so a graph resolving alkblobs against
alkcall 0.10.0 pairs one crate's expectation with a racy channels
layer. The two ranges overlap, so a fresh resolution unifies to 0.10.1
— the residual window is a stale consumer lock that keeps 0.10.0 alive
because it satisfies the open "0.10" range. The raised minimum
closes that window for any graph that ingests this crate.
Zero source change, zero test change; the gate block re-ran green at
the previous 0.2.1 tag hours apart, and cargo publish --dry-run --allow-dirty packages 0.2.2.
0.2.1 - 2026-10-22
The alkcall re-resolution release: Cargo.toml's ops pin
(alkcall = { version = "0.10", optional = true }) now resolves to
alkcall 0.10.1 — the adopt-window ordering fix (alkcall review 013,
routed from alkgit's wave-2 gate). A chunk routed by the demux inside
an adopt_channel install→drain window could be delivered ahead of
earlier-parked early arrivals, inverting the channel's byte-stream
order, and a related loss variant left a post-drain park invisible to
dropped_unknown_chunks forever. alkcall calls it a pure behavior
fix: no API, wire-format, or documented-cap change — ChannelState
and the fix surface are private.
alkblobs rides the native dispatch path with providers wired, not the
gateway door, and no ops-surface code touched the adoption window's
ordering assumptions — so the fix's impact here is nil and no source
change ships. The release exists for the downstream consumer
(alkgit) that ingests both crates and wants one fresh resolution
point riding the fixed alkcall.
Changed
Cargo.lockrelocks alkcall v0.10.0 → v0.10.1. The manifest requirement is unchanged — 0.10.1 is withinversion = "0.10"'s compatibility range, so this is a resolution refresh, not a pin move. Zero source change, zero test change.
Verified
- The full verification block: 219 tests pass on the default feature
set (
mem+sqlite— unit 156, conformance 54, git-interop 7, README examples 2) and 476 pass under--all-features(server-less; the pg columns skip cleanly); both clippy configurations (--all-targets, and--all-features --all-targets,-D warnings) andcargo fmt --checkare clean; the fuzz corpus-replay gate passes (cargo test --manifest-path fuzz/shared/Cargo.toml, 7/7) — campaigns run viafuzz/run-detached.shperfuzz/README.md;cargo publish --dry-run --allow-dirtypackages 0.2.1.
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/putoffers) 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/puttakes{namespace, key, len}with bothkeyandlenrequired; 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, andhavesemantics 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_batchtakes 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
putand the family-namedput_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 isKey::from_bytesover the physical encoding. - The silent canonical constructors
Digest::deriveandKey::derive— derivation is the explicitAlgorithm::derive.
Verified
- The full verification block against the live harness
(
postgres:16-alpineon 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-featuresagainst 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, andcargo doc --no-depsare clean;cargo publish --dry-run --allow-dirtypackages 0.2.0; the fuzz corpus-replay gate passes (cargo test --manifest-path fuzz/shared/Cargo.toml, 7/7) — campaigns run viafuzz/run-detached.shperfuzz/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
Storefacade —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_rangereturns 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). Nonew: the store is built through named constructors declaring one of three ADR-pinned modes —mem_only,kv_only, ordual_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
Pinguard releases on drop;Batchgroups 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), directdelete, 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(featuresqlite, default-on): WAL +synchronous=NORMAL, prepared statements, read paths tolerate the no-tables-yet database; the durable single-host tier. - kv
postgres(featurepostgres): pooled connections, prepared statements,ON CONFLICT DO NOTHINGCAS — the fleet host. - kv
mem(featuremem, 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(featurepg-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 theorphan_reap_permitteddeclaration carries (ADR-016).
- kv
- 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: trueexplicitly (never inferred) against the composite fleet-validity predicate (ADR-012). - The
opsmodule (featureops, default-off) — the network surface riding alkcall (ADR-001):blobs/fetch(the binary data-planeSubop, ranged-fetch addressing over the fanout registry) andblobs/put(the verified offer/stream/commitSinkop), 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'sAccessControlseam — 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_encodingover the key/algorithm parser cluster,ops_ingressover the ranged-fetch bounds ingress and token decode), and a stable-toolchain companion crate (fuzz/shared) replaying the committed corpora as a plaincargo testgate. - The git-object derivation family — the hash registry extends
with
0x03(git-object-sha-1) and0x04(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 publicput_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 realgit 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::MAXthe uncapped form), refusing — never evicting — with the newError::RegistryFullrefusal member and theblobs/putdeclared answerMINT_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-capacityKeyBytes(a stackCopywrapper deref-coercing to&[u8], byte-identical toas_bytes) drops the per-call heap allocation from the serving paths' re-encode sites.
Changed
- sqlite contention posture explicit —
PRAGMA busy_timeout = 5000is 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. alkcallbumped 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-alpineon 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, andcargo doc --no-depsare clean; the fuzz corpus-replay gate passes (cargo test --manifest-path fuzz/shared/Cargo.toml, 7/7) — campaigns run viafuzz/run-detached.shperfuzz/README.md.