approval-engine/INTENT.md

190 lines
9 KiB
Markdown
Raw Normal View History

# 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.4.md`.
>
> 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
> implementation.
## Purpose
> **approval-engine owns the approval as a durable, authenticated, consumable
> object — issued before an action, verified at the moment of use, and provably
> not replayable.**
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?**
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.
## Why This Exists
The gap was raised by `flex-auth` on 2026-08-28 (`FLEX-DEC-2026-001`) while
assenting to the security layer model. Splitting policy authoring from policy
evaluation left one half of the approval contract without an owner:
- the **design** of the approval contract is `gate-house`'s (Staff — doctrine);
- the **validation** of an approval at decision time is `access-engine`'s;
- the **durable object, authenticated entries, and atomic supersession** were
nobody's.
It is not `gate-house`'s: Staff holds no state another layer depends on at
runtime (standard §3.4). It is not `access-engine`'s: it does not own the
organizational approval lifecycle, and an evaluator that also owns the object it
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.
## The Concept
An **approval** is an independent authorization artifact required for actions
that exceed normal policy. It binds, at issue time, to:
- the **action** and the **target** it authorizes;
- the **actor** who may use it, and the **principal** on whose behalf;
- the **purpose** or task it was granted for;
- a **validity window**;
- the **approvers** — identified, authenticated, and countable, so that
distinct-approver requirements are enforceable rather than aspirational.
And it carries state that is deliberately not a maturity ladder:
```text
requested → approved → valid → consumed
superseded / revoked / expired
```
## Rigidity Is the Point
This engine is deliberately small, boring, and strict. Its state model is a
closed machine rather than a graded progression, because at the moment of use
the answer must be binary, total, and reconstructable.
**Atomic supersession** and **single consumption** are the security properties
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.
Progression modelling — graded, evidence-based, open-ended — belongs to
`maturity-engine`. The two engines are deliberate opposites and neither should
drift toward the other.
## What approval-engine Owns
- the durable approval object and its identifiers;
- authenticated approval entries, and distinct-approver counting;
- the state machine above, with **atomic** supersession and consumption;
- 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**;
- **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.
## Emission Atomicity
An approval MUST NOT be issued, consumed, superseded, or revoked without the
corresponding event being durably queued in the same transaction that changed
the state. Transactional outbox, or an equivalent that cannot separate the two.
This is a condition of `audit-core`'s assent (`AUDIT-IN-0001`) and is in the
standard at §9.4. The reasoning is worth carrying here rather than by reference,
because it constrains the implementation from the first commit:
An append-only archive with a verified hash chain proves that records were not
**altered or truncated after arrival**. It cannot prove that a record was never
sent (standard §9.6). A suppressed event leaves the chain perfectly intact and
verification reports intact. `audit-core` reports what it received and will not
imply it is everything that happened — correctly, because it cannot.
For an approval object this is not a corner case. **The single most valuable
event for an adversary to suppress is a revocation**, and its absence is exactly
what chain verification cannot see. Completeness is therefore this engine's
obligation and nothing downstream can retrofit it.
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.
## What approval-engine Does Not Own
- **the decision** — `access-engine`, always and only (standard §6);
- **approval policy** — which actions require approval, how many approvers, and
which separations of duty apply: that is doctrine, and doctrine is
`gate-house`'s (standard §6.2, reaching decisions as input claims or
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;
- **credentials** materialized after a decision — `secrets-engine`;
- **maturity, readiness, or progression of anything** — `maturity-engine`.
The repository's seed README described this as "an engine for modelling and
managing decisions". That wording is withdrawn: **decision** is `access-engine`'s
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.
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.
4. **Approvals are inputs, never authority.** Holding one is not permission; 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.
## Non-Goals
- Not a policy decision point, and never one.
- Not a workflow or ticketing system; it stores the artifact, not the meeting.
- Not an approvals *inbox* or notification surface.
- Not an audit archive.
- Not a general state-machine service for other concepts.
## What Would Make This Repository Wrong
- **It starts deciding.** Any endpoint that answers "may this actor do X" rather
than "is this approval valid" is the failure standard §6 names.
- **Supersession is not atomic.** If two supersessions can interleave, the
replay protection is theatre.
- **It grows a policy layer.** If it starts holding rules about who may approve
what, it has taken doctrine from `gate-house`.
- **Nobody consumes it.** If `access-engine` does not read approvals as input
claims, the object is decorative.
- **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.
## Success Criteria
1. `access-engine` renders approval-bound decisions using this engine's claims,
with the approval identifier reconstructable from the decision record;
2. Canon test `T-06 — Approval Replay` passes against a live implementation;
3. an approval can be revoked without the holder's cooperation, and the
revocation is effective at the next use;
4. distinct-approver requirements are enforced by the engine rather than by
convention;
5. no state change can be committed without its event — demonstrated by a test
that fails the transaction when emission fails, not by inspection;
6. `FLEX-WP-0017` T03 and T05 are unblocked.