Files
alkblobs/CHANGELOG.md
T
glm-5.3-flash f90dda7b03 chore(release): 0.2.1 — the alkcall 0.10.1 re-resolution release
The manifest's alkcall = "0.10" pin now resolves to 0.10.1 (the
adopt-window ordering fix, alkcall review 013, routed from alkgit's
wave-2 gate). No source change in this crate — the bump is the fresh
resolution point for the downstream consumer that ingests both crates.
CHANGELOG gains the [0.2.1] entry.

Verified: cargo test (default, 219), cargo test --all-features
(476, server-less), the fuzz corpus-replay gate (7/7), clippy across
both feature sets (-D warnings), fmt --check, and cargo publish
--dry-run --allow-dirty.
2026-10-22 05:42:55 +00:00

15 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.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.lock relocks alkcall v0.10.0 → v0.10.1. The manifest requirement is unchanged — 0.10.1 is within version = "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) and cargo fmt --check 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; cargo publish --dry-run --allow-dirty packages 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/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.