Harden contract helper validators

This commit is contained in:
jb
2026-09-19 10:07:35 -04:00
parent 614bbf9290
commit 373eb60d0b
3 changed files with 84 additions and 8 deletions
+2 -2
View File
@@ -13,12 +13,12 @@
- `JobAuthority` is explicit and duplicated job-level principal/revision fields are avoided; the fence is the authority source. A user-owned job cannot promote itself to `system`; system/reference/public-feed jobs have no user dataset authority.
- A `ConsentBinding` binds one OAuth attempt to one initiating session, intent, capability and a non-empty list of per-resource requested/approved-preservation scopes. Accepted callbacks require a pending, unexpired binding; grants echo the exact bound request, are cardinality-matched, and are validated for finite counters, required scopes, server allowlists, grant revision and authorization epoch before persistence. Callback failure with an unknown consumed-code outcome returns `recovery_required`; it must not blindly replay a code. Exact orphan cleanup and recovery UX remain an operator decision.
- Transfer, revocation and quarantine are distinct authorization states. The contract carries an unresolved `StaleAuthorityObservation.maximumWindowMs`; no stale grace period is invented here.
- An inventory cursor is opaque to the caller but carries a server-side pin of selected resources, authorization revisions, epochs, snapshot generations, reference version, price version and expiry plus continuation offset/last-item state. Server state marks cursors `active` or `exhausted`; every sort uses canonical `item_id` tie-breaking; an `after_end` result always has `nextCursor: null`. Possession of a cursor/report ID is not authorization.
- An inventory cursor is opaque to the caller but carries a server-side pin of selected resources, authorization revisions, epochs, snapshot generations, reference version, price version and expiry plus continuation offset/last-item state. Server state marks cursors `active` or `exhausted`; every sort uses canonical `item_id` tie-breaking, nonnegative safe offsets and canonical decimal last IDs; every lookup rejects duplicate principals, changed caller/query/revisions/input versions, malformed state or expired pins. An `after_end` result always has `nextCursor: null`; an empty initial page must summarize zero matches. Possession of a cursor/report ID is not authorization.
- Inventory summaries use `scope: all_matching_rows`, never the visible page. An empty `after_end` page is a valid terminal response and may retain a non-empty selected summary; `nextCursor: null` and `pageState: 'after_end'` make this explicit. An empty initial page means no matching rows. Clients must not substitute page totals.
- Wallet results contain labelled `wallet_balance` components. They do not expose a combined wealth total or imply journal, transaction, order or profit coverage; each component pins credential version and wallet coverage.
- `Asset.isSingleton` is optional at the legacy boundary; query rows must normalize absence to `isSingleton: null`, never infer singleton state from quantity. T02 only freezes this contract; retaining the ESI fact during ingestion remains a downstream T06/T07 gate.
- A cursor token is opaque. Its unsigned pin is stored server-side and includes server-derived caller identity, query fingerprint, selected resources, revisions, epochs and input versions; every use revalidates that pin and current authorization. Possessing or editing a token cannot authorize or retarget a cursor, and no cursor-signing crypto is selected here.
- Capability consent separates required scopes from the explicit server allowlist and approved-existing scopes preserved during an upgrade. Arbitrary requested scopes are rejected; declarative metadata does not itself replace runtime authorization checks.
- Capability consent separates required scopes from the explicit server allowlist and approved-existing scopes preserved during an upgrade. Arbitrary or duplicate requested/preserved scopes are rejected; declarative metadata does not itself replace runtime authorization checks.
- Reports carry one dataset pin per selected resource/dataset, including coverage, authorization revision, authorization epoch and fetch/effective times; compatibility maps are indexes, not the authority. Ready-result provenance requires complete snapshot/append pins, while partial results use explicit partial states.
## Frozen request/result surfaces