Files
alkblobs/docs/architecture/decisions/001-substrate-posture-and-ops-placement.md
T
glm-5.3-flash 4b5009d85a docs(architecture): Phase 1 spec tree — 7 specs, ADRs 001-006, OQ tracker
- overview/hashing/store-api/backends/gc/ops specs + README index,
  all draft status with YAML frontmatter and cross-referenced ADR/OQ
- ADR-001: substrate posture (store layer alkcall-free = family
  conformance, not deviation) + ops placement decided (feature-gated
  module, no consumer-waited deferral)
- ADR-002: canonical git-blob-sha-256 + key encoding — the declared
  wire-format ADR (one-way door)
- ADR-003: backend contract, sqlite/fs/mem tiers, pure-function
  dispatch, no migration, unknown-length buffering semantics pinned
- ADR-004: exactly two production backends; manifest/path-tree layers
  are consumer-side (the corrected two-vs-three-backends framing)
- ADR-005: pooled CAS, three liveness sources, delete windows with
  pin-before-publish + delete-time arbitration (race closed), no
  ambient scheduling
- ADR-006: whole-blob verification; chunk-tree encodings excluded by
  scoping, not deferred on a paused consumer
- open-questions.md: OQ-BL-01..06 resolved with ADR cross-refs; OQ-07
  /08 externally-owned, OQ-09 deferred(scope) with SDD tracker task
- AGENTS.md: status updated to Phase 1, gix-odb path corrected

Verification: cargo test / clippy -D warnings / fmt --check clean
2026-10-01 14:45:32 +00:00

134 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-001: Substrate posture and ops-surface placement
## Status
Accepted
## Context
Phase 0 settled that a network ops surface for the blob store exists
and rides alkcall (OQ-BL-01). Two residuals were left:
1. **Placement** — feature-gated `ops` module in this crate vs a sibling
crate. Phase 0's lean for the module was gated on "deciding input:
what alkgit's replicator wires first" — a deferral pattern a review
of the convergence has rejected as Schrödinger's code: alkgit is
paused *because* this crate is being built; "what the paused consumer
does first" never becomes a deciding input. The deferral would never
collapse; the decision must stand on evidence in hand.
2. **Deviation framing** — the user-level concern that a crate whose
store layer carries no alkcall dependency *deviates from the family
pattern* (alktty, alktunnels, alksocks all depend on
`alkcall = 0.8.x`). If this is a deviation, its rationale must be
explicit rather than assumed; if it is not, the conformance claim
must also be explicit, or every future session re-litigates it.
Also loaded here: the user-level concern that the family pattern must
be stated so the two *coming network consumers* (alkgit, alkfs) — which
will certainly use alkcall — see this crate fitting under it, not
beside it.
## What is actually decided
### 1. The family pattern is "nothing invents wire/ops/ACL", not "everyone depends on alkcall"
The family pattern (conventions 4–6) is: wire framing, op dispatch, and
authorization are invented at most once in the family — by alkcall — and
consumed everywhere else. Whether a crate *depends* on alkcall is then a
function of whether it faces the network, not of family membership:
- alkcall itself: zero sibling deps (it *is* the substrate; the pattern's
home).
- alktty / alktunnels / alksocks: depend on alkcall because they define
network-facing protocol layers.
- alkblobs' **store layer**: does not face the network. It is a library
seam — the same posture as alkcall's own `Connection` (handed in, not
dialed) and alktty's `TtyBackend` (backend-injected). A store cannot
carry alkcall without violating convention 4 (substrate-agnostic by
construction): the store must not know whether bytes arrive over a
network.
- alkblobs' **ops surface**: faces the network; rides alkcall like every
other network-facing family member.
The store layer's alkcall-freeness is therefore **conformance, not
deviation**: it is the pattern applied to a layer that has no network
side. The deviation framing dissolves once layers are named — a crate
may contain both a substrate-free core and a substrate-backed shell, as
this one does. What would be an actual deviation — an ops surface that
invents its own framing/ACL to avoid the alkcall dependency — is
rejected.
### 2. Why alkcall for the ops surface (kept from Phase 0, now recorded)
Both op shapes the blob family needs exist there with validation: JSON
ops with schema validation + typed error schemas (alkcall ADR-016), and
binary streaming via `channel_open` (alkcall ADR-047) in both
directions (Sub ADR-021 = verified fetch; Pub/Sink ADR-046 = put). ACL
maps onto `AccessControl` (alkcall ADR-011) + the ADR-017 privilege
model; an in-band scheme would be a second authorization story
(convention 6). Not adopting iroh-blobs' wire surface ("tickets",
postcard, provider protocol) remains deliberate (convention 7) — the
choice is not "iroh's protocol vs nothing", it is "the family substrate
vs inventing one".
### 3. Placement: feature-gated ops module, decided (no deferral)
The ops surface ships as an `ops` module gated behind feature `ops`
(default-off; the alkcall dep is feature-gated so the base crate stays
lean — convention 8). Decided on evidence in hand:
- The op family is **store-shaped** (hashes in, streams out), not
policy-shaped; a sibling crate buys nothing until a second
store-adjacent op family appears.
- Convention 8 is established family precedent (alktunnels/alksocks
feature-gate their alkcall-facing layers the same way).
- Module→sibling promotion, if ever needed, is mechanically additive —
the types move, the consumers re-point; no stored bytes change. The
cost of being wrong is bounded and small.
Promotion criteria (documented, not a hedge): if a second store-adjacent
op family appears outside this crate's scope (e.g., an alkfs sync op
family that wants blob ops but not this crate's registration), the ops
module may be promoted to a sibling crate. That would be a new ADR — the
decision recorded here is where it ships *now*.
### 4. The store layer never grows wire concerns — even in ops code
The ops module is a consumer of the store facade, not a side door: all
ops go through put/get/stat/read_range/pin (ADR-005, ADR-006). There is
no ops-internal path touching backends directly. This is what makes the
claim "if the ops surface were re-homed, the store loses nothing" true
rather than aspirational.
## Consequences
**Positive**
- Family conformance is explicit and traceable; no future session
re-litigates "why doesn't the store use alkcall".
- The base crate builds lean and substrate-free; the ops story exists
without forcing an alkcall dependency onto non-network consumers.
- alkgit/alkfs integrate the store core and the ops shell à la carte,
with the authorization story unified in alkcall.
**Negative**
- The feature-gate is a configuration surface to preserve in CI
(default + all-features builds; see AGENTS conventions).
- Two-layer naming ("alkblobs core" vs "alkblobs ops") must be kept
tight in docs so consumers don't mistake the shell for the core.
**Neutral**
- alkgit/alkfs will still carry *their own* direct alkcall deps for
their protocol layers; nothing about ADR-001 constrains them.
## References
- `docs/research/phase-0.md` OQ-BL-01 (substrate settlement, promoted
into this ADR)
- `docs/sdd_process.md` (deferral policy — Schrödinger's-code rule)
- [overview.md](../overview.md); [ops-surface.md](../ops-surface.md)
- alkcall ADRs 011/012/016/017/021/046/047/050
- ADR-003 (backends stay substrate-free too); ADR-005 (facade-only ops
access); ADR-006 (verified fetch)