Row-first gate: consumer-inventory.md gains an engine-tier section — the mem-engine need recorded operator-authority, dated 2026-10-10 (wasm32 sandboxing across the alk protocol family + downstream ffi/napi/python-adapter economics; rusqlite/tokio-postgres structurally out on wasm, so mem is the only triad member the sandbox can carry). ADR-024 (decisions/024-mem-engine.md) pins the decision set: (1) separate `alkstore-mem` crate — discharges and amends ADR-001 §4 (superseded in part: full contract-v1 engine, not an "implementation convenience"; annotation on ADR-001 §4 + Status, class-1 window still open); (2) honest-ephemeral posture — no durability, single-process, ephemeral by design, never fleet-valid (ADR-010 §3's shared-pool predicate fails structurally), riding ADR-016's carriers unchanged (identity + docs + matrix); (3) full contract v1 tier with the asymmetry classification — `PayloadTooLarge` never produced (SQLite arm), no reconnection concept (reconnect-wake/watcher rows absent from mem's per-engine column), receiver close at engine drop, wake coalescing N/A; (4) `MemStore::new()` engine-native constructor — the one documented exception to the open-prose, core-contract.md Store concept amended; shared-instance (two opens → one engine) rejected with the ADR-016 §4-style re-entry gate (consumer-inventory row), register stays closed; (5) wasm32 — compile-clean for wasm32-unknown-unknown plus the suite's mem column green on host under a current-thread runtime flavor are wave-6 acceptance items; on-target suite execution scoped out with a named collapse condition (wasm-bindgen-test adapter crate) — a scope decision, not a pending-client hedge; (6) mem is not a core dev-dependency — core's doc examples stay mock-based, ADR-001 §4's doctest posture declines. engine-mem.md (draft): the in-process mapping — guarded per-mechanism state (no pool, no schema bootstrap, no forwarder, no spawn_blocking seam), the tx overlay (staged *_tx effects merged atomically at commit; read-your-own-writes easy, drop-=-rollback trivial; wake-at-commit structural — the seam pg needed pg-fix-tx-wake for, mem pins from birth via the existing tx-commit-atomicity rows), ADR-012 §2 third owner of the curve/opts/@every arithmetic (three-way equivalence, ADR-022's StoreFactory reused verbatim — born pinned). deployment.md: host-semantics row (single-process, ephemeral, never fleet-valid, wasm32-clean), mem connection-budget subsection (none — nothing to budget), durability-knobs row (the honest row is the absence), toolchain row (wasm32 subset + current-thread-clean, acceptance-cited). README/overview: engine-mem.md row, ADR-024 row, family table entry (alkstore-mem, no driver deps), current-state updated to the implementation phase with engine specs' stable statuses reflected. implementation.md: wave table gains wave 6 (mem engine, depends waves 1 + 5) and renumbers release readiness to wave 7 (fuzzing decision — the one remaining decided-at-implementation deferral — rides wave 7); wave-6 section records the born-pinned posture, acceptance items, the one suite-harness pre-task (past_stamp_sleep blocking sleep → async sleep, review 003's properties.rs:1683 note), and the window riders (review 003 Finding 1: the pg transient-fault seam, MEDIUM, rides wave 6; Findings 2–3 follow their park into wave 7's pre-release); wave-6 review gate defined; Decided points updated (mem discharged, fuzzing renumbered); stale wave-6 references swept across review docs and the landed tasks' notes (release-readiness items renumbered or annotated). Docs-only change; verified by cross-reference audit (all ADR/OQ/anchor references resolve, residual wave-6 mentions are correct under the new numbering or carry dated annotations).
7.7 KiB
status: draft
last_updated: 2026-10-10 (ADR-024 — the mem engine joins the crate
family as alkstore-mem; family/ADR tables updated)
alkstore — Overview
One reactive store interface over SQLite, Postgres, and the
in-process mem engine: durable
notify/subscribe signals, streams with per-consumer offsets, durable
queues with the transactional enqueue property, named locks, a
scheduler, and an outbox helper — with each engine using its own
native machinery underneath (SQLite: honker's watcher design;
Postgres: pg_notify/LISTEN and the pg-boss schema family; mem:
guarded in-process state, ephemeral by design —
ADR-024).
Why this crate exists
The alk* ecosystem's current shape is a repository pattern with a default in-memory adapter, cache-invalidation wiring in hot paths, and per-project approximations where a durable reactive substrate was needed. alkblobs paused partly on that fuzziness. alkstore is the substrate, made once: the answer to "how does a change in the database become visible to other processes/connections?" that downstream stores don't re-derive.
Crate family
Per ADR-001:
| Crate | Contents | Driver dependencies |
|---|---|---|
alkstore (core) |
trait surface, types, error model | none (no capability surface — ADR-016: engine differences are compile-time identity + the deployment matrix, never a runtime descriptor) |
alkstore-sqlite |
SQLite engine (ADR-003) | rusqlite; the forked honker-core lineage rides in-tree as the engine crate's substrate module subtree (ADR-011, designed in ADR-012, folded per ADR-013) |
alkstore-postgres |
Postgres engine (ADR-004) | tokio-postgres, deadpool-postgres |
alkstore-mem |
mem engine — full contract v1, honest-ephemeral (no durability, single-process, never fleet-valid; ADR-024) | none (tokio sync + time only; the wasm32-clean member of the family) |
Downstream consumers depend on core + exactly one engine. Family
standards apply throughout: tokio async runtime, thiserror errors, no
panics in library code, no unwrap()/expect() outside tests,
lean base crate.
Feature surface
Per ADR-002:
- First-class: notify/listen; streams.
- In scope: named locks; queues + outbox helper; scheduler.
- Cut-flag: rate limits; result storage.
- Out: loadable-extension surface; DAGs/task chains/chords/multi-writer replication/cross-machine locking.
Document map
| Doc | Purpose |
|---|---|
| core-contract.md | The unified trait surface: mechanisms, delivery guarantees, tx seam, naming |
| engine-sqlite.md | SQLite engine: mapping the contract onto the forked substrate module/rusqlite |
| engine-postgres.md | Postgres engine: mapping the contract onto tokio-postgres/LISTEN |
| engine-mem.md | Mem engine: mapping the contract onto guarded in-process state (ADR-024) |
| queues.md | Queue/scheduler/outbox semantics depth (ADR-009/ADR-010 resolved) |
| deployment.md | Host capabilities, connection budgets, deployment matrix (OQ-08 resolved) |
| open-questions.md | OQ-01..NN tracker |
| decisions/ | ADRs |
What is decided (ADR index)
| ADR | Decision | Status |
|---|---|---|
| 001 | Reactive-core + per-engine crates | Accepted |
| 002 | Feature scope (inventory-confirmed) | Accepted |
| 003 | SQLite: rusqlite + honker-core lineage, bridged seam (ownership: ADR-011) | Accepted |
| 004 | Postgres: tokio-postgres + deadpool, hand-rolled LISTEN | Accepted |
| 005 | Published libraries by default, named fork triggers | Accepted |
| 006 | Opaque wake + re-read; notify-vs-streams guarantee split | Accepted |
| 007 | Caller-held TxHandle with *_tx methods |
Accepted |
| 008 | Contract v1 surface pinning (partition, TxHandle shape, wake type, reserved strings, error taxonomy) | Accepted |
| 009 | Scheduler collapse (queues + schedule(), @every-only) |
Accepted |
| 010 | Queue semantics depth (visibility, backoff, dead-letter, sweep, layout) | Accepted |
| 011 | SQLite substrate — fork honker-core into owned code | Accepted |
| 012 | Forked substrate design (contract-blind boundary, fidelity, port deltas) | Accepted |
| 013 | Fold the forked substrate into alkstore-sqlite (no fourth crate) |
Accepted |
| 014 | Transactional outbox enqueue (outbox_enqueue_tx on TxHandle) |
Accepted |
| 015 | Streams depth (carried-metadata keys, global-FIFO ordering, StreamEvent, trim_to) |
Accepted |
| 016 | Deployment honesty (no runtime capability surface; compile-time identity + matrix) | Accepted |
| 017 | Contract versioning (core crate's semver is the contract version; change classes, pairing carriers, lockstep duties) | Accepted |
| 018 | Substrate provenance register and cherry-pick procedure (PROVENANCE.md in-tree, per-delta entries) |
Accepted |
| 019 | Mechanism-handle surfaces (handle traits, Job/Schedule shapes, worker_id, StopToken) |
Accepted |
| 020 | Enqueue-option semantics (delay/run_at, expires, scheduler stamp source, payload encoding) | Accepted |
| 021 | Third review round (tx-read methods on TxHandle, Job.claimed_at, schedule() queue-argument validation, drop = rollback, receiver close/error arms) |
Accepted |
| 022 | Contract-suite layout (shared internal suite crate, store-factory parameterization) | Accepted |
| 024 | The mem engine (alkstore-mem — full contract v1, honest-ephemeral, wasm32 compile-clean acceptance) |
Accepted |
Non-goals
- Not a database abstraction/ORM: the store covers the reactive coordination surface, not general row storage (consumers keep their own engines/schema for business data, sharing the connection when appropriate — the outbox pattern).
- Not a network service: no transport; any networked ops surface rides alkcall and is a separate future decision (store layer stays substrate-free, the alkblobs store-layer precedent).
- Not multi-machine on SQLite: single-host honesty is inherited (deployment.md; the boundary's location in the surface is decided — ADR-016).
Evidence base
Phase 0 (docs/research/phase-0.md) is complete: two POCs
(poc-sqlite-posture-findings.md, poc-pg-posture-findings.md) passed
all gate conditions; the consumer inventory
(consumer-inventory.md) graded every feature row. The Phase 1
architecture work runs over that complete evidence base; open Phase 1
work is tracked in open-questions.md.