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).
10 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-10-03 (ADR-010/011 — I/O seams + GC-state home; vocabulary pinned, fs → large, mem joins kv) |
alkblobs — Architecture
Current State
Phase 1 (Architecture). Phase 0 (Exploration) converged — see
docs/research/phase-0.md for the vision, the evidence trail, and the
POC register (all POCs passed or were absorbed; POC code lives in
standalone crates under /workspace/ — alkblobs-trait-poc,
alkblobs-largeblob-poc, alkblobs-postgres-poc, alkblobs-redb-poc,
alkblobs-pglo-poc; findings live in
docs/research/). No implementation exists yet (src/lib.rs is a
placeholder).
The converged Phase 0 posture has been promoted into the specs and ADRs below. Two framing corrections from the convergence review are captured there — not inherited as hedges:
- ADR-001 makes the substrate posture explicit: the store layer carries no alkcall dependency by design — which is conformance to the family pattern, not deviation from it — and the ops surface rides alkcall, feature-gated.
- ADR-004 fixes the "two backends" framing: the crate ships exactly two tiers (kv small / large — both required by measured scale economics); the path→hash mapping layer (the alkfs/alknet "vfs" shape) is a consumer-layer concern and must never become a third backend.
- ADR-007 widens the kv tier's engine set rather than hedging it: sqlite (default) + postgres (feature-gated) behind one trait. The deployment economics this records: a downstream node (git server, vfs) classically needed three storage systems — kv + large content + relational — and this design collapses the three to two: one relational engine + large-tier storage, with the engine a per-node constructor choice. Resolved from evidence in hand (POCs #3/#5/#6); the would-be "deployment trigger" framing was caught at review as circular hedging (a deployment running alkblobs cannot pre-exist the engine) and closed on the OQ-09 precedent.
- ADR-008 (2026-10-03) repairs the seams ADR-007 stressed, against
the pinned consumer requirement REQ-2 (requirements.md — a replicator
node may run a fleet of store instances sharing one pg pool, a
planning fact predating all POCs): the
Backendtrait gains asizeprobe (before any backend ships data); fleet GC gets DB-backed pins, an advisory-locked single sweeper, and SQL-level delete arbitration (single-node postures unchanged); and the large tier becomes engine-selectable (localdefault;pg-lonamed candidate, admission-gated on POC #7). It also adds requirements.md — the REQ anchor that ends per-session re-derivation of consumer facts. - ADR-009 (2026-10-03) admits
pg-lo— postgres Large Objects as the large tier's second engine (featurepg-lo, default-off) — on POC #7's passed gate (docs/research/poc-pglo-findings.md): contract 10/10 exact-count tests, durable put ≈ fs durable put at ≥1 MiB, descriptorlesslo_getwindow gets, transactional LO lifecycle (zero crash orphans), companion-table contract authority. REQ-2's fleet consolidation option is now shipped: a fleet node can run postgres-only. Named deltas: cached-get 20–50× behind page-cache fs single-stream (aggregate serving ~700 MB/s at 16 readers), catalog space reused-but-never-returned, autovacuum inherited. The admission door (ADR-003, exercised by ADR-007 and ADR-009) has now shipped both its tier-extending engines. - ADR-010 (2026-10-03) pins the
Backendtrait's I/O seams — the last unpinned signatures before implementation:getreturns a crate-internal read cursor (kv engines materialize; large-tier engines stream —local's pread loop, pg-lo'slo_getwindows),puthas two named forms (whole-value for kv, staged put for large — stage-then-commit-rename promoted from behavior to contract); rules that GC state is store-core-owned (backends never learn liveness — ADR-005 verbatim) hosted by SQL-backed engines via a crate-internal contract-tested engine-state companion seam (schema/pin/window/lock clauses), rules the store core holds the joint entry+pin transaction (correcting ADR-009 §2's ownership statement), adds the kv tier's fleet-validity rule (mirror of ADR-008 §3 — postgres is the fleet engine; a sqlite-fleet posture is not on offer), resolves the ops pin-token TTL race (the token is the pin; renewal rides have/re-put; pause-past-TTL falls to delete-then-recover), and pins the feature graph (pg-lodoes not implypostgres). - ADR-011 (2026-10-03) pins the vocabulary: tier (contract +
routing class — exactly two) vs engine (concrete impl — exactly
five: kv's sqlite/postgres/mem, large's
local/pg-lo); mem demoted from "backend"/"testing tier" to the kv tier's third engine — the contract-reference engine; store instance / node / fleet split so REQ-2's "one node may run multiple instances" phrasing is no longer self-contradictory (fleet = pool-sharing, not node count); and the large tier is renamedlarge(featurefs→large) — its routing criterion is length, not medium — with constructor modes (dual-tier default, kv-only, mem-only) pinned in the same ADR's table.
Architecture Documents
| Document | Scope | Status |
|---|---|---|
| overview.md | Purpose, consumers, layer map, dependency posture | draft |
| requirements.md | Consumer requirements REQ-1..4, pinned vocabulary (tier/engine/instance/node/fleet) | draft |
| hashing-and-keys.md | Canonical hash, key encoding (the one-way door) | draft |
| store-api.md | Store facade: put/get/stat/range/pin/batch/sweep surface, errors, invariants | draft |
| backends-and-dispatch.md | Backend trait contract, tiers + engines table, size-threshold dispatch, constructor modes | draft |
| gc-and-namespaces.md | Pooled CAS, liveness seams, mark-and-sweep, delete windows | draft |
| ops-surface.md | alkcall-backed have/need + fetch/put ops, ACL mapping | draft |
| open-questions.md | Centralized OQ tracker (incl. promoted Phase 0 register) | draft |
Decision Records
| ADR | Decision | Status |
|---|---|---|
| 001 | Substrate posture & ops-surface placement (conformance, not deviation) | Accepted |
| 002 | Canonical hash + key encoding (the wire-format ADR; precedes any consumer) | Accepted |
| 003 | Backend contract, shipped backends, dispatch policy | Accepted |
| 004 | Exactly two backends; manifest layers stay above | Accepted |
| 005 | Pooled CAS, liveness seams, mark-and-sweep, delete windows | Accepted |
| 006 | Whole-blob verification; transfer encoding excluded by scoping | Accepted |
| 007 | Two kv engines (sqlite + postgres) behind one trait; constructor-selected | Accepted |
| 008 | Trait size probe; fleet GC (DB-backed pins, advisory-locked sweeper); large-tier engines |
Accepted |
| 009 | pg-lo — postgres Large Objects as the large tier's second engine (admitted on POC #7) | Accepted |
| 010 | Backend I/O seams (read cursor, staged put); GC state store-core-owned via the engine-state seam; kv fleet-validity; pin-token renewal | Accepted |
| 011 | Vocabulary: tier/engine/instance/node/fleet; mem joins kv; large tier renamed large; constructor modes |
Accepted |
Open Questions
Tracked in open-questions.md. The Phase 0 register
(OQ-BL-01..06) is promoted there with its resolutions; two questions
remain parked — OQ-07/OQ-08 externally-owned (alkgit seam mapping,
alkfs intake; carried for visibility, gating nothing here). OQ-09
(namespace-visibility default) resolved closed-by-default; OQ-10
(second kv engine) resolved by ADR-007. The pg-lo large-engine question
resolved to sequenced work, not a parked question: REQ-2/ADR-008
named it the candidate large-tier engine and POC #7
(docs/research/poc-pglo-spec.md) was its admission evidence — a
task, not a wait; that task completed 2026-10-03
(docs/research/poc-pglo-findings.md, passed) and ADR-009 shipped
the engine. Deferral
policy (the "Schrödinger's code" rule): a decision this crate needs
before shipping may not be deferred on a dependency that is itself
waiting for this crate to exist. Legitimate parked kinds:
deferred(scope) on a deciding fact that exists independently of this
crate, and externally-owned questions (how a consumer maps onto this
crate) that gate no decision here — full definitions in the header of
open-questions.md. A third category is not a
parked kind: decided-but-sequenced work (a resolved decision whose
admission gate requires evidence — the pg-lo round's POC #7 is the
worked example; a task with an owner, not a parked question). A
corollary worth restating, because it recurs: a fact this crate must
create (its own first deployment, its own first consumer) is never a
deciding input — decisions stand on evidence in hand, and future needs
reopen via named requirements, not via waiting. (Corollary
applications so far: OQ-09 and OQ-10 both resolved on it.)
Lifecycle
Spec docs: draft → reviewed → stable → deprecated. A doc moves
reviewed when every crate-owned open question it references
resolves and an architecture review pass clears it — externally-owned
OQs (OQ-07/OQ-08) gate nothing here by definition and do not block the
lifecycle; their outcomes arrive through their owners' processes.
stable when implementation verifies against it; deprecated when
superseded (kept for reference). ADRs use a separate status set
(Accepted | Proposed | Deprecated | Superseded), defined per ADR file.
This tree is in draft pending the first architecture review cycle.