feat(ops): blobs/fetch — the verified Sub op and the fanout seam's consumer side (wave 7)
- the open triple (fetch_openables): the channels/blobs/sub spec with
the channel_open marker on the alk/blobs ALPN (read action,
closed-by-default), the awaited establisher whose reply fields carry
the ack {source, len?, slice_digests?}, and the pump handler over
the channel's stream (bounded read windows; the fanout feed
forwarded chunk-for-chunk)
- verified streaming per ADR-006: store-first consult, a live
broadcast taps (the tap-time-set discipline documented; the
re-fetch is the partial-feed degrade), the ranged form carries
read_range's slice-digest convention over exactly the served bytes
- the miss maps onto ADR-049's vocabulary (channel:open_failed,
reason unknown_resource, the digest in the message; the declared
BLOB_MISSING contract disclosed on the spec); unverifiable reads
refuse with handler_error, never stream
- the producer-side design point decided and recorded against the A3
text: fetches never produce read-side broadcasts (module doc,
op doc, ops-surface.md)
- five harness tests through the channel halves: the verified whole
round-trip + consumer hash-check, ranged + ack digests, the
live-broadcast tap served without a commit, the late joiner served
by the store path, the typed miss, the closed-by-default denial
Verification: cargo test (default) + --features ops + --all-features
green; clippy -D warnings on all three shapes; fmt --check clean;
cargo doc --no-deps clean on both shapes.
This commit is contained in:
1 parent
5144b6a733
commit
d927da2ee5
4 files changed
+954
-24
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-10-03
|
||||
notes: "ADR-012 §2 — put response {token, digest}; have token-renewal form; fetch verification wording; non-fleet token scope"
|
||||
last_updated: 2026-10-13
|
||||
notes: "ADR-012 §2 — put response {token, digest}; have token-renewal form; fetch verification wording; non-fleet token scope; fetch establishment ack + miss mapping"
|
||||
---
|
||||
|
||||
# Ops surface (alkcall-backed network operations)
|
||||
@@ -79,17 +79,37 @@ JSON control ops (`Visibility` per ADR-001 §Decision):
|
||||
|
||||
Binary channel ops (registered via the `channel_open` marker):
|
||||
|
||||
- **`blobs/fetch`** (`Sub` — the server→client streaming op shape)
|
||||
- **`blobs/fetch`** (`Sub` — the server→client streaming op shape;
|
||||
open-op id `channels/blobs/sub`, the `alk/blobs` data-plane ALPN)
|
||||
— `{namespace, digest, ranges?}` in;
|
||||
verified bytes out: the consumer hash-checks the received whole
|
||||
against the carried digest (ADR-006's whole-blob verification —
|
||||
there are no per-chunk or per-range checks derivable under the
|
||||
canonical digest; ranged fetch responses carry the same ADR-006
|
||||
slice-digest convention `read_range` returns). Broadcast fanout
|
||||
above the store:
|
||||
one reader, store arm + subscriber arms (POC #3 finding A3); late
|
||||
joiners degrade to a normal post-commit `get` — identical bytes under
|
||||
CAS. Slow-subscriber policy (drop-and-late-join) is ops-layer
|
||||
slice-digest convention `read_range` returns). The establishment
|
||||
phase (ADR-049) carries the ack in the open reply's fields:
|
||||
`{source, len?, slice_digests?}` — `source` names the serving path
|
||||
(`store`/`fanout`); the whole form acks `len` from the store path
|
||||
(the hash-check's expected whole) and nothing from a fanout tap (a
|
||||
live put's tail; the hash-check runs at stream end); the ranged
|
||||
form acks one slice digest per range, in request order, over
|
||||
exactly the served bytes (over-reads clamp; the digest covers the
|
||||
clamp). The fanout consult (POC #3 finding A3's consumer side): a
|
||||
live broadcast → a subscriber arm taps it; no broadcast, or one
|
||||
that ended (a late joiner) → the normal post-commit `get` —
|
||||
identical bytes under CAS; a mid-put tap has no prefix, so the
|
||||
consumer's hash-check on a partial feed fails and the re-fetch is
|
||||
the degrade. Fetches never produce read-side broadcasts (recorded
|
||||
against the A3 text — the put handler's stream reader is the
|
||||
canonical producer; concurrent fetchers each ride their own facade
|
||||
read). The miss (absent digest, no broadcast) is the typed
|
||||
missing-error schema: the declared `BLOB_MISSING` contract, answered
|
||||
on the wire as the establishment refusal (`channel:open_failed`,
|
||||
reason `unknown_resource`, ADR-049's "the requested resource does
|
||||
not exist"), the digest in the refusal's message. The need
|
||||
announcement is the fetch miss — the have/need diff is caller-side;
|
||||
no separate need op exists (the wire carries only concrete fetch
|
||||
requests). Slow-subscriber policy (drop-and-late-join) is ops-layer
|
||||
policy, not a store concern.
|
||||
- **`blobs/put`** (`Sink` — the client→server streaming op shape)
|
||||
— `{namespace, digest?, len?}` offer +
|
||||
|
||||
Reference in new issue
Block a user