net-kingdom/canon/standards/bounded-execution-admission_v0.1.md
tegwick db56d8468e
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
NK-WP-0056-T12 done: pilot operations; synthetic rotation and revert pass
operations.py: OpenBao KV v2 CAS, LLDAP self-rotation (forward and revert),
ExternalSecret force-sync and status, rollout restart (RolloutFailed is a
definite failure), pilot checks. synthetic_pilot.py on the T08 stack plus
OpenBao 2.5.4 with CCR-2026-0058's policy: rotation completed; a one-off wrong
delivery reverted in declared order with the earlier value restored.

Runner: revert steps run in declared order (BEA-10), definite failures revert,
unknown outcomes stop, receipts carry error types. Found and corrected (in
railiance-platform) CCR-2026-0058's required_parameters refusing reads.
Packaging moved to new T13. 29 executor tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 994874@bnt-lap001
Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
2026-10-31 16:24:17 +01:00

254 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: netkingdom-bounded-execution-admission-v0.1
type: standard
title: "NetKingdom Bounded Execution Admission v0.1"
domain: netkingdom
status: proposed
version: "0.1"
owner: net-kingdom
created: "2026-10-31"
updated: "2026-10-31"
last_reviewed: "2026-10-31"
review_interval: 3m
scope: accountable-automation
schema:
- canon/schemas/bounded-execution-admission_v0.1.schema.json
validator:
- tools/bounded-execution-admission/validate.py
examples:
- examples/bounded-execution-admission/
related:
- canon/standards/service-identity-privilege_v0.1.md
- canon/standards/playbook-capability-contract_v0.2.md
- workplans/NK-WP-0056-bounded-executor-path.md
- workplans/NK-WP-0040-execution-attribution-receipt.md
---
# NetKingdom Bounded Execution Admission v0.1
## 0. Status
Proposed (NK-WP-0056-T01). Nothing here is enforced by a running service yet. The executor runtime
is T02/T03, budget and kill-switch enforcement is T07, and the first live use is the T06 pilot. A
document that passes the validator is a well-formed **request**. It is an **admission** only together
with a carrier record that a person approved (section 5).
## 1. Purpose
SI-15 names the golden path *prepare → admit → execute → verify → retain*, and allows "a bounded
executor under the admission" at step 3. This standard defines what that admission is. The founder
approves, at `aal2`:
- an exact plan, by digest;
- a validity window;
- a scope ceiling in SI `MODE @ SCOPE` terms;
- a blast-radius rule;
- a stop-and-revert policy tied to named machine checks;
- a risk class;
- an expense budget.
The executor consumes the admission once. Top-level decisions and guardrails stay with the person,
and execution below them is delegated, bounded and reconstructable.
Out of scope: who runs the executor (T02), how a carrier stores approvals (Approval Engine owns that),
and the receipt's emission and custody (NK-WP-0040-T02).
## 2. Vocabulary
| Term | Meaning |
| --- | --- |
| Admission request | The immutable document defined here (`BoundedExecutionAdmission`), identified by `spec.id` and by its digest |
| Carrier | The system that records a person's approval of one request and its single consumption: Approval Engine, or a founder SSH signature (NK-WP-0049 practice) |
| Admission | A request plus a carrier record that passes BEA-C1..C7 |
| Executor | The non-human identity that runs the plan: a `WORKLOAD`, `SERVICE` or harness-launched `AGENT` (SI-14) |
| Step class | `delegable` (the executor may run it under an admission), `root-class` (the founder admits each run; high risk only), `human-intrinsic` (never in an admission: for example a person's own password) |
| Check | A named, source-pinned machine check that must pass after a mutating step |
| Consumer | The unit the blast radius counts: a service whose behaviour a step can change |
## 3. The admission request
A YAML or JSON document validated by `canon/schemas/bounded-execution-admission_v0.1.schema.json`:
| Field | Meaning |
| --- | --- |
| `spec.id`, `title`, `purpose`, `work_ref` | Identity and intent. `id` is `BEA-YYYY-NNNN` |
| `risk_class` | `low`, `medium` or `high`. Bounds the window, the attempts, the ceiling and the step classes |
| `window.not_before` / `not_after` | When the admission can be consumed. At most 72 h (low), 24 h (medium) or 4 h (high) |
| `approval` | Who may approve (`approvers`, `HUMAN`, `aal2`) and through which carriers |
| `executor` | The executing identity, its principal type and runtime |
| `scope_ceiling` | Grants `{mode, scope, environments}` that bound every step. Scopes are `/`-separated paths, and a grant covers its own scope and anything below it |
| `blast_radius` | `unit`, the `consumers` the plan may touch, and `max_concurrent` |
| `trust_requires` | Playbook Capability Contract trust states the executor must find satisfied before the first step |
| `plan.source` | Repository, commit, and the sha256 of every file the run executes or checks with |
| `plan.steps`, `plan.revert_steps` | Ordered steps: `operation`, `target`, `consumer`, `class`, `mutates`, `requires` (the grant it needs), `checks_after`, `revert` or `irreversible` |
| `plan_digest` | `sha256:` over the canonical JSON of `plan` (sorted keys, no whitespace, UTF-8) |
| `checks` | Named checks: `ref` (a pinned file), `sha256`, `timeout_seconds`, optional `settle_seconds`, `expect: pass` |
| `stop_and_revert` | `on_check_failure` and `on_budget_exhausted` (`revert` or `stop`); `on_unknown: stop`; `on_revert_failure: stop-and-alert` |
| `budget` | `wall_time_seconds`, `attempts`, and `cost` (CPU seconds, egress bytes, currency, amount) |
| `consumption` | `single_use: true`, `consume_before_first_step: true` |
| `receipt` | The NK-WP-0040 execution-attribution receipt, where it goes, and that it is retained by hash with no secret values |
| `not_authorized` | What this admission explicitly does not cover |
`settle_seconds` exists because of the T08 finding: privacyIDEA's resolver cache hides a broken bind
for up to 120 s, so a check run sooner proves nothing.
## 4. Rules (validator: `validate`)
| Rule | Requirement |
| --- | --- |
| BEA-1 | The document matches the schema (`HUMAN` approvers at `aal2`, single use, `on_unknown: stop`) |
| BEA-2 | `plan_digest` equals the digest of `plan`: the person admits exactly this plan |
| BEA-3 | The window is positive and no longer than the risk class allows |
| BEA-4 | The executor is not `HUMAN` and not an approver. An `AGENT` must be harness-launched (SI-14) |
| BEA-5 | No `SOVEREIGN` or `GOVERN` in the ceiling. No `CONSTRUCT` in `PRODUCTION`. `ADMINISTER` needs `max_concurrent: 1`; at a whole-service scope (no `/`) it needs `high` risk and only `root-class` steps |
| BEA-6 | Least privilege: every step and revert step is covered by a ceiling grant of the same mode, and every ceiling grant covers at least one step |
| BEA-7 | No `human-intrinsic` step. A `root-class` step needs `high` risk |
| BEA-8 | Every step's consumer is in the blast radius. `medium` and `high` risk change one consumer at a time |
| BEA-9 | Every mutating step names checks, revert steps included: a revert must prove the restored state with its own checks. Every check is used and pinned: its `ref` and `sha256` appear in `plan.source.files` |
| BEA-10 | Every mutating step has a revert step, or is `irreversible` at `high` risk. Revert steps mutate and do not nest. Step ids are unique. The executor runs revert steps in their declared order (the recovery order: data, then delivery, then restart), limited to those whose forward step ran |
| BEA-11 | A revertable plan reverts on check failure. A plan with nothing to revert stops |
| BEA-12 | The wall time fits the window, the checks (timeouts plus settle times) fit the wall time, and attempts are at most 3 (1 at `high` risk) |
| BEA-13 | The executed source is pinned by file and commit |
| BEA-14 | No secret-shaped value anywhere in the document |
Findings name the rule and a location, never a value.
## 5. Carrier and consumption (validator: `check-approval`)
A carrier record (`netkingdom.io/bounded-execution-approval/v0.1`) states the carrier, its approval
reference, the admission id and digest, the approver (name, principal type, assurance), the approval
time, and the consumptions.
The **admission digest** is `sha256:` over the canonical JSON of the whole request, including
`plan_digest`. That digest is what a carrier binds.
| Rule | Requirement |
| --- | --- |
| BEA-C1 | The carrier is one the request allows, and the record has the expected shape |
| BEA-C2 | The record names this admission's id and digest |
| BEA-C3 | The approver is a listed `HUMAN` approver and not the executor |
| BEA-C4 | The approval was made at `aal2` |
| BEA-C5 | The approval was made before the window closed |
| BEA-C6 | Consumed at most once |
| BEA-C7 | The consumption happened in the window, after approval, by the executor identity |
**Approval Engine (preferred carrier).** Approval Engine already stores an approval that binds action,
actor, principal, purpose and target by digest, carries a validity window, and is consumed exactly
once (`POST /v1/approvals/{id}/consume`), read back through `GET /v1/approvals/{id}`. The proposed
mapping, which is for agreement with Approval Engine and Access under T02, not decided here:
- `action: bounded-execution`;
- `actor`: the executor identity;
- `principal`: the approver;
- `purpose`: `spec.purpose`;
- `target: {admission_id, admission_digest}`;
- validity equal to `window`;
- the executor consumes before its first step (`consume_before_first_step`).
Approval Engine's own contract notes that `consume` does not check that the request digest is
equivalent to the decision. An independent mediator must therefore fetch the approval and compare
`target.admission_digest` itself. A `consumed: true` returned by the caller is not proof.
**Founder SSH signature (transitional carrier).** This is the NK-WP-0049 practice:
- the founder signs the canonical request with `ssh-keygen -Y sign`, namespace
`netkingdom-bounded-execution`;
- the signature is verified against the repository's `allowed_signers`;
- `aal2` is established by the signing key's custody, not by a token.
Single use is then only as strong as the executor's own consumption ledger. That is acceptable for
the transition and is recorded as such in the receipt.
## 6. Mapping
**SI-15 golden path.**
| Golden-path step | What it means here |
| --- | --- |
| Prepare | `plan.source` (source-reviewed, pinned files), tested on synthetic fixtures. For bind changes, NK-WP-0056-T08's synthetic stack |
| Admit | Carrier approval of the admission digest at `aal2` |
| Execute | The executor, inside `scope_ceiling`, `blast_radius`, `window` and `budget`, consuming once |
| Verify | The named checks during the run. Afterwards, an independent verifier checks the carrier record (BEA-C1..C7) and the receipt's source hashes |
| Retain | The receipt, kept by hash with no secret values |
**Playbook Capability Contract.** `trust_requires` uses the contract's trust-state vocabulary. The
executor refuses to start unless each state is satisfied, using the readiness checks of the
capability declarations it runs. A step's `operation` should name a declared capability operation
once Railiance declarations exist for it.
**Execution-attribution receipt (Playbook Capability Contract v0.2, NK-WP-0040).** One receipt per run:
| Receipt field | Filled from |
| --- | --- |
| `initiator` | The carrier's approver (the founder). The person authorized the run; the executor ran it |
| `delegation` | Absent unless the approver acted through an agent |
| `executing_workload`, `runtime_principal` | `executor.identity`, `executor.runtime` |
| `run_id` | New per run |
| `source_commit` | `plan.source.commit` |
| `artifact_digest` | `plan_digest` |
| `target`, `action` | Per step |
| `decision_refs` | `spec.id`, the admission digest and the carrier's approval reference |
| `time_interval` | Measured |
The receipt adds the outcome per step, check results, whether a revert ran, and the budget used. It
never holds a secret value.
Founder decision 2026-10-31, agreed with audit-core (AUDIT-WP-0018):
- When the executor consumes the admission, before its first step, it emits a separate
`execution.run.started` event, so a run that crashes before its receipt is still detectable.
- It then emits one `execution.attribution.receipt` per run:
- `target` and `action` at the admission level (`admission:<spec.id>`, `bounded-execution`);
- per-step detail in `steps[]`;
- `decision_refs` labelled `admission`, `admission_digest` and `approval`.
- Envelope ids are `run:<run_id>:started` and `run:<run_id>:receipt`.
**Policy decision point.** access-engine's gate (FLEX-WP-0040) has two decisions: `start` before
consume, and `execute-step` after it. The T06 pilot runs without the gate: its receipt records the
gate as `not-evaluated` and claims no allow. Any further admission needs the gate, or an exception
the founder records (founder decision 2026-10-31).
**Failure classes.** A failed check, or an operation that reports a definite failure (for example a rollout that never becomes ready), reverts per `on_check_failure`. An error whose outcome is not established stops (`on_unknown: stop`) without reverting. Receipts name the error type, never its message.
**Credentials during a run.** Write roles exist only during the admitted window: the platform creates
them when an admission is approved and deletes them after the receipt (CCR-2026-0058). A directory
bind rotates its own password, so no password-manager account is needed (founder decisions
2026-10-31).
## 7. The bind split as admissions
NK-WP-0056 tagged each step of the 2026-10-30 LLDAP bind split. The examples express them:
| Runbook step | Class | Example |
| --- | --- | --- |
| 2, 4, 10 (verify) | delegable, read-only | `bind-split-read-only-verify.yaml`: low risk, `OBSERVE` only, nothing to revert |
| 3 create `svc-*` accounts | root-class | `bind-split-create-service-accounts.yaml`: high, whole-directory `ADMINISTER` (no narrower LLDAP grant, SI-X6), revert deletes what was created |
| 5–8 switch one consumer | delegable | `bind-rotation-svc-authelia.yaml`: medium, one consumer, a synthetic-journey check after the restart, a revert per step. This is the T06 pilot shape |
| 9 change LLDAP's super-admin | root-class | `bind-split-switch-super-admin.yaml`: high, one attempt, `settle_seconds: 125` on the journey check (privacyIDEA cache) |
| 10 `admin`'s own password; seal `ld-admin` | human-intrinsic | Refused: `tools/bounded-execution-admission/fixtures/invalid/human-intrinsic-step.yaml` (BEA-7) |
The examples are illustrative. They pin real files at a real commit, but the executor and its
operations do not exist yet.
## 8. Conformance
```bash
make bounded-execution-admission-test # validator tests
make bounded-execution-admission-check # validate the examples
python3 tools/bounded-execution-admission/validate.py check-approval <admission> <carrier-record>
python3 tools/bounded-execution-admission/validate.py digest <admission>
```
Agents may run these: they are offline and read-only. Writing an admission is preparation, and
approving one is the founder's act.
## 9. Open items
- T02: the carrier mapping with Approval Engine and Access (action name, actor and representation,
digest equivalence, consume scope for the executor), and the executor runtime owner.
- T03: the executor identity and its grants, matching the ceilings in the examples.
- T07: enforcement of budget and attempts at run time, the founder's kill switch, and aggregate
review through posture feedback (NK-WP-0031).
- NK-WP-0040-T02: receipt emission, custody and schema in audit-core.
- Recurring admissions (one approval for repeated runs of the same plan) are deliberately excluded
from v0.1: every admission is single use.