Revise specs against approval-engine's approver-surface requirements
approval-engine replied to INFD-IN-0001 with docs/approver-surface-requirements.md (31da1af, 5203f46) and two corrections. Several of my requirements were wrong or incomplete; revised rather than appended to. Corrected: - PR-02 listed only approval:approve. Wrong — the surface also needs approval:read to fetch what it renders. As drafted it would have shipped a client able to submit an entry it could never display. That changes a registration key-cape has already implemented, so it is their call (open question A). - NC-03 implied approval-engine refuses non-human approver entries. It does not; only /consume is principal-restricted, and the operator service client holds approval:approve. Enforcement of "humans bind, agents draft" is therefore ours alone, and is auditable via schema v4's entries[].principal_type — never from the shape of subject_id. Added: - PR-04 assurance shape. approval-engine persists it verbatim and accepts an empty object, so it is the only place MFA survives into the approval record. Needs auth method, acr/amr, auth_time, agreed with key-cape. - PR-05 entitlement. A 200 from the engine is not permission to view; we owe access-engine a check before rendering. Consuming a decision, not making one. - PR-06 response mapping, including 409 duplicate_approver rendered as SUCCESS (a browser double-submit is routine and the first entry stands) and 503 as fail-closed. - PR-07 and a matching INTENT wrongness condition: never render `approved` as permission to act. That is a PDP in the browser wearing UI copy. - L-05, L-06 and EvidenceModel 8b: view_hash cannot ride into the entry — the POST discards its body by design — so Stage 1 correlates by (approval_id, subject, approved_at). DoD-3 is satisfied by the triple, not by a stored hash. PRD open question 1 is answered by construction: there is no inbox endpoint and there will not be one, so the approvals-inbox shape is foreclosed upstream. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3W1dQG7GFFM9d94jFx7iR Assistant: claude-code Assistant-Model: opus Assistant-Process: 1565372@bnt-lap001 Assistant-Session: 16bb2f25-b34c-49ef-8e94-5fec3567a568
This commit is contained in:
parent
23fba00b1b
commit
08b190928f
5 changed files with 210 additions and 17 deletions
|
|
@ -189,6 +189,53 @@ between the two. Three candidate outcomes and their costs are set out in
|
|||
avoided is both digests shipping with no stated authority rule, leaving the
|
||||
estate with two canonicalizations of one act.
|
||||
|
||||
## 8b. Where `view_hash` actually lives (Stage 1)
|
||||
|
||||
**It is not in the approval entry, by the engine's deliberate design.**
|
||||
|
||||
`POST /v1/approvals/{id}/entries` reads the request body and **discards it**.
|
||||
Approver identity, `assurance` and `evidence_ref` come only from the verified
|
||||
token (`evidence_ref` is `jwt-sha256:<digest of the presented token>`). The
|
||||
reasoning is sound and we adopt it rather than argue it: a caller-supplied
|
||||
approver field is provenance, not evidence, and that entry is currently the one
|
||||
record containing no caller-supplied data.
|
||||
|
||||
Stage 1 arrangement, requiring no change to `approval-engine`:
|
||||
|
||||
1. this repository holds the presentation record and computes both hashes;
|
||||
2. it emits that record to `audit-core` as its own evidence;
|
||||
3. correlation to the approval entry is by **`(approval_id, subject,
|
||||
approved_at)`** — all three returned by the engine after a successful entry.
|
||||
|
||||
**Consequence for an auditor, stated plainly:** someone holding only the
|
||||
approval object cannot reach the presentation. They need this surface's record
|
||||
or `audit-core` as well. `GOAL.md` DoD-3 — "the approval entry is
|
||||
reconstructable from a `view_hash`" — is satisfied by the correlation triple,
|
||||
not by a hash stored on the entry.
|
||||
|
||||
Putting the binding *inside* the approval object would be a real change request
|
||||
against `approval-engine` (an optional, verified-alongside-token `view_hash` on
|
||||
the entry) and it is doctrine before it is a change request, so it goes to
|
||||
`gate-house` first. Not raised at Stage 1; recorded here so the option is not
|
||||
lost.
|
||||
|
||||
### `assurance` is the only carrier of authentication facts
|
||||
|
||||
`approval-engine` persists the token's `assurance` claim verbatim and
|
||||
interprets nothing — an empty `{}` is *accepted* and evidences nothing. It is
|
||||
therefore the single place where "this was bound under MFA" survives into the
|
||||
approval record. The shape must carry authentication method, `acr`/`amr` and
|
||||
`auth_time` at minimum, and `informed-decision` and `key-cape` own it between
|
||||
them. Nothing downstream reconstructs it.
|
||||
|
||||
### Principal type is verified evidence; `subject_id` is not
|
||||
|
||||
Schema v4 records `entries[].principal_type` from the verified token. Design
|
||||
principle 10 (humans bind, agents draft) is enforceable from the evidence chain
|
||||
by reading that field. Never infer it from the shape of `subject_id` — a naming
|
||||
convention is not a verified claim. Entries written before v4 are `null` and
|
||||
must not be read as `human`.
|
||||
|
||||
## 9. Signed attributes (L4+, horizon)
|
||||
|
||||
When AES/QES arrives, the signed attributes carry `memo_id`, `memo_version`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue