5.8 KiB
5.8 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ops-json-ops | the JSON control plane — blobs/stat, blobs/have (+ the token-renewal form), blobs/delete | pending |
|
moderate | medium | component | implementation |
|
Description
The three JSON control ops (ops-surface.md §Op family), each exported
as a spec/handler pair the embedder assembles — riding alkcall's
OperationSpec + JSON Schema validation (alkcall ADR-016) and its ACL
machinery at op entry (convention 6: this module never re-implements
an authorization check):
blobs/stat{namespace, digest}→{len}— Query; the read action; the handler callsStore::stat(the fall-through general rule, ADR-012 §6.1); a miss is the typed missing-error schema (not a{len: null}).blobs/have{namespace, digests[], token?}→{present[]}— Query; read-gated (an existence probe over arbitrary digests is a discovery surface — gated exactly like fetch, not public);present[]corresponds todigests[]by index; content never traverses this op (hashes only). The renewal form (ADR-012 §2): whentokenis present, exactly one digest accompanies it — the handler verifies the token maps to that digest's pin row (the foundation's token layer), calls the crate-internalpin_renew(key, owner)via the foundation's store-core access, and answerspresent[]as usual; an expired or foreign row answerspresent: falseand no renewal occurs — the putter's backstop is re-put. Withouttoken, the pure existence probe (ADR-008'shasdiscipline) over the facade'shas. The non-fleet renewal form: the token is the held guard's handle — the probe answers present from the custody (nothing expires; ADR-012 §2's generalization); the custody/conversion lifecycle is the foundation's.blobs/delete{namespace, digests[]}— Mutation;Visibility::Internal, evaluated under the internal authority context per alkcall ADR-017 (internal calls switch authority context, never skip ACL): requires the embedder's operator/admin authority context, not a namespace grant; thenamespacefield exists for audit/triage scoping, not gating. The handler callsStore::delete(the arbitration-then-delete path; aGcRefuserefusal surfaces as the typed error schema). External discovery of an Internal op returns NOT_FOUND (alkcall ADR-017's visibility discipline).- The ACL mapping (ops-surface.md §ACL mapping; OQ-09's
resolution): namespace = resource —
resource_type: "blob-namespace",resource_id_pathselecting the payload'snamespacefield; stat/have = the read action; unlisted namespaces deny by default (closed-by-default: no ACL entry ⇒ no read, no write — a grant per namespace is the deferred cost, deliberately paid over accidental exposure). - Schemas (alkcall ADR-016): input/output JSON Schema per op +
typed error schemas; the namespace field's syntax (non-empty UTF-8
string, embedder-validated — the minting of namespaces is the
embedder's act); the digest wire form is hex (the canonical
digest hex — the same form
blobs/put's response returns; parsed via the key layer's hex ingress, malformed → the typed invalid error, rejects-never-guesses). - Tests through the harness (
ops-call-harness): stat round-trip- the miss shape; have's index correspondence (present/absent mixed); the renewal form's three answers (live row renews → present: true; expired row → present: false, no renewal; foreign token → present: false); delete under the internal authority context (an external caller gets NOT_FOUND; an internal operator-context call deletes); the closed-by-default ACL matrix (an unlisted namespace denies read; a granted identity reads).
Acceptance Criteria
- Three spec/handler pairs exported (stat, have, delete); input/ output schemas + typed error schemas per alkcall ADR-016
- The ACL mapping: namespace-as-resource with the resource_id_path; the read action on stat/have; closed-by-default verified by test (unlisted namespace denies)
- delete:
Visibility::Internal, the internal-authority-context requirement (not namespace-gated), external discovery NOT_FOUND - have's renewal form: one digest + token; verify-mapping →
pin_renew; expired/foreign →present: falsewith no renewal; the non-fleet form answers from the held custody - The handlers touch only the store facade + the foundation's crate-internal renewal access (no backend side-door — the ADR-001 §4 rule)
- Harness tests green;
cargo test(default) green;cargo test --features opsgreen; clippy/fmt green on both shapes;cargo doc --no-depsclean
References
- docs/architecture/ops-surface.md (§Op family — the three JSON ops; §ACL mapping)
- docs/architecture/decisions/001-substrate-posture-and-ops-placement.md §2/§4
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §2 (the have payload shape + the renewal form's three-step handler duty)
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md
(the
hasdiscipline the non-token form preserves) - docs/architecture/open-questions.md OQ-09 (closed-by-default, the risk-asymmetry rationale)
- alkcall (upstream checkout): ADR-016 (operation error schemas),
ADR-017 (the privilege model — internal switches context, never
skips ACL),
src/registry/spec.rs(AccessControl,resource_id_path) - tasks/ops-foundation.md (the token layer + the renewal access the have handler rides), tasks/ops-call-harness.md (the harness)
Notes
To be filled by implementation agent
Summary
To be filled on completion