Say what the tenant check is, and what view_hash is not
gate-house (GH-DEC-2026-013) was right that our wording implied a fact about the person. The tenant comparison is store isolation — does this caller belong to the store this engine serves — and a registration- supplied claim satisfies that while satisfying no doctrine about the approver's own membership. Exact equality cannot see the difference, so state it, and note that provenance gets recorded on the entry the way v4 records principal_type once the claim carries it. Answer informed-decision's R3 in the claim contract: view_hash and binding.digest answer different questions and must not be merged. Three hashes, three questions. Recommend their binding document carry our digest rather than re-canonicalize the same five fields, so the act has one canonicalization and a mismatch is detectable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyybaE7DUXrWYrhbnESCTe Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275879@bnt-lap001 Assistant-Session: eb464208-f821-41b2-bc5a-a6c33d92a8ad
This commit is contained in:
parent
849c75bb09
commit
62233c7c52
3 changed files with 89 additions and 0 deletions
|
|
@ -79,6 +79,38 @@ change that JSON and therefore the digest. A decision rendered against
|
|||
approval A for request R cannot be replayed for request R' if the consumer
|
||||
compares digests.
|
||||
|
||||
### What this digest is not — answering `INFD-IN-0001` R3
|
||||
|
||||
`informed-decision` asked whether its `view_hash` and this digest are the same
|
||||
hash, since both are described as canonicalizing a binding. **They are not, and
|
||||
they must not be merged.** Three hashes, three questions:
|
||||
|
||||
| Hash | Preimage | Answers |
|
||||
| --- | --- | --- |
|
||||
| `binding.digest` (here) | five fields: `action`, `actor`, `principal`, `purpose`, `target` | *which act* is approved |
|
||||
| `binding.pdp_digest` | the PDP's own `NewDecisionBinding` digest, recorded at issue | *which decision request* this approval corresponds to |
|
||||
| `view_hash` (`informed-decision`) | a canonicalized binding document additionally covering brief, packet, highlights, locale and UI release | *what a person was shown* when they bound themselves |
|
||||
|
||||
The overlap is only that all three canonicalize *something*. This engine's
|
||||
digest deliberately covers five fields and no more: it exists so that
|
||||
wrong-action, wrong-target and wrong-scope are distinguishable, and it has no
|
||||
opinion about presentation, which does not exist for a machine-to-machine
|
||||
approval. Widening it to cover a brief or a locale would change the digest of
|
||||
an act whose act did not change.
|
||||
|
||||
This repository already keeps two digests apart for exactly this reason rather
|
||||
than as a matter of taste — `binding.digest` and `pdp_digest` answer different
|
||||
questions and are stored separately precisely so that a reader cannot infer one
|
||||
property from the other. A third hash answering a third question is the same
|
||||
pattern, not a duplication of it.
|
||||
|
||||
**The recommended relationship, which avoids two canonicalizations of one act:**
|
||||
`view_hash`'s binding document should *carry this digest as a field* rather than
|
||||
re-canonicalize `action`/`actor`/`principal`/`purpose`/`target` itself. Then
|
||||
there is exactly one canonicalization of the act, computed here, referenced by
|
||||
the presentation hash — and a mismatch is detectable rather than being two
|
||||
independently correct answers about the same approval.
|
||||
|
||||
### Mapping from a flex-auth CheckRequest
|
||||
|
||||
| Claim binding | CheckRequest |
|
||||
|
|
|
|||
|
|
@ -8,6 +8,30 @@ mode is explicit, file-backed, and refused with `--production`.
|
|||
The verified `tenant` must exactly match the service's configured store tenant;
|
||||
cross-tenant reads and mutations are rejected before object lookup.
|
||||
|
||||
**This check is store isolation, and it is not a statement about the person.**
|
||||
It answers "does this caller belong to the store this engine serves", not "is
|
||||
this human a member of the platform zone". `GH-DEC-2026-013` rules that a
|
||||
key-cape human tenant claim may be supplied by the client registration rather
|
||||
than asserted by the directory, and forbids a consumer treating the two as
|
||||
equivalent for a decision turning on a fact about the person. Exact string
|
||||
equality cannot see that difference — a string that matches exactly matches
|
||||
whoever asserted it — so the difference is stated here instead.
|
||||
|
||||
Consequences this engine accepts deliberately:
|
||||
|
||||
- A registration-supplied tenant **is** admissible for admission to this store.
|
||||
Admission means "arrived through a channel the platform registered", the
|
||||
approver client is static and deployment-owned, and dynamic client
|
||||
registration is excluded from key-cape by design.
|
||||
- A registration-supplied tenant is **not** admissible for any doctrine that
|
||||
turns on the approver's own membership — "an approver must be a member of the
|
||||
platform zone" is a fact about the person, and this claim cannot carry it.
|
||||
No such doctrine exists today; if `gate-house` issues one, it needs a
|
||||
directory-sourced claim and this check does not become that claim by matching.
|
||||
- When key-cape emits provenance alongside the tenant, this engine records it on
|
||||
the approver entry the way schema v4 records `principal_type` — an evidence
|
||||
reader should not have to infer provenance from a string that cannot carry it.
|
||||
|
||||
| Route | Required scope |
|
||||
|---|---|
|
||||
| create approval | `approval:create` |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue