Files
alkblobs/docs/architecture/overview.md
T
glm-5.3-flash bee86b0dbd docs(architecture): ADR-013 — family composition, reactive boundary, sweep lease; OQ-11 resolved
- No sibling (alkstore) dependency in-core; reactivity is
  deployment-layer composition above the facade — cadence stays
  embedder-owned (ADR-005), with alkstore the named substrate for it;
  in-crate notify/scheduler/queues/invalidation hooks are non-goals.
- Fleet sweeper lock pinned as 'named exclusive sweep lease'
  semantics; shipped provider unchanged (pg advisory clause);
  provider swaps are ADR-007-shaped substitution doors.
- Composition contract for downstream ADRs: co-tenancy of CAS/GC
  state + reactive machinery + consumer/liveness tables on one fleet
  host; no-cross-store-atomicity ruled; compensation rests on
  idempotent re-puts, windowed deletes, sweeps-as-report-data.
- OQ-11 resolved (the register's one live circular hedge): engine
  features pull their own driver clusters — sqlite becomes feature
  'sqlite' (default-on, carries rusqlite); 'kv'/'large' tier features
  retired; 'local' ungated; pg-pure builds are supported,
  non-default. Deciding fact already in-tree (REQ-2/ADR-009).
- requirements.md: UC-4/REQ-5 records the composition facts
  (operator record 2026-10-11, corroborated by alkstore's
  consumer-inventory).
- Publish guard stated pre-code: facade ships before publish;
  semver-is-contract from first publish (alkstore ADR-017 discipline).
- Amended accepted ADRs' feature wording (ADR-010 §1, ADR-011 §3/§4,
  ADR-003) with supersession notes; README/overview/open-questions/
  backends/store-api/gc aligned; parked index now empty.

Verification: docs-only round (no Rust surface touched); full-tree
single-value check on the feature graph and sweep-lease wording.
2026-10-11 16:47:22 +00:00

12 KiB
Raw Blame History

status, last_updated
status last_updated
draft 2026-10-11 (ADR-013 — family composition & reactive boundary; OQ-11 resolved, feature graph re-pinned to engine features)

Overview

What this crate is

alkblobs is content-addressed blob storage in the alk* family: a put/get/verify store keyed under one canonical content hash (the git-family derivation; legacy SHA-1-tolerant), with pluggable tier engines (small blobs in a kv store per ADR-007/011, over-threshold blobs on the large tier per ADR-008/009 — the tier formerly named fs), split from any wire/ops-protocol layer. Network ops ride the alkcall substrate behind a feature-gated ops module (ADR-001).

Deployment-relevant consumer facts — what each use case forces on this crate — are pinned in requirements.md (REQ-1..4, with the node/pool/fleet/engine vocabulary defined there). This tree references REQs by ID; per-session re-derivation from vision prose is exactly what that document exists to prevent.

Why it exists — the extracted shared core

Two downstream projects were converging on the same storage stack, so it is being built once, here:

  • alkgit was paused mid-Phase-1 (its architecture is reviewed, docs/architecture/ in the alkgit repo) to stop duplicated storage-layer work. Its object backend needs git's own oid derivation, a better small-object backend than its proposed default file layout, and a git-lfs-shaped large-blob story. Under the canonical-hash resolution (ADR-002) git objects are first-class pool entries with zero indirection.
  • alkfs (Phase 0 pending) needs the appfile/workspace shape: small content in a fast kv store, large content on the filesystem, path→hash mapping above. The path-tree/manifest layer is alkgit's and alkfs's, not this crate's (ADR-004).

Both demanding consumers (p2p git replicators, agent workspaces) reduce to the same shape: a pooled CAS per node, where repos and workspaces are sets of hash references, not walled-off object stores — cross-repo dedup by construction (ADR-005). That pooling property is why the storage layer was pulled out of both consumers rather than built twice or three times.

Layer map

