Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
157 lines
6.5 KiB
Markdown
157 lines
6.5 KiB
Markdown
---
|
|
id: APPROVAL-WP-0007
|
|
type: workplan
|
|
title: "Bounded-execution admission carrier: assurance floor, correspondence and revoker evidence"
|
|
domain: infotech
|
|
repo: approval-engine
|
|
status: active
|
|
flavor: implementation
|
|
owner: approval-engine
|
|
topic_slug: netkingdom
|
|
created: "2026-10-31"
|
|
updated: "2026-11-01"
|
|
depends_on: []
|
|
state_hub_workstream_id: "af365506-f451-50b5-a0a2-f0ba9a0a4d9c"
|
|
---
|
|
|
|
Demand: NetKingdom NK-WP-0056-T02 owner request (hub message
|
|
40344380-ea33-4670-9b27-dafca1699d36) to carry a Bounded Execution Admission
|
|
v0.1 (proposed) as an approval. Owner answer:
|
|
`docs/bounded-execution-admission-carrier.md`. The binding, consume and
|
|
revoke already work. This plan closes the engine-side gaps the answer found.
|
|
Every change is opt-in at issue, so existing approvals and consumers keep
|
|
their behavior. Native digest, CAS consumption and no-unconsume are unchanged.
|
|
|
|
## Declared minimum approver assurance
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "9ce0aca8-7694-53c6-a76a-f78d395a9861"
|
|
```
|
|
|
|
Add an optional `required_assurance` (minimum `level`, ordered aal1 < aal2 <
|
|
aal3) to `POST /v1/approvals`. Store it and serve it in the object and the
|
|
claim, and inherit it on supersede. `add_entry` refuses an entry whose
|
|
verified token `assurance.level` is weaker or missing. `_evaluate` and
|
|
`consume` refuse when an existing entry does not meet it. Schema migration
|
|
leaves old rows NULL, meaning no floor. Tests cover aal1 refused, aal2
|
|
accepted, missing level refused, supersede inheritance and the claim
|
|
reason_code. Serves BEA-C4. Whether KeyCape emits `aal2` is KeyCape's
|
|
question, outside this plan.
|
|
|
|
Done 2026-10-31 in source (schema v7, `tests/test_required_assurance.py`,
|
|
contract in `docs/approval-claim.md`). The member is present only when
|
|
declared, so undeclared claims keep their exact bytes for strict readers.
|
|
Not released or deployed: a v7 image and a founder-run rollout are still needed.
|
|
|
|
## Opt-in consumer = actor (`actor_consumes`)
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T02
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "9657820d-23f9-54d9-920c-8f2a72110ad0"
|
|
```
|
|
|
|
Optional issue flag `actor_consumes: true` on `POST /v1/approvals`: the
|
|
consuming subject must equal `binding.actor`. Closes the denial-of-run gap
|
|
(today any tenant service with `approval:consume` can spend the object). Adds
|
|
no identity mapping, because the actor is already pinned to the creator's
|
|
token subject. Serves GH-DEC-2026-026 build-mode relaxation B2.
|
|
|
|
Split from the original T02 by founder decision 2026-11-01, relayed by the
|
|
net-kingdom coordinator: "Agree to split APPROVAL-WP-0007-T02: build
|
|
actor_consumes now (opt-in at issue; the bounded executor will set it);
|
|
principal_approves keeps waiting for the KeyCape subject mapping."
|
|
`principal_approves` moved to T05.
|
|
|
|
Done 2026-11-01 in source (schema v8, `tests/test_actor_consumes.py`,
|
|
contract in `docs/approval-claim.md`). Stored on the object, served in GET and
|
|
the claim only when declared (undeclared claims keep their exact bytes),
|
|
carried in audit `details`, inherited on supersession (existing-successor
|
|
mismatch is 409). Consume refuses with 403 when the consuming subject differs,
|
|
also on idempotent retries; an internal call without a consumer fails closed.
|
|
Legacy rows migrate without the flag. Same commit: under `human_control`,
|
|
`add_entry` refuses (403) the binding actor as approver (GH-DEC-2026-026
|
|
Ruling 6); human_control alone already covered a service actor, this covers a
|
|
human actor. Not released or deployed: a v8 image and a founder-run rollout
|
|
are still needed.
|
|
|
|
## Opt-in approver = principal (`principal_approves`)
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T05
|
|
status: wait
|
|
priority: medium
|
|
state_hub_task_id: "b0b9398d-f9f9-507f-b8f7-91ed10c1a9b8"
|
|
```
|
|
|
|
Optional issue flag `principal_approves: true`: every entry subject must equal
|
|
`binding.principal`. It gives `principal` an enforced meaning, so it waits.
|
|
Wait reasons: the KeyCape subject mapping (NetKingdom's principal strings must
|
|
equal KeyCape subjects exactly) and the S5 doctrine. Split from T02 by the
|
|
founder decision of 2026-11-01 quoted under T02. Until it lands, any human
|
|
holding `approval:approve` for the tenant can approve, and the executor checks
|
|
the entry subject itself (`docs/bounded-execution-admission-carrier.md` §3).
|
|
|
|
## Revoker identity in revocation evidence
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T03
|
|
status: todo
|
|
priority: medium
|
|
state_hub_task_id: "7ceb5ffd-3ba8-5e9d-96a2-5e89c38a5c43"
|
|
```
|
|
|
|
Record the verified revoker's subject, principal_type and assurance, plus an
|
|
optional bounded `reason`, in the `approval.revocation` outbox event. Persist
|
|
them on the object (the `revoked_by` fields) without changing the CAS or
|
|
terminal semantics. This is the evidence for the founder's pre-start kill
|
|
switch (NK-WP-0056-T07). Do not require the revoker to equal the principal:
|
|
revocation must stay possible without the holder.
|
|
|
|
## Synthetic bounded-execution carrier proof
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T04
|
|
status: todo
|
|
priority: medium
|
|
state_hub_task_id: "673890fe-3d49-5fe3-8ad7-c8c73e4d9376"
|
|
```
|
|
|
|
Add an in-process test and an example request covering the full path: the
|
|
executor (service) creates the request (`action: bounded-execution`,
|
|
`target: {admission_id, admission_digest}`, `human_control: true`,
|
|
`pdp_path: false`); a human aal2 entry approves it; GET binding comparison;
|
|
consume with `request_digest` = admission digest; idempotent retry; a
|
|
different digest returns 409; revoke before consume gives 409 on consume;
|
|
consumption outside the window is refused. Synthetic fixtures only, with no
|
|
live service.
|
|
|
|
## Founder decision 2026-10-31
|
|
|
|
Approved by the founder (`proposed` -> `ready`) after the NK-WP-0056-T02 owner answers (hub decision 19afd15e).
|
|
|
|
## Self-acceptance as an exception with handlers (backlog)
|
|
|
|
```task
|
|
id: APPROVAL-WP-0007-T06
|
|
status: todo
|
|
priority: low
|
|
```
|
|
|
|
Founder direction, 2026-11-01 (gate-house GH-DEC-2026-026 addendum, relayed by the net-kingdom coordinator).
|
|
Today, under `human_control`, an entry whose subject equals `binding.actor` is refused (403, `5c45d3c`), and that
|
|
stays the default.
|
|
|
|
Later, self-acceptance may become a declared exception on the approval:
|
|
|
|
- explicitly accepted by the self-accepter;
|
|
- with exception handlers bound to it, such as eligibility exclusions (trainees) or a publication plus cooldown
|
|
window in which named others can veto before the approval becomes valid.
|
|
|
|
The engine would hold the exception and its window and refuse validity until the window closes without a veto.
|
|
Doctrine on which classes admit it comes from gate-house (GH-WP-0005-T07). Not to be implemented now.
|
|
|