- The module doc's ACL section opens with ADR-017's two-sentence model
statement (CAS global pool; a namespace grant is capability-to-ask,
not data partitioning; isolation-by-threat-model = separate store
instances, ADR-003), placed ahead of the namespace-as-resource
naming
- The four ACL/miss-boundary texts adopt pool framing ("absent from
the pool"): stat's declared error + handler miss, fetch's declared
BLOB_MISSING description, fetch_miss's message; the sweep found no
other partitioning claim in src/ops
- OpsContext::renew gains the namespace parameter and validates it
against the mint record (RenewalTarget carries it) — confirm's
refusal discipline's parity (ADR-017 §3); a mismatch answers
UnknownToken (the decided mapping) with the mapping intact by
construction (peek, never remove); have_renewal passes the payload's
namespace
- The remote-putter role requirement (read+write; renewal and
confirmation ride read; write ⊄ read untouched) named on the pin
hand-over section (ADR-017 §4)
- Regression tests both columns: the server-less custodial wire
parity test and the live-harness fleet parity probe (mapping
intact; the row's expiry moves only on the correct-namespace
renewal); the existing tokens.rs renewal unit tests updated to the
new signature and the ACL matrix e2e green unmodified
Verification: cargo test (204), cargo test --all-features server-less
(450, pg skips clean) and against the live pglo-poc harness (450
green), clippy --all-targets -- -D warnings (both feature variants),
cargo fmt --check, cargo doc --no-deps
11 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| s1-s3-ops-authority-model | S-1/S-3 — the ops authority model lands per ADR-017: the CAS-global-pool model statement, pool-framing texts, renewal namespace parity, the remote-putter role requirement | completed | moderate | low | component | implementation |
|
Description
Review-001 S-1 + S-3, decided together as OQ-14 and resolved to ADR-017 (docs/architecture/decisions/017-ops-authority-model-cas-global-pool.md, accepted 2026-10-15). This task lands the ADR's Decision section on the ops surface. The four clauses:
- The model statement (ADR-017 §1 + §2, second sentence). The ops module doc (src/ops/mod.rs — the ACL mapping section, "namespace-as-resource mapping") carries the ADR's two-sentence model statement: the pool is a CAS global pool; a namespace grant is capability-to-ask, not data partitioning; a deployment whose threat model requires data isolation between namespaces runs separate store instances (the topology fact, ADR-003). Placed so the next reader meets the naming before the per-namespace framing could re-imply partitioning.
- The texts stop lying (ADR-017 §2, first sentence). The
ACL/miss boundary texts adopt CAS-pool framing — "absent from the
pool"; the namespace named only as the ACL resource whose grant
the caller presented. The current texts (post-split locations):
src/ops/specs.rs:47 and :83 ("the digest is absent from the
namespace's pool" — stat and have), src/ops/fetch.rs:363 (the
declared
BLOB_MISSINGerror description) and :507 (fetch_miss's message, "absent from its namespace's pool"). Any other ACL/miss-boundary text implying partitioning gets the same framing (sweep the ops modules for the pattern). - Renewal authority: namespace parity (ADR-017 §3). The renewal
form validates the presented namespace against the token's mint
record — the same discipline
confirmalready uses (src/ops/tokens.rs:460-471, the mint records the namespace). The mint record is registry-internal, so the check belongs in [OpsContext::renew] (src/ops/tokens.rs:382 — currently takes no namespace): the signature gains the namespace, the registry's renewal target carries the minted namespace (the ticket has it —RenewalTargetgains the field), and a mismatch answers the validity-domain refusal shape. The handler arm (have_renewal, src/ops/specs.rs:185-211) passes the payload's namespace. Design point — the mismatch's outcome mapping: theRenewalOutcomevocabulary is the wire contract (every non-renewed variant answerspresent: false, never an error); the natural mapping isUnknownToken(a namespace-mismatched renewal is the same "not this token's domain" answer confirm's refusal is) — decide and record in Notes. The mapping must leave the registry mapping intact (confirm's refusal discipline). - The write-only renewal duty dissolves into a named role
requirement (ADR-017 §4). Doc-only: the ops module doc states
the remote-putter role requirement — a principal performing
remote puts into a fleet must hold read+write on its namespace;
renewal (the have-token form) and confirmation (the have probe)
both ride read. No new op, no per-payload action mapping, no
authorization re-implementation (ADR-001's alkcall-ridden ACL).
The ACL matrix's
write ⊄ readalgebra is untouched.
Unchanged by design: the payload schemas, the ACL matrix, the op family, the closed-by-default enforcement (OQ-09) — ADR-017's Neutral clause. The existing ACL matrix e2e must stay green unmodified.
Live harness
The fleet renewal forms run against the local dockerized postgres
container (pglo-poc, postgres:16-alpine on :15432):
export ALKBLOBS_PG_HOST=localhost
export ALKBLOBS_PG_PORT=15432
export ALKBLOBS_PG_USER=postgres
export ALKBLOBS_PG_PASSWORD=poc
export ALKBLOBS_PG_DB=blobs
Server-less, the pg tests skip cleanly; acceptance requires green against the live harness (the AGENTS verification matrix's shape).
Acceptance Criteria
- The module doc carries the ADR-017 model statement (CAS global pool; grant = capability-to-ask; separation = separate stores) and the remote-putter read+write role requirement
- The ACL/miss boundary texts use pool framing (stat, have,
fetch's miss + declared
BLOB_MISSINGdescription); no remaining text claims namespace-partitioned data OpsContext::renewvalidates the presented namespace against the mint record (parity with confirm); a mismatched renewal answerspresent: falsewith the mapping intact; the outcome mapping recorded in Notes- Regression tests: the parity check (wrong-namespace renewal answers present: false, a subsequent correct-namespace renewal succeeds — mapping intact) on the custodial form server-less, and the fleet form against the live harness
- The existing ACL matrix e2e green unmodified (no schema or ACL behavior change)
- Gates green:
cargo test(default),cargo test --all-featuresserver-less,cargo test --all-features+ live harness, clippy--all-targets -- -D warningsacross the feature variants,cargo fmt --check
References
- docs/architecture/decisions/017-ops-authority-model-cas-global-pool.md (the Decision section, clauses 1-4 — read line by line)
- docs/reviews/001-storage-surface-general-review.md (S-1, S-3 — pre-split coordinates)
- docs/plans/implementation.md (§Wave 10; §Review gates — the security gate)
- src/ops/mod.rs (the module doc's ACL section — the model statement's home; the pin hand-over sections — the role requirement's home)
- src/ops/specs.rs (stat/have texts;
have_renewal) - src/ops/fetch.rs (
fetch_miss, theBLOB_MISSINGdescription) - src/ops/tokens.rs (
OpsContext::renew,RenewalTarget,confirm— the parity discipline's contrast) - src/ops/tests.rs (the ACL matrix e2e; the fleet renewal forms — the live-harness column)
Notes
- The mismatch's outcome mapping (decided):
UnknownToken. A namespace-mismatched renewal answers the validity-domain refusal —present: false, never an error, the same "not this token's domain" answer confirm's refusal is. Recorded onRenewalOutcome::UnknownToken(src/ops/tokens.rs:78-84), therenewdoc (ADR-017 §3 named in the composition), and thehave_pairdoc ("invalid, foreign, or namespace-mismatched — the validity-domain refusals"). - Check ordering: the namespace parity check sits between the token lookup and the digest check — the mint record is part of the token's domain, so the parity refusal precedes the one-token-one-row digest question.
- Mapping intact by construction: the renewal peeks the ticket
(
renewal_targetclones it out under the lock); it never removes — unlike confirm, which removes-then-restores on a refused match. No restore code needed; the parity check retires nothing, and the subsequent correct-namespace renewal succeeds (asserted in all three regression shapes: the tokens.rs unit test, the server-less wire test, the live fleet form). - Text sweep result: exactly four ACL/miss-boundary sites carried
the partitioning framing — specs.rs stat's declared error and
handler miss (both "absent from the namespace's pool"), fetch.rs's
declared
BLOB_MISSINGdescription, andfetch_miss's message (both "absent from its namespace's pool"). All now read "the digest is absent from the pool (…)" / "absent from the pool"; no other ops text claimed namespace-partitioned data (delete's "the digest is absent: nothing deleted" never did). - Signature change:
OpsContext::renew(namespace, token, digest)— the have handler arm (have_renewal) passes the payload's namespace (extracted besidedigestsat the handler ingress);RenewalTargetcarries the mint record's namespace. No wire change: the payload schemas are untouched; the namespace is already the ACL resource path. - Role requirement placement: the remote-putter read+write duty
sits in the ops module doc's
blobs/putpin hand-over section as a named design point (ADR-017 §4) — the contract's have-probe duty cited, the role-level-duty-not-grant-implication boundary stated, the put-without-read consequence recorded. - One transient flake in verification: the first live-harness
full run tripped
store::sweep::postgres_fleet_sweep_tests::sql_arbitration_refuses_a_foreign_pin_row_on_the_direct_path(a 300ms wall-clock expiry window sampled once, sweep.rs:1966 — the documented cadence-flake class riding the default-parallelism load, unrelated to this task's changes). It passes in isolation; the default-parallelism full-suite re-run was green end to end. No--test-threadsreduction used.
Summary
ADR-017's four clauses landed on the ops surface. (1) + (4): the ops
module doc's ACL section now opens with the two-sentence model
statement — CAS global pool; a namespace grant is capability-to-ask,
not data partitioning; isolation-by-threat-model runs separate store
instances (ADR-003's topology fact) — placed ahead of the
namespace-as-resource naming so the next reader meets the model
before the per-namespace framing; the pin hand-over section carries
the remote-putter role requirement (read+write; renewal and
confirmation ride read; write ⊄ read untouched). (2): the four
ACL/miss-boundary texts adopted pool framing ("absent from the
pool"); a grep sweep of src/ops found no other partitioning claim.
(3): OpsContext::renew gained the namespace parameter and validates
it against the mint record (RenewalTarget carries it) — the same
discipline confirm uses, answering UnknownToken on mismatch with
the mapping intact by construction (peek, never remove); the
blobs/have renewal arm passes the payload's namespace. Regression
tests added in both columns: the server-less custodial wire test
(wrong-namespace probe → present: false, correct-namespace renewal →
present: true, mapping intact through both, confirm's discipline
reachable) and the live-harness fleet form (the parity probe before
the live renewal; the row's expiry moves only on the correct-
namespace renewal). The ACL matrix e2e stayed green unmodified.
Gates: cargo test (204), cargo test --all-features server-less
(450, pg skips clean) and against the live pglo-poc harness (450,
green), clippy --all-targets -- -D warnings across both feature
variants, cargo fmt --check, cargo doc --no-deps — all pass.