Align to security layer model v0.7 and open the engine spine
The statute is accepted at v0.7; the operative form is net-kingdom/SECURITY-COMPANION.md v0.2. INTENT now declares Engine / PIP in its own voice, carries the §9.6 threat decomposition, the load-bearing heartbeat obligation, issuer and freshness on the claim, consumption as a mutation, and the custody question closed rather than held open. SCOPE.md is the first-cut boundary (nothing shipped). layer.yaml is the machine-readable declaration. The review under history/ scores intent vs scope vs the empty implementation. APPROVAL-WP-0001 sequences contracts before code and keeps consumption unimplemented until GH-WP-0002-T06. Registered with State Hub as infotech / approval-engine. Assistant: grok Assistant-Session: 01a04ceb-2057-7e20-b0f9-c282964d5dd9
This commit is contained in:
parent
ab5d69e8f1
commit
2a5a47df89
9 changed files with 1069 additions and 35 deletions
34
.custodian-brief.md
Normal file
34
.custodian-brief.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!-- custodian-brief: generated by statehub register; fix-consistency may replace this file -->
|
||||
# Custodian Brief - approval-engine
|
||||
|
||||
**Project:** approval-engine
|
||||
**Domain:** infotech
|
||||
**State Hub:** http://127.0.0.1:8000
|
||||
**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a`
|
||||
**Layer:** Engine / PIP (statute v0.7)
|
||||
|
||||
## Open Workplans
|
||||
|
||||
### v0.7 alignment and the engine spine
|
||||
|
||||
Workplan file: `workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md`
|
||||
|
||||
Open tasks:
|
||||
- T01 - Align declaration to the accepted statute (done)
|
||||
- T02 - Publish the approval claim contract
|
||||
- T03 - Specify the local transactional-outbox contract
|
||||
- T04 - Declare load-bearing cadence as heartbeat or reconciliation
|
||||
- T05 - Wait on consumption ordering; do not implement it
|
||||
- T06 - Durable object, closed state machine, authenticated entries
|
||||
- T07 - Introspection API as input claims
|
||||
- T08 - Local outbox in the mutation transaction
|
||||
- T09 - Canon T-06 and the FLEX-WP-0017 handoff
|
||||
|
||||
## Session Start
|
||||
|
||||
1. Read `INTENT.md`, `SCOPE.md`, `layer.yaml`, and `AGENTS.md`.
|
||||
2. Check inbox: `GET /messages/?to_agent=approval-engine&unread_only=true`.
|
||||
3. Scan `workplans/`.
|
||||
4. Update task statuses in workplan files as work progresses.
|
||||
|
||||
Last generated: 2026-08-29
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# state-hub: track .claude/rules
|
||||
# Claude Code local state (track shared rules; ignore machine-specific files)
|
||||
.claude/*
|
||||
!.claude/rules/
|
||||
!.claude/rules/*.md
|
||||
272
AGENTS.md
Normal file
272
AGENTS.md
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
# approval-engine — Agent Instructions
|
||||
|
||||
## Repo Identity
|
||||
|
||||
**Purpose:** PIP for the approval object: durable, authenticated, consumable, atomically supersedable.
|
||||
|
||||
**Domain:** infotech
|
||||
**Repo slug:** approval-engine
|
||||
**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a`
|
||||
**Workplan prefix:** `APPROVAL-WP-`
|
||||
|
||||
---
|
||||
|
||||
## State Hub Integration
|
||||
|
||||
The Custodian State Hub tracks work across all domains. Codex uses HTTP REST and
|
||||
the `statehub` CLI by default. MCP is opt-in because the current Codex MCP bridge
|
||||
adds severe call latency; the full administrative MCP surface remains available
|
||||
to clients that need it.
|
||||
|
||||
| Context | URL |
|
||||
|---------|-----|
|
||||
| Local workstation | `http://127.0.0.1:8000` |
|
||||
| Remote via tunnel | `http://127.0.0.1:18000` |
|
||||
| Optional local edge relay | http://127.0.0.1:18080 |
|
||||
|
||||
When an operator has enabled the edge relay, set API_BASE to the relay URL.
|
||||
Queueable writes return an explicit queued receipt if the central hub is
|
||||
unreachable. Treat that as pending local evidence, then ask the operator to run
|
||||
statehub outbox status/replay after connectivity returns.
|
||||
|
||||
Codex workspace-write sandboxes need network access enabled to reach the host's
|
||||
loopback listener. Bootstrap this once with `make -C ~/state-hub configure-codex`
|
||||
and restart Codex. The canonical REST health endpoint is `/state/health`, not
|
||||
`/health`. If a sandboxed loopback probe fails, retry it with escalated execution
|
||||
before declaring State Hub unavailable; a managed Codex permission profile may
|
||||
still enforce isolated networking. Experimental MCP can be enabled explicitly
|
||||
with `make -C ~/state-hub configure-codex WITH_MCP=1`.
|
||||
|
||||
### Orient at session start
|
||||
|
||||
```bash
|
||||
# Offline brief — works without hub connection
|
||||
cat .custodian-brief.md
|
||||
|
||||
# Active workplans for this domain
|
||||
curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# Check inbox
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=approval-engine&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
Mark a message read:
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
### Log progress (required at session close)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"summary": "what was done",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "<uuid>",
|
||||
"task_id": "<uuid>"
|
||||
}'
|
||||
```
|
||||
|
||||
Omit `workplan_id` / `task_id` when not applicable.
|
||||
|
||||
### Update task status
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "progress"}'
|
||||
# values: wait | todo | progress | done | cancel
|
||||
```
|
||||
|
||||
### Flag a task for human review
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"needs_human": true, "intervention_note": "reason"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Protocol
|
||||
|
||||
**Start:**
|
||||
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
|
||||
2. Check inbox: `GET /messages/?to_agent=approval-engine&unread_only=true`; mark read
|
||||
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||
|
||||
**During work:**
|
||||
- Update task statuses in workplan files as tasks progress
|
||||
- Record significant decisions via `POST /decisions/`
|
||||
|
||||
**Close:**
|
||||
1. Update workplan file task statuses to reflect progress
|
||||
2. If finishing a workplan: hand off **residuals** as live work records first
|
||||
(intake with `origin: residual` + `origin_ref: <WP-id>`, or a next workplan /
|
||||
decision / engagement). Do not park leftovers only in prose or `SCOPE.md`.
|
||||
Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals.
|
||||
3. Log: `POST /progress/` with a summary of what changed (name handoff ids)
|
||||
4. After workplan file changes, run:
|
||||
```bash
|
||||
statehub fix-consistency
|
||||
```
|
||||
Coding agents should run this directly; ask the operator only if the CLI or
|
||||
State Hub API is unavailable. This syncs task status from files into the hub DB.
|
||||
If C-06/C-11 reports that this host is not the identifier registrar, do not
|
||||
retry, export `STATEHUB_REGISTRAR`, or register records by hand. Commit and
|
||||
push the file-backed work first, then run the repo-manager fallback once:
|
||||
```bash
|
||||
uv run --project ~/repo-manager rmgr registrar-reconcile \
|
||||
--path . --confirm-primary --push
|
||||
```
|
||||
If unavailable, send one deduplicated registrar request to `repo-manager`
|
||||
naming the repo and canonical ids; UUID absence does not block local work.
|
||||
|
||||
---
|
||||
|
||||
## Credential and access routing
|
||||
|
||||
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||
|
||||
The companion (`net-kingdom/SECURITY-COMPANION.md`) says what the rules are;
|
||||
`ops-warden` stewards the paths through them. **Do not** message `ops-warden`
|
||||
on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||
|
||||
### Lookup (do this first)
|
||||
|
||||
```bash
|
||||
warden route find "<describe your need>" --json
|
||||
warden route show <catalog-id> --json
|
||||
```
|
||||
|
||||
Requires the `warden` CLI from `~/ops-warden`.
|
||||
|
||||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route`; inbox `to_agent=approval-engine` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | domain summary for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** | Never put secret retrieval in prompts |
|
||||
|
||||
### Quick routing table
|
||||
|
||||
| I need… | Owner | ops-warden executes? |
|
||||
| --- | --- | --- |
|
||||
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||
| API key, DB password, provider token | OpenBao | No — route only |
|
||||
| Login / OIDC / MFA | key-cape | No — route only |
|
||||
| Authorization decision | access-engine (`flex-auth`) | No — route only |
|
||||
| Approval current-state | **this engine** (not yet implemented) | No |
|
||||
| SSH tunnel | ops-bridge | No — route only |
|
||||
|
||||
### Anti-patterns
|
||||
|
||||
- Asking State Hub or `ops-warden` to vend a secret
|
||||
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||
- Treating a callable tool as permission (companion §7)
|
||||
|
||||
**Canon:** `~/ops-warden/wiki/CredentialRouting.md`
|
||||
|
||||
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||
<!-- Append repo-specific agent instructions below this marker.
|
||||
The state-hub template sync preserves content after this line. -->
|
||||
|
||||
## This repository's layer
|
||||
|
||||
Engine, role **PIP**. Statute v0.7, companion v0.2. Declaration: `INTENT.md`
|
||||
frontmatter and `layer.yaml`.
|
||||
|
||||
- Answer "is this approval valid for this exact binding, and has it been used?"
|
||||
Never "may this actor do X".
|
||||
- Do not implement consumption until `GH-WP-0002-T06` / `APPROVAL-WP-0001-T05`
|
||||
settles the contract with `access-engine`.
|
||||
- The outbox is local. Do not emit synchronously to `audit-core` inside a
|
||||
mutation transaction.
|
||||
- Approval evidence is load-bearing. Atomicity covers crash, not compromise.
|
||||
- An approval is not a Railiance workload.
|
||||
- Remote hub: this host reaches State Hub on `http://127.0.0.1:8000` (primary
|
||||
on railiance01). Do not use `127.0.0.1:18000` — that reverse tunnel is being
|
||||
retired (`CUST-WP-0067`).
|
||||
|
||||
---
|
||||
|
||||
## Workplan Convention (ADR-001)
|
||||
|
||||
Work items originate as files in this repo — not in the hub. The hub is a
|
||||
read/cache/index layer that rebuilds from files.
|
||||
|
||||
**File location:** `workplans/APPROVAL-WP-NNNN-<slug>.md`
|
||||
|
||||
**Archived location:** finished workplans may move to
|
||||
`workplans/archived/YYMMDD-APPROVAL-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||
the completion/archive date; the frontmatter `id` does not change.
|
||||
|
||||
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||
`workplans/ADHOC-YYYY-MM-DD.md`, workplan id
|
||||
`APPROVAL-WP-ADHOC-YYYY-MM-DD`, and task ids
|
||||
`APPROVAL-WP-ADHOC-YYYY-MM-DD-T01`, etc. `APPROVAL-WP` includes its final `-WP`
|
||||
token. Unqualified historic `ADHOC-*` ids are grandfathered and must not be
|
||||
copied into new records. Use this only for low-risk work completed directly;
|
||||
create a normal workplan for anything needing analysis, design, approval,
|
||||
dependencies, or multiple phases.
|
||||
|
||||
**Frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: APPROVAL-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: infotech
|
||||
repo: approval-engine
|
||||
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||
owner: codex
|
||||
topic_slug: ...
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
state_hub_workstream_id: "<uuid>" # fix-consistency — do not edit (legacy field name; workplan UUID)
|
||||
---
|
||||
```
|
||||
|
||||
Use `proposed` for a new draft, `ready` after review against current repo
|
||||
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||
derived health labels, not frontmatter statuses.
|
||||
|
||||
**Terminology:** workplan is the fleet term; `workstream` appears only in legacy
|
||||
API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see
|
||||
`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`.
|
||||
|
||||
**Task block format** (one per `##` section):
|
||||
|
||||
```
|
||||
## Task Title
|
||||
|
||||
` ` `task
|
||||
id: APPROVAL-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
` ` `
|
||||
|
||||
Task description text.
|
||||
```
|
||||
|
||||
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||
|
||||
**Residuals when finishing:** actionable leftovers become live work records
|
||||
before `status: finished` — usually an intake (`origin: residual`,
|
||||
`origin_ref: APPROVAL-WP-NNNN`) or a spawned workplan. Residual is a *role*,
|
||||
not a kind. Fleet list lives on State Hub, not in `SCOPE.md`.
|
||||
|
||||
To create a new workplan:
|
||||
1. Write the file following the format above
|
||||
2. Run `statehub fix-consistency` locally.
|
||||
3. On a non-registrar C-06/C-11 skip, use the repo-manager fallback documented
|
||||
above exactly once; never set registrar authority directly.
|
||||
169
INTENT.md
169
INTENT.md
|
|
@ -1,8 +1,25 @@
|
|||
---
|
||||
layer: Engine
|
||||
role: PIP
|
||||
standard: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
||||
companion: net-kingdom/SECURITY-COMPANION.md
|
||||
---
|
||||
|
||||
# INTENT
|
||||
|
||||
> **Layer: Engine.** NetKingdom Security Layer Model §4 catalog — a deterministic
|
||||
> API for a modeled concept, where the same authoritative input state yields the
|
||||
> same result. Standard: `net-kingdom/canon/standards/security-layer-model_v0.5.md`.
|
||||
> **Layer: Engine. Role: PIP.** NetKingdom Security Layer Model §3.3 / §4
|
||||
> catalog — a deterministic API for a modeled concept, where the same
|
||||
> authoritative input state yields the same result. This engine **supplies
|
||||
> facts a decision consumes as claims**; it never renders one. A new engine is
|
||||
> a PIP unless the statute is amended, and §6 means it can never be a second
|
||||
> PDP.
|
||||
>
|
||||
> Statute (accepted 2026-08-29):
|
||||
> `net-kingdom/canon/standards/security-layer-model_v0.7.md`.
|
||||
> Operative form: `net-kingdom/SECURITY-COMPANION.md` v0.2. The statute governs
|
||||
> on disagreement; a disagreement is a finding for `gate-house`.
|
||||
>
|
||||
> Machine-readable declaration: `layer.yaml`.
|
||||
>
|
||||
> This file captures why this repository exists and the kind of system it is
|
||||
> meant to become. It is aspirational and stable, not a description of current
|
||||
|
|
@ -21,7 +38,9 @@ It answers one question, totally and decidably:
|
|||
|
||||
Nothing else. It does not decide whether the action is permitted; that is
|
||||
`access-engine`'s and it stays the only decision point. An approval is one
|
||||
*input* to that decision.
|
||||
*input* to that decision. An outage here is **input degradation**, which is this
|
||||
engine's fallback to own (§9.3) — never a consumer-invented substitute for the
|
||||
object.
|
||||
|
||||
## Why This Exists
|
||||
|
||||
|
|
@ -41,7 +60,8 @@ evaluates is self-dealing. It is not `audit-core`'s: an approval needs mutable,
|
|||
in-path, current-state semantics, and an append-only audit fabric is built for
|
||||
the opposite property.
|
||||
|
||||
So it is a concept, and concepts get engines.
|
||||
So it is a concept, and concepts get engines. The catalog types it **PIP**
|
||||
because "we need an engine for X" must not drift into "X now decides".
|
||||
|
||||
## The Concept
|
||||
|
||||
|
|
@ -63,6 +83,12 @@ requested → approved → valid → consumed
|
|||
superseded / revoked / expired
|
||||
```
|
||||
|
||||
An approval object is **not a Railiance workload** and never becomes one
|
||||
(standard §20.1). Human commands, broker actions, and approvals that are not
|
||||
themselves deployables sit outside the `railiance-*` / `rail-*` / `rapp-*` /
|
||||
`reef-*` axes. This engine does not acquire an operational axis by being
|
||||
consumed from one.
|
||||
|
||||
## Rigidity Is the Point
|
||||
|
||||
This engine is deliberately small, boring, and strict. Its state model is a
|
||||
|
|
@ -74,6 +100,12 @@ that make Canon test `T-06 — Approval Replay` passable. An approval that is
|
|||
partly valid, or whose validity must be inferred by replaying a log, is an
|
||||
approval that can be replayed. Flexibility here is a defect.
|
||||
|
||||
**Consumption is a state change, never an inference** (standard §9.7.3). An
|
||||
approval is consumed by a mutation in this engine. It MUST NOT be inferred from
|
||||
the existence of a decision record — the decision precedes the action and the
|
||||
action precedes consumption, so a decision record proves an intent to act, not
|
||||
an act.
|
||||
|
||||
Progression modelling — graded, evidence-based, open-ended — belongs to
|
||||
`maturity-engine`. The two engines are deliberate opposites and neither should
|
||||
drift toward the other.
|
||||
|
|
@ -86,11 +118,16 @@ drift toward the other.
|
|||
- validity windows and expiry;
|
||||
- binding to action, target, actor, principal, and purpose;
|
||||
- revocation that does not require the holder's cooperation;
|
||||
- an introspection API that `access-engine` consumes as an **input claim**;
|
||||
- an introspection API that `access-engine` consumes as an **input claim**,
|
||||
carrying issuer and freshness so the PDP can state a deadline for this input
|
||||
class (§9.7.2);
|
||||
- **emission atomicity** — every issuance, use, supersession, and revocation is
|
||||
emitted to `audit-core` in the **same transaction** as the state change
|
||||
(transactional outbox or equivalent). See below; this is a contract, not an
|
||||
implementation detail.
|
||||
queued in this engine's **local** transactional store in the **same
|
||||
transaction** as the state change. See below; this is a contract, not an
|
||||
implementation detail;
|
||||
- **emission cadence** for load-bearing classes, in the form that actually
|
||||
works for rare events: positive reconciliation or a heartbeat, not rate
|
||||
monitoring.
|
||||
|
||||
## Emission Atomicity
|
||||
|
||||
|
|
@ -126,18 +163,62 @@ Consequence for design: emit-after-commit is a defect, not an optimization. A
|
|||
best-effort publish, a fire-and-forget queue, or any path where the state change
|
||||
can succeed while the event is lost is out of contract.
|
||||
|
||||
## Which threat atomicity closes — and which it does not
|
||||
|
||||
v0.6 read as though a local outbox closed §9.6's opening sentence. It does not.
|
||||
The decomposition is now in the statute and is this engine's to carry, because
|
||||
approval evidence is **load-bearing**:
|
||||
|
||||
| Threat | Covered by | When |
|
||||
| --- | --- | --- |
|
||||
| Accidental omission — crash between mutation and emit | local outbox (§9.4) | **prevented** |
|
||||
| Adversarial omission — a compromised source declines to insert, deletes before drain, or drains to nowhere | cadence and reconciliation | **detected, after the fact** |
|
||||
| Adversarial omission at a compromised source | — | **nothing in the model prevents it** |
|
||||
|
||||
The outbox sits inside this engine's blast radius. It makes emission atomic
|
||||
against crash and partial failure, and nothing more. That residual is real.
|
||||
|
||||
Approval events are **low-volume load-bearing**. Rate monitoring cannot work:
|
||||
a handful of revocations a month has no rate to drop below, and suppression is
|
||||
indistinguishable from a quiet month. The required form is **positive
|
||||
reconciliation or a heartbeat** — compare this engine's object-state transitions
|
||||
against `audit-core`'s event count per class, or assert *nothing to report* as
|
||||
a signed positive claim that can itself go missing. `GH-WP-0002-T04` is the
|
||||
reference instance. A source of load-bearing evidence **MUST** declare this
|
||||
(standard §9.6).
|
||||
|
||||
**Custody is not the residual's answer.** Whether approvals warrant archival
|
||||
custody stronger than every other source is **decided: no** (standard §16).
|
||||
Approval evidence carries the same bound as every other source. Archival
|
||||
custody does not address omission at all. If a future requirement genuinely
|
||||
needs WORM or a transparency log, that is a different store with a different
|
||||
owner, raised then. This engine must not plan around a stronger archive.
|
||||
|
||||
## The Claim Shape
|
||||
|
||||
`access-engine` consumes approvals as input claims. The claim MUST carry the
|
||||
approval identifier **and a digest over the same canonical binding the decision
|
||||
point already computes**, so that *approved* and *approved for this exact
|
||||
request* are distinguishable at decision time. Without it, wrong-action,
|
||||
wrong-target, and wrong-scope proofs have nothing to compare against.
|
||||
`access-engine` consumes approvals as input claims. The claim MUST carry:
|
||||
|
||||
- the approval identifier;
|
||||
- a digest over the same canonical binding the decision point already computes,
|
||||
so that *approved* and *approved for this exact request* are distinguishable
|
||||
at decision time;
|
||||
- **issuer** (this engine) and a **freshness** rule, so the PDP can state a
|
||||
deadline for this input class rather than a single fiction covering every
|
||||
source (§9.7.2, §17 request-claim schema).
|
||||
|
||||
Without the digest, wrong-action, wrong-target, and wrong-scope proofs have
|
||||
nothing to compare against. Without issuer and freshness, a stated deadline
|
||||
for the approval input class is unfalsifiable.
|
||||
|
||||
Until Taxonomy publishes the shared request-claim schema (§17; ownership
|
||||
proposed, not assigned), this engine publishes the contract at its own
|
||||
boundary and yields to the schema when it exists. Inventing a permanent local
|
||||
shape is the drift §17 exists to prevent.
|
||||
|
||||
## Consumption Ordering — Unresolved
|
||||
|
||||
The decision precedes the action, and the action precedes consumption. Three
|
||||
states follow that neither engine closes alone:
|
||||
failure modes are named, and each needs an owner (standard §9.7.4, §16):
|
||||
|
||||
- an allow rendered against an approval that is then never consumed;
|
||||
- an approval consumed twice by racing callers;
|
||||
|
|
@ -146,8 +227,9 @@ states follow that neither engine closes alone:
|
|||
`approval-engine` performs the mutation, because `access-engine` never mutates.
|
||||
But *who signals consumption, at what point relative to the decision, and what
|
||||
happens on each of the three states above* is a contract between the two engines
|
||||
and is **not yet settled**. It is recorded in the standard's §16 and is required
|
||||
before `FLEX-WP-0017` T05. Raised by `flex-auth`.
|
||||
and is **not yet settled**. It is recorded in the standard's §16, in
|
||||
`GH-WP-0002-T06`, and is required before `FLEX-WP-0017` T05. Raised by
|
||||
`flex-auth`.
|
||||
|
||||
Nothing here may be implemented by guessing that contract.
|
||||
|
||||
|
|
@ -160,12 +242,18 @@ Nothing here may be implemented by guessing that contract.
|
|||
versioned policy rules);
|
||||
- **identity and authentication of approvers** — `key-cape`, consumed as claims;
|
||||
- **the evidence record** — `audit-core` holds the archive; this engine holds
|
||||
only the operative current state. Note the guarantee is bounded: `audit-core`
|
||||
is not WORM or object-locked, and its tamper evidence is conditional on live
|
||||
preconditions. Whether approvals warrant stronger custody is an open gap in
|
||||
the standard's §13, and this engine must not assume it has been closed;
|
||||
only the operative current state. The guarantee is bounded: `audit-core` is
|
||||
not WORM or object-locked, and its tamper evidence is conditional on live
|
||||
preconditions. Stronger custody is decided against (§16); this engine must
|
||||
not assume it has been reopened;
|
||||
- **credentials** materialized after a decision — `secrets-engine`;
|
||||
- **maturity, readiness, or progression of anything** — `maturity-engine`.
|
||||
- **maturity, readiness, or progression of anything** — `maturity-engine`;
|
||||
- **an unreachable-engine stance map** — this engine is not PEP-shaped. It
|
||||
does not cause a protected side effect on the far side of a decision. Callers
|
||||
that do are PEP-shaped and owe companion §5 / statute §6.4;
|
||||
- **Railiance operational axes** — an approval is not a workload (§20.1). How
|
||||
those axes meet this model beyond §20's first statement is unwritten; do not
|
||||
assume a mapping.
|
||||
|
||||
The repository's seed README described this as "an engine for modelling and
|
||||
managing decisions". That wording is withdrawn: **decision** is `access-engine`'s
|
||||
|
|
@ -173,7 +261,8 @@ word under standard §8, and this engine must never claim it.
|
|||
|
||||
## Design Principles
|
||||
|
||||
1. **Binary at the point of use.** Valid or not, consumed or not. No grades.
|
||||
1. **PIP, never PDP.** Valid or not, consumed or not. No grades, and no
|
||||
"may this actor do X".
|
||||
2. **Atomic or it does not count.** Supersession and consumption are
|
||||
compare-and-swap operations, never read-then-write.
|
||||
3. **The evaluator is not the owner.** `access-engine` reads; it does not mutate.
|
||||
|
|
@ -181,11 +270,16 @@ word under standard §8, and this engine must never claim it.
|
|||
is one claim the decision point weighs.
|
||||
5. **Revocation beats cooperation.** An approval can be killed without the
|
||||
holder's participation.
|
||||
6. **Every transition is evidence, atomically.** Issuance, use, supersession,
|
||||
and revocation reach `audit-core` in the same transaction that made them
|
||||
true. An event that can be lost is a state change that never happened, as far
|
||||
as anyone auditing can tell.
|
||||
7. **Small on purpose.** Scope growth here is a security regression.
|
||||
6. **Every transition is evidence, atomically, and locally.** Issuance, use,
|
||||
supersession, and revocation are queued in this engine's own store in the
|
||||
same transaction that made them true. An event that can be lost is a state
|
||||
change that never happened, as far as anyone auditing can tell.
|
||||
7. **Atomicity prevents accidents; it does not prevent a compromised source.**
|
||||
Cadence for load-bearing classes is a heartbeat or a reconciliation, and it
|
||||
is a MUST.
|
||||
8. **Consumption is a mutation.** Inferring it from a decision record is a
|
||||
defect.
|
||||
9. **Small on purpose.** Scope growth here is a security regression.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
|
|
@ -194,6 +288,7 @@ word under standard §8, and this engine must never claim it.
|
|||
- Not an approvals *inbox* or notification surface.
|
||||
- Not an audit archive.
|
||||
- Not a general state-machine service for other concepts.
|
||||
- Not a Railiance axis, a PEP, or an actuation surface.
|
||||
|
||||
## What Would Make This Repository Wrong
|
||||
|
||||
|
|
@ -208,6 +303,15 @@ word under standard §8, and this engine must never claim it.
|
|||
- **Emission is best-effort.** If a revocation can succeed while its event is
|
||||
lost, the evidence half is silently incomplete and the most security-relevant
|
||||
transition is the one most likely to vanish.
|
||||
- **It claims the outbox closes omission.** Atomicity covers crash. Treating it
|
||||
as covering a compromised source, or skipping the heartbeat, is the v0.6
|
||||
overclaim this engine must not reintroduce.
|
||||
- **It implements consumption by guessing.** The three races are named and
|
||||
unowned. Code that picks a side is a contract with `access-engine` that
|
||||
`access-engine` has not assented to.
|
||||
- **It cites observation or containment that has not happened.** Nothing is
|
||||
observed in production, and nothing can be contained automatically
|
||||
(companion §10).
|
||||
|
||||
## Success Criteria
|
||||
|
||||
|
|
@ -221,6 +325,9 @@ word under standard §8, and this engine must never claim it.
|
|||
5. no state change can be committed without its event — demonstrated by a test
|
||||
that fails the transaction when emission fails, not by inspection, and with
|
||||
the queue local so an `audit-core` outage never blocks a revocation;
|
||||
6. the claim carries a binding digest, and a decision rendered against approval
|
||||
A for request R cannot be replayed for request R';
|
||||
7. `FLEX-WP-0017` T03 and T05 are unblocked.
|
||||
6. the claim carries a binding digest, issuer, and freshness, and a decision
|
||||
rendered against approval A for request R cannot be replayed for request R';
|
||||
7. load-bearing classes declare a heartbeat or reconciliation, and divergence
|
||||
from `audit-core`'s event count is a finding;
|
||||
8. consumption is not implemented until `GH-WP-0002-T06` settles the contract;
|
||||
9. `FLEX-WP-0017` T03 and T05 are unblocked.
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -3,8 +3,9 @@
|
|||
**The approval as a durable, authenticated, consumable object — issued before an
|
||||
action, verified at the moment of use, and provably not replayable.**
|
||||
|
||||
An Engine in the NetKingdom security layer model. It answers one question,
|
||||
totally and decidably:
|
||||
An Engine, role **PIP**, in the NetKingdom security layer model (statute v0.7,
|
||||
accepted; operative form `net-kingdom/SECURITY-COMPANION.md`). It answers one
|
||||
question, totally and decidably:
|
||||
|
||||
> Is this approval valid right now — for this exact action, target, actor, and
|
||||
> purpose — and has it already been used?
|
||||
|
|
@ -18,5 +19,6 @@ consumption are what make Canon test `T-06 — Approval Replay` passable.
|
|||
Flexibility here would be a defect. Graded, evidence-based progression belongs to
|
||||
`maturity-engine`; the two engines are deliberate opposites.
|
||||
|
||||
See [INTENT.md](INTENT.md). Origin: `flex-auth` `FLEX-DEC-2026-001`, raised while
|
||||
assenting to the security layer model.
|
||||
See [INTENT.md](INTENT.md) and [SCOPE.md](SCOPE.md). Declaration: [layer.yaml](layer.yaml).
|
||||
Origin: `flex-auth` `FLEX-DEC-2026-001`, raised while assenting to the security
|
||||
layer model.
|
||||
|
|
|
|||
151
SCOPE.md
Normal file
151
SCOPE.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# SCOPE
|
||||
|
||||
> Implemented-and-first-cut boundary for agents and contributors. Aspirational
|
||||
> direction belongs in `INTENT.md`; current work and operational gates belong in
|
||||
> `workplans/`. Derived from INTENT after alignment to the accepted NetKingdom
|
||||
> Security Layer Model v0.7 and its working companion.
|
||||
|
||||
## One-liner
|
||||
|
||||
approval-engine is the PIP for the approval object: a durable, authenticated,
|
||||
consumable artifact that `access-engine` reads as an input claim and that this
|
||||
engine alone mutates.
|
||||
|
||||
## Core Idea
|
||||
|
||||
An approval is current-state, not a log and not a decision. This repository
|
||||
owns the object, the closed state machine, the local outbox that makes every
|
||||
transition evidence, and the claim `access-engine` consumes. It does not decide
|
||||
whether the action is permitted, does not author the policy that requires an
|
||||
approval, and does not archive the trail.
|
||||
|
||||
The first cut is the spine that makes Canon `T-06 — Approval Replay` passable
|
||||
and unblocks `FLEX-WP-0017` T03/T05 — and nothing that has to guess a contract
|
||||
the other side has not assented to.
|
||||
|
||||
## In Scope
|
||||
|
||||
- Durable approval objects with stable identifiers.
|
||||
- Bindings captured at issue time: action, target, actor, principal, purpose,
|
||||
validity window, identified approvers.
|
||||
- Authenticated approval entries and distinct-approver counting.
|
||||
- The closed state machine
|
||||
`requested → approved → valid → consumed`, with
|
||||
`superseded / revoked / expired` as terminal exits from `valid`.
|
||||
- Atomic supersession and single consumption (compare-and-swap, never
|
||||
read-then-write).
|
||||
- Revocation that does not require the holder's cooperation and is effective
|
||||
at the next use.
|
||||
- A local transactional outbox in this engine's own store; no synchronous
|
||||
`audit-core` dependency inside a state-change transaction.
|
||||
- An introspection API that returns an input claim: approval identifier,
|
||||
canonical-binding digest, issuer, freshness.
|
||||
- Emission-cadence declaration for load-bearing classes, as a heartbeat or as
|
||||
reconciliation against `audit-core` event counts — not rate monitoring.
|
||||
- Machine-readable layer declaration (`layer.yaml`) as Engine / PIP, with no
|
||||
decision surface and no Tooling contact.
|
||||
- Fail-closed when this engine's own store is unavailable. An `audit-core`
|
||||
outage does not block a revocation.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Authorization decisions — `access-engine` (statute §6).
|
||||
- Approval policy: which actions require approval, how many approvers, which
|
||||
separations of duty — `gate-house` doctrine, arriving as claims or versioned
|
||||
policy rules.
|
||||
- Identity and authentication of approvers — `key-cape`.
|
||||
- The evidence archive — `audit-core`. This engine emits; it does not store
|
||||
the trail or answer "is this approval valid?" from records.
|
||||
- Stronger-than-shipped custody (WORM, object lock, transparency log) —
|
||||
decided against (statute §16).
|
||||
- Credentials materialized after a decision — `secrets-engine`.
|
||||
- Graded progression, readiness, gap register — `maturity-engine`.
|
||||
- Workflow, ticketing, inbox, notifications — the artifact, not the meeting.
|
||||
- A general state-machine service for other concepts.
|
||||
- PEP shape, unreachable-engine stance maps, Railiance `rail-*` / `rapp-*` /
|
||||
`reef-*` axes. An approval is not a workload (statute §20.1).
|
||||
- Consumption signaling relative to the decision, until `GH-WP-0002-T06`
|
||||
settles it with `access-engine`. Guessing that contract is out of scope
|
||||
even as a prototype.
|
||||
|
||||
## Relevant When
|
||||
|
||||
- A protected action exceeds normal policy and needs a durable approval
|
||||
object bound to the exact request.
|
||||
- `access-engine` needs an input claim that distinguishes *approved* from
|
||||
*approved for this exact action, target, actor, and purpose*.
|
||||
- Distinct-approver requirements must be enforced rather than conventional.
|
||||
- An approval must be revocable without the holder's cooperation.
|
||||
- Canon `T-06 — Approval Replay` is the test, or `FLEX-WP-0017` T03/T05 is
|
||||
blocked on the missing object.
|
||||
- An event about an approval (especially a revocation) must not be separable
|
||||
from the state change that made it true.
|
||||
|
||||
## Not Relevant When
|
||||
|
||||
- The question is "may this actor do X" — that is a decision.
|
||||
- The work is policy authoring, runbooks, or credential issuance.
|
||||
- The need is an audit query, a hash chain, or a WORM store.
|
||||
- The need is a workflow inbox, a meeting, or a notification surface.
|
||||
- The need is graded readiness or a maturity ladder.
|
||||
- The work is mapping Railiance operational axes onto security objects.
|
||||
- Consumption ordering is being "solved" from this side alone.
|
||||
|
||||
## Current State
|
||||
|
||||
- Status: **seed**. The repository holds `INTENT.md`, this file, `layer.yaml`,
|
||||
and a README. There is no store, no API, no outbox, no tests, no runtime.
|
||||
- Layer declaration exists in this engine's own voice (INTENT frontmatter +
|
||||
`layer.yaml`). Conformance checks against Tooling contact are vacuously
|
||||
clean: there is no code to contact anything.
|
||||
- Consumption ordering remains unresolved estate-wide (`GH-WP-0002-T06`).
|
||||
- Taxonomy request-claim schema is proposed, not assigned (statute §17).
|
||||
- Work: `APPROVAL-WP-0001`.
|
||||
|
||||
## How It Fits
|
||||
|
||||
```text
|
||||
gate-house designs the approval contract (Staff, doctrine)
|
||||
key-cape authenticates approvers (Tooling → claims)
|
||||
approval-engine owns and mutates the object (Engine, PIP) ← this repo
|
||||
access-engine consumes the claim; never mutates (Engine, PDP)
|
||||
audit-core archives what this engine emitted (Engine, Evidence)
|
||||
secrets-engine materializes credentials after a decision (Engine, Lifecycle)
|
||||
```
|
||||
|
||||
Staff never holds this object. `access-engine` never owns it. `audit-core`
|
||||
never answers whether it is still valid. Railiance consumes it as a claim;
|
||||
it does not become a Railiance axis.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Meaning here |
|
||||
| --- | --- |
|
||||
| **Approval** | The durable object this engine owns. Not a decision, not a workflow item. |
|
||||
| **Claim** | What `access-engine` reads. Identifier + binding digest + issuer + freshness. |
|
||||
| **PIP** | Policy Information Point. Supplies facts; does not decide. |
|
||||
| **PDP** | Policy Decision Point. `access-engine` only. |
|
||||
| **Consumption** | A mutation in this engine. Never inferred from a decision record. |
|
||||
| **Load-bearing** | A control's soundness depends on the event being present. Revocations are this. |
|
||||
| **Local outbox** | Durable queue in this engine's own transactional store. |
|
||||
| **Heartbeat** | Signed *nothing to report* for a quiet load-bearing class. |
|
||||
|
||||
## Related / Overlapping
|
||||
|
||||
| Other | Relation | Do not confuse with |
|
||||
| --- | --- | --- |
|
||||
| `access-engine` / `flex-auth` | Consumer of claims; owner of the decision. `FLEX-WP-0017` T03/T05 wait on this object. | This engine validating "may this actor do X". |
|
||||
| `gate-house` | Doctrine; designs the contract. `GH-WP-0002` is the evidence-integrity work this spine must satisfy. | This engine authoring who may approve what. |
|
||||
| `audit-core` | Evidence destination. `AUDIT-IN-0001` conditions the outbox. | This engine exposing an approval-validity query from records. |
|
||||
| `maturity-engine` | Deliberate opposite: open graded ladder vs closed binary machine. | Levels as a substitute for valid/consumed. |
|
||||
| `secrets-engine` | Downstream of a decision. | Approvals as credentials. |
|
||||
| State Hub `/decisions/{uuid}` | Not this object. Free-form `decided_by` is what `FLEX-WP-0017` T03 rejected as a substitute. | Storing approvals in the hub. |
|
||||
|
||||
## Provided Capabilities
|
||||
|
||||
None. The spine described above is INTENT and first-cut SCOPE, not a shipped
|
||||
surface. No store, no API, no claim, no outbox.
|
||||
|
||||
When the first surface ships, this section gains `capability` blocks for the
|
||||
introspection API, the mutation API, and the outbox. Until then, declaring
|
||||
capabilities would advertise a PIP that does not exist.
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
# Security layer model v0.7 — approval-engine scope vs intent review
|
||||
|
||||
**Date:** 2026-08-29
|
||||
**Statute:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted)
|
||||
**Companion:** `net-kingdom/SECURITY-COMPANION.md` v0.2
|
||||
**Reviewed against:** this repository's v0.5-aligned `INTENT.md` (commits
|
||||
`0c481de`, `35fee0e`, `ab5d69e`), the empty implementation, and the first-cut
|
||||
`SCOPE.md` written from the aligned intent
|
||||
**Outcome:** INTENT aligned to v0.7; SCOPE created as a first-cut boundary;
|
||||
implementation is seed-only. The work to evolve the repo is
|
||||
`APPROVAL-WP-0001`.
|
||||
|
||||
Hub MCP tools were not exposed in this session. Orientation and the inbox
|
||||
read used the State Hub HTTP API at `http://127.0.0.1:8000`. The gate-house
|
||||
broadcast `ACCEPTED: security layer model v0.7 — and start here:
|
||||
net-kingdom/SECURITY-COMPANION.md` is the trigger.
|
||||
|
||||
---
|
||||
|
||||
## What v0.5 already had right
|
||||
|
||||
The seed INTENT, written against v0.5, already carried the properties that
|
||||
make this engine an engine:
|
||||
|
||||
- the object, not the decision;
|
||||
- atomic supersession and single consumption;
|
||||
- local transactional outbox, no synchronous `audit-core` in the mutation
|
||||
path;
|
||||
- claim shape with identifier and canonical-binding digest;
|
||||
- consumption ordering recorded as unresolved rather than guessed;
|
||||
- the "what would make this repository wrong" list.
|
||||
|
||||
Those survive. v0.7 does not reopen them. It types them, bounds them, and
|
||||
adds obligations the v0.5 text did not know it owed.
|
||||
|
||||
## What v0.6/v0.7 changed that this engine must carry
|
||||
|
||||
| Change | Where | Effect on this repo |
|
||||
| --- | --- | --- |
|
||||
| Engines are typed. This one is **PIP**. | §3.3, §4 | INTENT frontmatter `layer: Engine` / `role: PIP`. A new engine is a PIP unless the statute is amended; it can never be a second PDP. |
|
||||
| Machine-readable declaration | §11, companion §2 | `layer.yaml` in this engine's own voice. A layer stated *about* this repo is not a declaration. |
|
||||
| Statute accepted; companion is the operative form | §14, companion | Cite v0.7 as accepted. Disagreement with the companion is a finding, not a workaround. |
|
||||
| Threat decomposition on §9.6 | §9.6, v0.7 change 5 | Local outbox **prevents accidental omission**. It does **not** prevent a compromised source. Cadence/reconciliation **detects** the adversarial case after the fact. The residual is stated. |
|
||||
| Cadence is MUST for load-bearing, and rate monitoring is the wrong form | §9.6 | Revocations are rare. Required form: heartbeat or positive reconciliation against `audit-core` (`GH-WP-0002-T04`). |
|
||||
| Stronger custody decided: no | §13, §16 | v0.5 INTENT treated this as an open gap this engine must not assume closed. v0.7 closes it: same bound as every other source. Archival custody does not address omission. |
|
||||
| Time in the model | §9.7 | Every allow has a lifetime (the object's validity window). Consumption is a mutation, never an inference from a decision record. Three races named, each needing an owner. |
|
||||
| Claim schema includes issuer and freshness | §17, §9.7.2 | The v0.5 claim (id + binding digest) is necessary and not sufficient. PDP deadlines are per input class; this PIP must publish freshness. |
|
||||
| Approval objects are not Railiance workloads | §20.1 | Do not grow a `rail-*` / `rapp-*` mapping. How the axes meet the model is unwritten. |
|
||||
| Observation and actuation at zero | companion §10, §9.2, §12 | Do not cite "observed in operation". Do not plan on automatic containment. |
|
||||
| Evidence outage rule is a trade, not a property | §3.3 | `audit-core` MUST NOT block the recorded operation *by default*. Independent-recording-before-effect remains raisable. This engine's local-outbox rule is how availability and atomicity coexist. |
|
||||
|
||||
## Scope vs intent
|
||||
|
||||
`INTENT.md` is the kind of system this repository is meant to become.
|
||||
`SCOPE.md` is the first-cut boundary: what the first workplan is allowed to
|
||||
build, and what it is forbidden to guess.
|
||||
|
||||
There is no contradiction of ownership. Every in-scope item is an INTENT
|
||||
item. The gaps are maturity, missing contracts, and a missing runtime.
|
||||
|
||||
### Intent scorecard
|
||||
|
||||
| Intent area | SCOPE | Implementation | Material gap |
|
||||
| --- | --- | --- | --- |
|
||||
| PIP, never PDP | In scope as a prohibition | No endpoints exist, so no decision surface exists either | Vacuous. The failure mode is the first endpoint that answers "may". Guard with `layer.yaml` `decision_surfaces_exposed: none` and a test once code exists. |
|
||||
| Durable object + closed state machine | In scope | None | Entire store and machine. First implementation slice. |
|
||||
| Atomic supersession and single consumption | In scope as CAS | None | Must be in the first mutation API. Read-then-write is a defect, not a stepping stone. |
|
||||
| Authenticated entries, distinct-approver counting | In scope | None | Depends on `key-cape` claims for identity; this engine counts and binds, it does not authenticate. |
|
||||
| Validity window / expiry | In scope | None | The object's lifetime is how §9.7.1 lands here. |
|
||||
| Revocation without holder cooperation | In scope | None | Load-bearing event. Must hit the local outbox in the same transaction. |
|
||||
| Claim: id + binding digest | In scope | None | Needed for `FLEX-WP-0017` T05. |
|
||||
| Claim: issuer + freshness | In scope | None | v0.7 addition. Until Taxonomy's request-claim schema exists, publish a local contract and yield later. |
|
||||
| Local outbox, no sync `audit-core` in the mutation txn | In scope | None | Prose contract in INTENT; wire contract not written (`GH-WP-0002-T02`). |
|
||||
| Fail-the-transaction-if-emission-fails test | Success criterion; in scope | None | Demonstrable, not inspectable. |
|
||||
| Cadence / heartbeat / reconciliation | In scope as declaration + design | None | MUST for load-bearing. `GH-WP-0002-T04` is the detection surface; this engine owes the source side. |
|
||||
| Consumption ordering | **Explicitly out of first-cut** | Must stay unimplemented | Unresolved in §16 / `GH-WP-0002-T06`. Implementing it is the failure INTENT names. |
|
||||
| Stronger custody | Out of scope (decided no) | n/a | Closed. Do not reopen in this repo. |
|
||||
| Policy, identity, archive, maturity, inbox | Out of scope | n/a | Unchanged from v0.5. |
|
||||
| PEP stance / Railiance axes | Out of scope | n/a | v0.7 clarification. |
|
||||
| Canon `T-06` | Success criterion | Unrunnable | No implementation to point the test at. |
|
||||
| `FLEX-WP-0017` T03 / T05 | Success criterion | Blocked | T03 waits on the object; T05 waits on T03 and on consumption ordering. |
|
||||
|
||||
### Where SCOPE is narrower than INTENT on purpose
|
||||
|
||||
1. **Consumption signaling.** INTENT owns the mutation. SCOPE forbids
|
||||
implementing *when* it fires until the other engine assents. This is not
|
||||
a missing SCOPE item; it is a named wait.
|
||||
2. **Shipped capabilities.** INTENT describes a live PIP. SCOPE currently
|
||||
provides **none**. Advertising a capability block would claim a surface
|
||||
that does not exist.
|
||||
3. **Taxonomy claim schema.** INTENT will yield to it. SCOPE publishes a
|
||||
local contract in the meantime rather than waiting on an unassigned
|
||||
owner.
|
||||
|
||||
### Where SCOPE would be wrong if it grew
|
||||
|
||||
- A "may this actor do X" helper for local tests.
|
||||
- Storing approvals in the State Hub as a substitute object
|
||||
(`FLEX-WP-0017` T03 already rejected that shape).
|
||||
- Emitting synchronously to `audit-core` "for now" to skip the outbox.
|
||||
- Inferring consumption from a decision record, because it is easier than
|
||||
a mutation.
|
||||
- A workflow inbox, because operators will ask for one.
|
||||
- A Railiance mapping, because §20 looks adjacent.
|
||||
|
||||
## Implementation necessities
|
||||
|
||||
The repository at review time contains documentation and git history. There
|
||||
is no language runtime, schema, store, or test. Everything in SCOPE is
|
||||
therefore a necessity, sequenced so that contracts precede code, and so that
|
||||
the one unowned contract is not filled in from this side.
|
||||
|
||||
### P0 — must exist before any mutation API is reachable
|
||||
|
||||
1. **Claim contract** — identifier, canonical-binding digest, issuer,
|
||||
freshness. `access-engine` cannot consume what is not specified.
|
||||
2. **Local outbox wire contract** — event classes, at-least-once into the
|
||||
outbox, `audit-core` dedupes on event id. Boundary is in INTENT; the
|
||||
wire is `GH-WP-0002-T02`.
|
||||
3. **Keep consumption unimplemented** until `GH-WP-0002-T06`. A reachable
|
||||
`consume` that guesses the races is worse than no `consume`.
|
||||
|
||||
### P0 — first implementation slice, after the contracts
|
||||
|
||||
4. **Durable object and closed state machine** with CAS supersession and
|
||||
CAS consumption *once T06 has settled the latter*.
|
||||
5. **Authenticated entries and distinct-approver counting.**
|
||||
6. **Introspection API** that returns the claim and never a decision.
|
||||
7. **Revocation** without holder cooperation, outbox-atomic, effective at
|
||||
next use.
|
||||
8. **Fail-closed on this store's outage; never on `audit-core`'s.** Proven
|
||||
by a test that fails the transaction when emission fails, and by a test
|
||||
that revokes while the evidence engine is down.
|
||||
|
||||
### P1 — load-bearing evidence residual
|
||||
|
||||
9. **Heartbeat or reconciliation** for issuance / use / supersession /
|
||||
revocation classes. Rate monitoring is not an acceptable substitute.
|
||||
Source side of `GH-WP-0002-T04`.
|
||||
10. **Layer conformance test** once code exists: no decision surface, no
|
||||
undeclared Tooling contact, outbox remains local.
|
||||
|
||||
### P2 — yield, do not invent forever
|
||||
|
||||
11. Replace the local claim contract with Taxonomy's request-claim schema
|
||||
when that artifact exists and is assented.
|
||||
12. Do not cite production observation or automatic containment. Both are
|
||||
at zero estate-wide.
|
||||
|
||||
## Dependencies this repo does not own
|
||||
|
||||
| Work | Owner | What we owe |
|
||||
| --- | --- | --- |
|
||||
| `GH-WP-0002-T02` outbox wire | `gate-house` | Satisfy the contract; do not author doctrine |
|
||||
| `GH-WP-0002-T03` revocation failure mode | `gate-house` | Local-outbox already narrows it; ship the engine to the ruling |
|
||||
| `GH-WP-0002-T04` detection surface | `gate-house` | Heartbeat / counts from this side |
|
||||
| `GH-WP-0002-T06` consumption ordering | `gate-house` + `access-engine` | Mutation API after assent, not before |
|
||||
| `FLEX-WP-0017` T03 / T05 | `access-engine` / `flex-auth` | The object and the claim; not a hub substitute |
|
||||
| `AUDIT-IN-0001` | `audit-core` | Local outbox, no validity query from records |
|
||||
| Taxonomy request-claim schema | unassigned | Local contract, then yield |
|
||||
| Canon `T-06` | `whitehat-security` / canon | A live implementation to aim at |
|
||||
|
||||
## Conformance snapshot
|
||||
|
||||
| §11 state | Now | Notes |
|
||||
| --- | --- | --- |
|
||||
| Layer declared in own voice | yes | INTENT frontmatter + `layer.yaml` |
|
||||
| Claims inside layer permissions | yes, as prose | Reviewable, not compileable, until there is code |
|
||||
| Tooling contacts | none | Vacuously conforming. Intended non-Tooling clients listed so they cannot appear as silence. |
|
||||
| Decision surface | none | Required to stay none |
|
||||
| Blocked-clean items | none of ours | Consumption wait is an unsettled contract, not a missing engine surface |
|
||||
|
||||
## Position
|
||||
|
||||
approval-engine assents to v0.7 as the statute it already declared under at
|
||||
v0.5, typed and bounded. The PIP role is correct. The local-outbox rule is
|
||||
already in the seed INTENT. The new obligations that bite are the heartbeat
|
||||
for load-bearing classes, issuer and freshness on the claim, the explicit
|
||||
ban on inferring consumption, and the custody question being closed rather
|
||||
than held open.
|
||||
|
||||
The implementation gap is total. The dangerous gap is not the missing code;
|
||||
it is the temptation to fill `GH-WP-0002-T06` from this side so that a
|
||||
demo `consume` exists. SCOPE forbids that. `APPROVAL-WP-0001` is the work
|
||||
that remains.
|
||||
76
layer.yaml
Normal file
76
layer.yaml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# approval-engine — NetKingdom security layer declaration
|
||||
#
|
||||
# Framework: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
||||
# Companion: net-kingdom/SECURITY-COMPANION.md v0.2
|
||||
# Voice: INTENT.md (this repository's own, per §11 "who must declare")
|
||||
#
|
||||
# Reference form offered by ops-warden and adopted by audit-core and
|
||||
# kings-guard. Prose cannot distinguish a declaration from a transcribed
|
||||
# review; this file is the mechanical half.
|
||||
|
||||
schema_version: "0.1"
|
||||
framework: netkingdom-security-layer-model
|
||||
standard_version: "0.7"
|
||||
companion_version: "0.2"
|
||||
repository: approval-engine
|
||||
layer: engine
|
||||
role: pip # §3.3 engine typing; §4 catalog
|
||||
declared_by: INTENT.md
|
||||
declared_at: "2026-08-29"
|
||||
|
||||
# §4 catalog entry, transcribed so drift between the catalog and this file
|
||||
# is visible. The statute is authoritative for the row.
|
||||
catalog_entry:
|
||||
owns:
|
||||
- the approval object — durable, authenticated, consumable, atomically supersedable
|
||||
statute: "§9.4"
|
||||
|
||||
# §3.3: a PIP supplies facts a decision consumes as claims. Outage is input
|
||||
# degradation, which is this engine's fallback to own (§9.3).
|
||||
# §6: no repository other than access-engine exposes an authorization decision.
|
||||
decision_surfaces_exposed: none
|
||||
|
||||
# §9.4 — callers needing current state ask this engine. audit-core must not
|
||||
# expose an approval-validity query; this engine must not expose a decision.
|
||||
approval_validity_query: owned # current-state introspection, not a verdict on "may"
|
||||
|
||||
# §5 applies to Staff. This is an Engine. Its future transactional store is
|
||||
# its own operational store, not a §4 Tooling row (same reasoning as
|
||||
# audit-core's PostgreSQL custody).
|
||||
tooling_contacts: []
|
||||
|
||||
# §11: record non-Tooling clients so the check is total. None exist: there
|
||||
# is no runtime. Listed targets are the intended Engine APIs and the
|
||||
# uncatalogued hub write, to be filled in as code appears rather than
|
||||
# discovered later as silence.
|
||||
non_tooling_clients: []
|
||||
|
||||
intended_non_tooling_clients:
|
||||
- target: audit-core
|
||||
layer: engine
|
||||
rationale: "Evidence destination for the local outbox drain. Engine API."
|
||||
- target: access-engine
|
||||
layer: engine
|
||||
rationale: "Consumer of claims. This engine does not call it to decide."
|
||||
- target: state-hub
|
||||
layer: not-catalogued
|
||||
rationale: >-
|
||||
Progress events. Outside §5 by the v0.5 scope rule. Recorded, not
|
||||
policed, and must not become a state plane another layer reads for
|
||||
approval current-state.
|
||||
|
||||
# §9.6 — approval evidence is load-bearing. Atomicity prevents accidental
|
||||
# omission; it does not prevent a compromised source. Cadence for
|
||||
# low-volume load-bearing classes is reconciliation or a heartbeat.
|
||||
evidence:
|
||||
kind: load-bearing
|
||||
atomicity: local-outbox # required; not yet implemented
|
||||
cadence_form: heartbeat-or-reconciliation
|
||||
cadence_status: undeclared # APPROVAL-WP-0001-T04
|
||||
residual: adversarial-omission-at-compromised-source
|
||||
custody: same-bound-as-every-other-source # §16 decided: no stronger archive
|
||||
|
||||
declared_shapes:
|
||||
"5.1": []
|
||||
"5.2": []
|
||||
"5.3": []
|
||||
202
workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md
Normal file
202
workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
---
|
||||
id: APPROVAL-WP-0001
|
||||
type: workplan
|
||||
title: "v0.7 alignment and the engine spine"
|
||||
domain: infotech
|
||||
repo: approval-engine
|
||||
status: active
|
||||
owner: grok
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-29"
|
||||
updated: "2026-08-29"
|
||||
---
|
||||
|
||||
# APPROVAL-WP-0001 — v0.7 alignment and the engine spine
|
||||
|
||||
Evolve this repository from a v0.5 seed INTENT into a v0.7-aligned PIP with
|
||||
the contracts and first implementation that make Canon `T-06` passable and
|
||||
unblock `FLEX-WP-0017` T03/T05.
|
||||
|
||||
Source:
|
||||
|
||||
- `net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted)
|
||||
- `net-kingdom/SECURITY-COMPANION.md` v0.2
|
||||
- `history/2026-08-29-security-layer-model-v0.7-scope-intent-review.md`
|
||||
- `INTENT.md`, `SCOPE.md`, `layer.yaml`
|
||||
|
||||
**Rule of the workplan.** Contracts before code. Consumption is not
|
||||
implemented until `GH-WP-0002-T06` settles it. A reachable mutation that
|
||||
guesses the three races is a defect, not a prototype.
|
||||
|
||||
Peer work this does not own and must not duplicate: `GH-WP-0002` (T02 outbox
|
||||
wire, T03 revocation failure mode, T04 detection surface, T06 consumption
|
||||
ordering), `FLEX-WP-0017` T03/T05, `AUDIT-IN-0001`.
|
||||
|
||||
## T01 — Align declaration to the accepted statute
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
2026-08-29: INTENT frontmatter `layer: Engine` / `role: PIP`; aligned to
|
||||
v0.7 (PIP typing, threat decomposition, load-bearing cadence, custody
|
||||
closed, consumption as mutation, issuer/freshness, Railiance non-axis).
|
||||
SCOPE.md written as first-cut boundary with standard sections. `layer.yaml`
|
||||
declared in this repository's own voice. Review filed under `history/`.
|
||||
Registered with State Hub (`infotech`, topic custodian). Register's
|
||||
bootstrap workplan was discarded: it collided on `APPROVAL-WP-0001` and
|
||||
its three tasks are already discharged by this file, `AGENTS.md`, and
|
||||
`SCOPE.md`.
|
||||
|
||||
## T02 — Publish the approval claim contract
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T02
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Specify the input claim `access-engine` consumes: approval identifier,
|
||||
canonical-binding digest over the same binding the PDP already computes,
|
||||
issuer (this engine), and freshness. Schema and examples in-repo.
|
||||
|
||||
Until Taxonomy's request-claim schema exists (statute §17, ownership
|
||||
proposed not assigned), this is the boundary contract. Mark it as yielding
|
||||
to Taxonomy; do not treat a local shape as permanent.
|
||||
|
||||
Acceptance: a document `access-engine` can implement a consumer against
|
||||
without reading this engine's source; wrong-action / wrong-target /
|
||||
wrong-scope are distinguishable; a PDP can state a deadline for this input
|
||||
class.
|
||||
|
||||
## T03 — Specify the local transactional-outbox contract
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T03
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Event classes (issuance, use, supersession, revocation, and the heartbeat
|
||||
class T04 needs), same transaction as the object mutation, queue local to
|
||||
this engine, at-least-once into the outbox (`audit-core` dedupes on event
|
||||
id). No synchronous `audit-core` call inside the state-change transaction.
|
||||
|
||||
Coordinate with `GH-WP-0002-T02`; do not author doctrine this engine does
|
||||
not own. INTENT already states the boundary and the locality.
|
||||
|
||||
Acceptance: a wire contract another repository can review; an implementer
|
||||
cannot satisfy it by emitting synchronously to `audit-core`.
|
||||
|
||||
## T04 — Declare load-bearing cadence as heartbeat or reconciliation
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T04
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Approval evidence is load-bearing and low-volume. Rate monitoring is the
|
||||
wrong form (statute §9.6). Publish the source-side declaration: expected
|
||||
classes, the heartbeat (*nothing to report*, signed, itself able to go
|
||||
missing) and/or reconciliation of this engine's transitions against
|
||||
`audit-core` event counts per class.
|
||||
|
||||
Coordinate with `GH-WP-0002-T04`, which is the detection surface. This
|
||||
task is the source that surface reads.
|
||||
|
||||
Acceptance: `layer.yaml` `evidence.cadence_status` is no longer
|
||||
`undeclared`; a missing heartbeat or a count divergence is specified as a
|
||||
finding, not as a log line.
|
||||
|
||||
## T05 — Wait on consumption ordering; do not implement it
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T05
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Blocked on `GH-WP-0002-T06` / `access-engine`. The three races (allow never
|
||||
consumed; double consumption; consume after failed action) are named in
|
||||
statute §9.7.4 and §16. This engine performs the mutation; it does not
|
||||
choose the signal, the point relative to the decision, or the handling of
|
||||
the three states.
|
||||
|
||||
Acceptance: a written contract both engines have assented to, recorded
|
||||
here, *then* a consume path. Until then, no `consume` endpoint, no inferred
|
||||
consumption from a decision record, no demo that "just consumes on allow".
|
||||
|
||||
## T06 — Durable object, closed state machine, authenticated entries
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T06
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Depends on T02 and T03. Implement the object and the machine in SCOPE:
|
||||
identifiers; bindings (action, target, actor, principal, purpose, validity
|
||||
window, approvers); authenticated entries; distinct-approver counting;
|
||||
CAS supersession; validity/expiry; revocation without holder cooperation.
|
||||
|
||||
Do not ship a public `consume` until T05 is unblocked. Internal CAS
|
||||
consumption may exist behind an unexported seam so T08 can be tested
|
||||
without guessing the ordering contract.
|
||||
|
||||
Acceptance: state transitions are total and reconstructable; two concurrent
|
||||
supersessions cannot both succeed; distinct-approver requirements fail
|
||||
closed on duplicates; revocation is effective at the next introspection.
|
||||
|
||||
## T07 — Introspection API as input claims
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T07
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Depends on T02 and T06. An API that answers INTENT's question and returns
|
||||
the T02 claim. No endpoint answers "may this actor do X". Fail-closed when
|
||||
this engine's own store is unavailable.
|
||||
|
||||
Acceptance: `access-engine` can fetch a claim; a decision rendered against
|
||||
approval A for request R cannot be replayed for R' on the digest; tests
|
||||
forbid a decision-shaped response.
|
||||
|
||||
## T08 — Local outbox in the mutation transaction
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T08
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Depends on T03 and T06. Every issuance, use, supersession, and revocation
|
||||
inserts the outbox row in the same transaction. Drain is asynchronous.
|
||||
`audit-core` outage does not block a revocation.
|
||||
|
||||
Acceptance: a test that fails the transaction when emission fails, not an
|
||||
inspection of the code; a test that revokes while the evidence engine is
|
||||
unreachable; no path where the state change commits and the outbox row
|
||||
does not.
|
||||
|
||||
## T09 — Canon T-06 and the FLEX-WP-0017 handoff
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0001-T09
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Depends on T07, T08, and T05 (the last only for the consume-side replay
|
||||
cases). Point Canon `T-06 — Approval Replay` at a live implementation.
|
||||
Hand `access-engine` / `secrets-engine` a claim they can validate before
|
||||
privileged production actions (`FLEX-WP-0017` T03/T05).
|
||||
|
||||
Acceptance: `T-06` passes; T03 is unblocked on the object (not on a hub
|
||||
substitute); T05 remains blocked only on consumption ordering if T05 of
|
||||
this workplan is still `wait`, never on a missing object or a missing
|
||||
digest.
|
||||
Loading…
Add table
Add a link
Reference in a new issue