┌────────────────────────────────────────────────────────────┐
│ consumers (above this crate)                               │
│   alkgit object storage · alkfs path trees/workspaces      │
│   refs, manifests, path→hash maps, git semantics           │
└────────────┬───────────────────────────────────────────────┘
             ▼
┌────────────────────────────────────────────────────────────┐
│ ops surface   (feature "ops"; ADR-001)                     │
│   have/need + verified fetch + put; JSON control ops,      │
│   binary data channels; ACL via alkcall AccessControl      │
└────────────┬───────────────────────────────────────────────┘
             ▼
┌────────────────────────────────────────────────────────────┐
│ store core    (always on)                                  │
│   hashing & keys (ADR-002) · put/get/stat/range/pin seams  │
│   mark-and-sweep GC + liveness registration (ADR-005)      │
│   size-threshold dispatch (ADR-003); GC state owned        │
│   here; fleet state hosts on the fleet's kv engine         │
│   via the seam (ADR-010/012)                               │
└───────┬───────────────────┬───────────────────────────────┘
        ▼                   ▼
┌──────────────────────┐  ┌─────────────────────────────────┐
│ kv tier (ADR-003/    │  │ large tier (ADR-003/008/011;    │
│ 007/011)             │  │  formerly "fs")                 │
│   sqlite ("sqlite";  │  │   local (ungated; ADR-013 §4)   │
│    default engine)   │  │   pg-lo ("pg-lo"; ADR-009)      │
│   postgres ("postgres")  │                              │
│   mem ("mem"; tests +     │                             │
│    contract reference)    │                             │
└──────────────────────┘  └─────────────────────────────────┘
        ▲                   ▲
        └── one Backend trait (ADR-003/010: read cursor +
            staged put I/O seams); engine = per-node
            constructor choice (ADR-011's table)

   transport: none, anywhere. alkgit/alkfs or the embedder dials;
   an alkcall Connection is handed in at the ops boundary.

Dependency posture

Layer Dependencies Notes
base crate (store core + tier dispatch + GC) tokio, thiserror, parking_lot no alkcall, no reactive substrate, no serialization frameworks
engine features (ADR-010 §1, extended to every engine by ADR-013 §4) each engine pulls only its own driver cluster sqlite (default-on, rusqlite), postgres (default-off, tokio-postgres+deadpool-postgres), mem (default-off, nothing), pg-lo (default-off, the shared pg cluster); local is ungated (dependency-free). Every engine-feature combination is a coherent node shape (e.g. large=pg-lo + kv=sqlite, ADR-010 §1); the feature-gated engines' conformance/sweep-safety gates run under --all-features (ADR-007/009)
ops feature adds alkcall (feature-gated, default-off) JSON op specs + binary channels + AccessControl; ADR-001
consumer crates depend on alkblobs (and on alkcall directly when they speak ops) alkgit, alkfs — which also deploy the sibling reactive substrate beside it (below)

The sibling reactive substrate (ADR-013 §1/§3): alkstore (v0.1.0 — notify/locks/queues/streams/outbox/scheduler behind its own engine crates) ships beside this crate, not under it. The core keeps no alkstore dependency and no reactive machinery: cadence and invalidation wiring are deployment-layer composition above the facade (ADR-005's "embedder owns cadence" stands, with the sibling the named substrate; sweep outcomes are report data — ADR-012 §1 — whose pinned consumer is that wiring). Cross-store atomicity is unachievable by construction; the composition properties are idempotent re-puts, windowed deletes, and reports-as-data (ADR-013 §3, the citable downstream contract).

Tiers are constructor composition, not features (ADR-013 §4); engine features as in the table above. The default build is batteries-included (sqlite default-on — opt-out rather than opt-in, the alkgit feature-model pattern), and a pg-pure build (sqlite off; kv=postgres, large=pg-lo if wanted) is a supported configuration this crate does not default to — REQ-2's consolidation posture (ADR-009), now carried by the feature graph instead of parked on it. Wasm: the tier engines are not a wasm story; a wasm client is a consumer of the ops surface (which rides alkcall, itself wasm-clean), never an in-process embedder (ADR-001 §Consequences).

Non-goals (each enforced by an ADR)

  • No wire framing, op dispatch, ACL, or transport inside the store layer (ADR-001).
  • No adopted iroh-blobs wire surface — no tickets, no postcard, no provider protocol (AGENTS convention 7; ADR-001 §Context).
  • No namespaces-as-physical-storage; the pool is flat, namespaces are consumer-level reference sets (ADR-005).
  • No manifest/path-tree/vfs tables; consumers never implement a Backend to model paths (ADR-004).
  • No chunk-tree/CDC storage encoding; whole-file CAS is the store's only granularity (ADR-006).
  • No reactive coordination machinery in-core — no notify, no scheduler, no queues, no invalidation hooks: reactivity is the sibling substrate's (alkstore), composed at the deployment layer above the facade (ADR-013 §1).
  • No per-repo or per-namespace walled stores — one pooled CAS (ADR-005).

Design Decisions

ADR Decision Summary
001 Substrate posture & ops placement store stays substrate-free; ops ride alkcall feature-gated
002 Canonical hash + key encoding git-blob-sha-256 canonical; algorithm in-key; one-way door
003 Backend contract & dispatch two backends, size-threshold routing, no migration
004 Two backends, no third manifest layers are consumer-side
005 Pooled CAS & GC flat pool, registered liveness, delete windows
006 Verification posture whole-blob verification; chunk trees excluded by scoping
007 Two kv engines sqlite default + postgres behind one trait; one relational engine + large tier per node
008 size probe + fleet GC + large-tier engines trait length probe; fleet pins/sweeps engine-backed; the large tier (now large) engine-selectable
009 pg-lo large engine postgres Large Objects as the large tier's second engine, admitted on POC #7; a fleet node can run postgres-only
010 I/O seams + GC-state home read cursor / staged put; store-core-owned GC state via the engine-state seam; kv fleet-validity rule; pin-token renewal
012 Sweep-abort shape + composition rulings sweeps never error (GcAborted retired, GcRefuse for direct-delete); token wire shape; fleet mode = constructor declaration; GC state hosts on the kv engine; sqlite seam reduction
011 Vocabulary + rename tier/engine/instance/node/fleet pinned; mem joins the kv tier; large tier renamed large; constructor modes
013 Family composition & reactive boundary no sibling dependency in-core; sweep lease semantics (provider: raw advisory clause); co-tenancy + no-cross-store-atomicity; OQ-11 resolved (engine features pull own drivers)

Open Questions

Tracked in open-questions.md. Ones affecting the crate level:

  • OQ-07: how alkgit's object-storage seam consumes alkblobs (externally-owned: alkgit's architecture process)
  • OQ-08: alkfs requirement intake (externally-owned: alkfs Phase 0)
  • OQ-09: default namespace visibility in network ops — resolved closed-by-default (deny-unlisted; reopen only on a named open-read requirement)
  • OQ-10: second kv engine — resolved by ADR-007 (sqlite + postgres shipped behind one trait; per-node constructor choice)
  • OQ-11: pg-only kv node feature graph — resolved by ADR-013 §4 (engine features pull their own driver clusters; sqlite default-on; pg-pure builds supported, non-default)
  • POC #7 (docs/research/poc-pglo-findings.md): pg Large Objects as the large tier's pg-lo engine — passed 2026-10-03; admitted as a shipped engine by ADR-009 (REQ-2's consolidation option)

References

  • docs/research/phase-0.md — the converged Phase 0 record; inputs to every ADR above
  • docs/research/poc-trait-dispatch-findings.md, docs/research/poc-largeblob-findings.md, docs/research/iroh-blobs-eval.md — the evidence trail
  • docs/sdd_process.md — the process governing this tree
  • /workspace/@alkdev/alkgit/docs/architecture/backend.md — the reviewed consumer-side seam that OQ-07 maps onto alkblobs
  • AGENTS.md — family conventions (conventions 4–8 gate the decisions)