The ADR-008 pg-lo admission POC ran in a standalone crate (/workspace/alkblobs-pglo-poc): PgLoBackend over the ADR-003/008 trait contract (including size), 10/10 exact-count sweep-outcome contract tests, clippy/fmt clean; dockerized postgres:16-alpine on :15432, POC #5 driver stack (tokio-postgres + deadpool) via SQL lo_* functions, no new dependency. Gate verdict: passed, with named deltas. - Performance: durable put 60-65 MB/s at >=1 MiB, within 1.5x of — and below 1 MiB beating — durable local fs on this fsync-slow disk; cached gets 70-180 MB/s single-stream, ~0.7 GB/s aggregate over 16 readers (20-50x behind page-cache fs — the honest named delta) - Contract: companion table is the list()/size()/CAS authority (never the catalogs); stage-then-commit; GC-participating lo_unlink delete - Handles: the tx-scoped descriptor is real but pool-compatible via descriptorless lo_get(oid, off, len) windows — window gets keep handle-acquire p99 at 1-6 ms under readers <= pool; held descriptor is the fallback posture - Vacuum: pg_largeobject pages churn-reused, never returned; tracked by autovacuum; rel-size monitoring named as an ops requirement - Crash/orphan: LO creation is transactional — kill/terminate mid-write-tx leaves zero orphan pages; the only orphan class is a committed LO bypassing the companion table (planted, reaped by the ~7 ms/oid sweep; committed content survives byte-exact) - Harness lessons: lo_lseek is int4 — the 64 variants are the >2 GiB discipline; shared-table parallel tests are unsound (per-test CREATE DATABASE isolation) Docs: new poc-pglo-findings.md; poc-pglo-spec.md status passed; register OQ-BL-06 #7 marked passed; ADR-008 pg-lo bullet updated (duplicate bullet removed) + backends-and-dispatch/open-questions cross-references. Verification: cargo test --release (10 passed), clippy -D warnings, fmt --check in /workspace/alkblobs-pglo-poc.
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-10-03 |
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 (/workspace/alkblobs-trait-poc,
/workspace/alkblobs-largeblob-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 backends (kv small / fs 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 + fs + relational — and this design collapses them to one relational engine + fs, 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 fs 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.
Architecture Documents
| Document | Scope | Status |
|---|---|---|
| overview.md | Purpose, consumers, layer map, dependency posture | draft |
| requirements.md | Consumer requirements REQ-1..4, node/pool/fleet vocabulary | 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, shipped backends, size-threshold dispatch | 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); fs-tier engines |
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 fs-engine question
resolved to sequenced work, not a parked question: REQ-2/ADR-008
named it the candidate fs-tier engine and POC #7
(docs/research/poc-pglo-spec.md) is its admission evidence — a task,
not a wait. 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. Two parking kinds remain legitimate:
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 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.