Architecture Blueprint and rewritten SCOPE; close T05 and T06
T05 written after the ruling rather than before it, which was the point of gating it. GH-DEC-2026-012 limit 3 did most of the shaping: the evidence copy must reach audit-core independently of this component, because here the actor being audited and the evidence source are the same. Booked as four binding implementation consequences plus O-02, which must be resolved before T08 ships — "we will add the independent path later" is how limit 3 becomes limit-3-in-principle. Other constraints fixed in the blueprint: presentation/ is the only writer of view_hash; the approval-engine client exposes no validity cache; a fail-closed outcome is never recorded as an approver's decline, since the human made none; the assurance shape is cited from key-cape's contract rather than restated so it cannot drift; and no polling loop may synthesise the inbox approval-engine refuses to provide. T06 closed with the SCOPE.md rewrite the ruling unblocked. It carries a "What this repository does not claim" section, because a scope file listing only capabilities overstates them: the decision path is not validated while GH-DEC-2026-010 is open, the residual is not closed, view_hash is not inside the approval entry, and nothing is deployed. Two open items block the remainder. O-01, the human token tenant, blocks T07 and is not ours alone to decide. O-02, the independent evidence path, blocks T08. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3W1dQG7GFFM9d94jFx7iR Assistant: claude-code Assistant-Model: opus Assistant-Process: 1565372@bnt-lap001 Assistant-Session: 16bb2f25-b34c-49ef-8e94-5fec3567a568
This commit is contained in:
parent
be74a6bc35
commit
b8e57e8404
4 changed files with 365 additions and 41 deletions
116
SCOPE.md
116
SCOPE.md
|
|
@ -6,16 +6,15 @@
|
||||||
|
|
||||||
## Status — 2026-09-09
|
## Status — 2026-09-09
|
||||||
|
|
||||||
**Nothing is implemented.** This repository currently contains founding
|
**Specification and declaration are complete; no service is deployed.**
|
||||||
documents, the preserved founding exploration under `history/`, and
|
|
||||||
`workplans/INFD-WP-0001`. There is no package, no service, no deployment, and
|
|
||||||
no UI.
|
|
||||||
|
|
||||||
This file exists because the Repo Manager requires it and because an honest
|
What exists and is tested: the layer and stance declarations (`layer.yaml`,
|
||||||
empty boundary is more useful than an imagined one. It is rewritten in
|
`pep-stance.yaml`, `informed_decision/stance.py`), the governed canonicalizer
|
||||||
`INFD-WP-0001-T06`, once the `gate-house` layer ruling (T02) and the
|
and schema, and the four specs under `docs/specs/`. 46 tests pass.
|
||||||
specifications (T03–T05) have fixed the real boundary. **Do not read the
|
|
||||||
sections below as describing working code.**
|
What does not exist: any HTTP surface, any storage, any UI, any deployment. The
|
||||||
|
walking skeleton is `INFD-WP-0001-T08` and is gated on an external decision
|
||||||
|
(§"Open" below).
|
||||||
|
|
||||||
## One-liner
|
## One-liner
|
||||||
|
|
||||||
|
|
@ -24,30 +23,52 @@ renders a Decision Memo to the human who holds the mandate, records what was
|
||||||
shown, and binds their identity to the act — and owns the browser-facing
|
shown, and binds their identity to the act — and owns the browser-facing
|
||||||
approver UI that `approval-engine` deliberately does not contain.
|
approver UI that `approval-engine` deliberately does not contain.
|
||||||
|
|
||||||
|
## Layer
|
||||||
|
|
||||||
|
**PEP-shaped**, ruled by `GH-DEC-2026-012`. Not an Engine. Companion §5 owed,
|
||||||
|
statute §6.4 in full. Declared in `layer.yaml` in this repository's own voice.
|
||||||
|
|
||||||
|
It emits one PIP-like fact — *what was presented* — as a claim, under three
|
||||||
|
limits that are the substance of the permission rather than caveats on it:
|
||||||
|
|
||||||
|
1. the claim carries presentation and nothing else, and must never carry,
|
||||||
|
restate, summarise or imply a decision or verdict;
|
||||||
|
2. the claim must never be an input to the decision it presents for;
|
||||||
|
3. the evidence copy reaches `audit-core` **independently** of this repository.
|
||||||
|
|
||||||
|
Limit 2 is load-bearing: the argument that a renderer attesting its own
|
||||||
|
rendering is not self-dealing was accepted *because* that limit holds.
|
||||||
|
|
||||||
## Core Idea
|
## Core Idea
|
||||||
|
|
||||||
A decision surface is not a workflow engine and not a decision point. This
|
A decision surface is not a workflow engine and not a decision point. This
|
||||||
repository owns the Decision Memo object, the presentation record, the
|
repository owns the Decision Memo object, the presentation record, the
|
||||||
canonicalization that produces `view_hash` / `awareness_hash`, the disposition
|
canonicalization producing `view_hash` / `awareness_hash`, the disposition
|
||||||
vocabulary, and the evidence bundle export. It does not evaluate whether an act
|
vocabulary, and the evidence bundle export. It does not evaluate whether an act
|
||||||
is permitted, does not hold approval current-state, and does not archive the
|
is permitted, does not hold approval current-state, and does not archive the
|
||||||
trail.
|
trail.
|
||||||
|
|
||||||
## In Scope — first cut (Stage 1, not yet built)
|
## In Scope — first cut (Stage 1)
|
||||||
|
|
||||||
- The Decision Memo object and its versions, promoted from
|
**Built and tested:**
|
||||||
`history/20260909-initial-exploration/` into governed `schemas/`.
|
|
||||||
- Canonicalization of the binding and awareness documents, with the four
|
- Canonicalization of the binding and awareness documents, with the three
|
||||||
isolation vectors under test.
|
published vectors reproducing byte for byte and all four isolation properties
|
||||||
- The presentation record: what was rendered, to whom, when, in which locale
|
pinned (`tests/test_canonicalize.py`).
|
||||||
and UI release.
|
- The Decision Memo schema and worked examples, governed under `schemas/`.
|
||||||
|
- The unreachable-engine stance map, built to v0.8 obligation 3, with
|
||||||
|
published-equals-shipped asserted by test (`tests/test_layer_conformance.py`).
|
||||||
|
|
||||||
|
**Specified, not built:**
|
||||||
|
|
||||||
|
- The presentation record: what was rendered, to whom, when, in which locale and
|
||||||
|
UI release.
|
||||||
- Required-highlight acknowledgment as a precondition of binding.
|
- Required-highlight acknowledgment as a precondition of binding.
|
||||||
- The disposition vocabulary — `comment`, `discuss`, `return`, `forward`,
|
- The disposition vocabulary and its legality tables. Only `accept` reaches
|
||||||
`escalate`, `acknowledge`, `accept`, `decline`, `withdraw`, `configure` —
|
`approval-engine`; `return`, `discuss`, `escalate` and the rest are
|
||||||
and its legality tables.
|
memo-level.
|
||||||
- The browser-facing OIDC client for human principals: authorization-code +
|
- The browser-facing OIDC client: authorization-code + S256 PKCE against
|
||||||
PKCE against `key-cape`, yielding a token with `aud=approval-engine`,
|
`key-cape`, scopes `[openid, approval:read, approval:approve]`.
|
||||||
`principal_type: human`, scope `approval:approve`.
|
|
||||||
- An L3 approver surface calling `approval-engine`'s approval-entry mutation.
|
- An L3 approver surface calling `approval-engine`'s approval-entry mutation.
|
||||||
- The evidence bundle as an offline-verifiable export.
|
- The evidence bundle as an offline-verifiable export.
|
||||||
|
|
||||||
|
|
@ -55,21 +76,40 @@ trail.
|
||||||
|
|
||||||
- Authorization decisions — `access-engine`, always and only (statute §6).
|
- Authorization decisions — `access-engine`, always and only (statute §6).
|
||||||
- The approval object, its state machine, validity and consumption —
|
- The approval object, its state machine, validity and consumption —
|
||||||
`approval-engine`. This surface never caches validity, never infers
|
`approval-engine`. Never cached, never inferred, never `approval:consume`.
|
||||||
consumption, never requests `approval:consume`.
|
- Approval doctrine — `gate-house`.
|
||||||
- Approval doctrine: which acts require approval, how many approvers, which
|
- Identity and authentication — `key-cape`. Imported, never invented. The
|
||||||
separations of duty — `gate-house`.
|
`assurance` shape is `key-cape`'s and is cited, not restated.
|
||||||
- Identity and authentication — `key-cape`. Identity is imported, never
|
- The evidence archive — `audit-core`.
|
||||||
invented here.
|
|
||||||
- The evidence archive — `audit-core`. This repository emits and exports.
|
|
||||||
- Credentials materialized after a decision — `secrets-engine`.
|
- Credentials materialized after a decision — `secrets-engine`.
|
||||||
- Notification transport, ticketing, and general workflow.
|
- Notification transport, ticketing, general workflow.
|
||||||
- L4/L5, QES, QTSP integration, qualified archival retention.
|
- **An approvals inbox.** Foreclosed upstream as well as here:
|
||||||
- The mandate graph. Stage 1 routes to a named approver and does not maintain a
|
`approval-engine` exposes get-by-id only and will not add a list.
|
||||||
map of who may bind what — a known, accepted limitation recorded in `GOAL.md`.
|
- L4/L5, QES, QTSP, qualified archival retention.
|
||||||
|
- The mandate graph — so a Stage 1 `escalate` is an assertion, not a verified
|
||||||
|
claim.
|
||||||
|
|
||||||
## Layer placement
|
## What this repository does not claim
|
||||||
|
|
||||||
**Provisional and unratified.** The working position is PEP-shaped under
|
Stated here because a scope file that only lists capabilities overstates them.
|
||||||
statute §6.4 and companion §5. `layer.yaml` does not exist yet and is written
|
|
||||||
from the `gate-house` ruling in `INFD-WP-0001-T02`, not from this file.
|
- **The decision path is not validated.** `GH-DEC-2026-010` requires a decision
|
||||||
|
be attributable to `access-engine`; no consumer can satisfy that today because
|
||||||
|
the envelope is unsigned (`FLEX-WP-0024`). This surface records
|
||||||
|
`decision_attributable: false` and must not describe validation as complete.
|
||||||
|
- **The residual is open.** A compromised surface can present X and attest Y.
|
||||||
|
`GH-DEC-2026-012` states it is not closed and does not credit this repository
|
||||||
|
with closing it.
|
||||||
|
- **`view_hash` is not inside the approval entry.** `POST /entries` discards its
|
||||||
|
body by design. Correlation is `(approval_id, subject, approved_at)`, so an
|
||||||
|
auditor holding only the approval object cannot reach the presentation.
|
||||||
|
- **Nothing is deployed**, so nothing is observed in production and nothing is
|
||||||
|
contained automatically.
|
||||||
|
|
||||||
|
## Open
|
||||||
|
|
||||||
|
- **Human token tenant** — blocks `T07`. A human access token cannot carry
|
||||||
|
`tenant:platform` today. Registration-bound versus directory-sourced; not this
|
||||||
|
repository's to decide alone.
|
||||||
|
- **The independent evidence path** — mechanism unchosen; required by limit 3
|
||||||
|
before `T08` ships.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| workplan | INFD-WP-0001 | active | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| workplan | INFD-WP-0001 | active | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
| task | INFD-WP-0001-T01 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| task | INFD-WP-0001-T01 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
| task | INFD-WP-0001-T02 | progress | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| task | INFD-WP-0001-T02 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
| task | INFD-WP-0001-T03 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| task | INFD-WP-0001-T03 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
| task | INFD-WP-0001-T04 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| task | INFD-WP-0001-T04 | done | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
| task | INFD-WP-0001-T05 | todo | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
| task | INFD-WP-0001-T05 | todo | — | workplans/INFD-WP-0001-founding-specs-and-approver-ui-ownership.md |
|
||||||
|
|
|
||||||
261
docs/specs/ArchitectureBlueprint.md
Normal file
261
docs/specs/ArchitectureBlueprint.md
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
# Architecture Blueprint — Stage 1
|
||||||
|
|
||||||
|
**Repo:** informed-decision
|
||||||
|
**Workplan task:** `INFD-WP-0001-T05`
|
||||||
|
**Status:** draft, pending review
|
||||||
|
**Governing ruling:** `GH-DEC-2026-012` (answered `INFD-IN-0001`)
|
||||||
|
**Declarations:** `layer.yaml`, `pep-stance.yaml`
|
||||||
|
**Companions:** `ProductRequirementsDocument.md`, `UseCaseCatalog.md`,
|
||||||
|
`EvidenceModel.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What this component is
|
||||||
|
|
||||||
|
**PEP-shaped**, ruled by `GH-DEC-2026-012`. Browser-facing; causes a protected
|
||||||
|
side effect on the far side of a decision (an approver entry recorded against an
|
||||||
|
approval object). Companion §5 owed, statute §6.4 in full. Not an Engine: it
|
||||||
|
holds no state another layer reads at runtime for a verdict.
|
||||||
|
|
||||||
|
It also emits a **presentation claim** — one fact, *what was shown* — under the
|
||||||
|
three limits in `layer.yaml`. No second catalog row.
|
||||||
|
|
||||||
|
**Nothing in this component renders an authorization decision.** No route
|
||||||
|
answers "may this actor do X".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Call graph
|
||||||
|
|
||||||
|
```
|
||||||
|
browser (approver)
|
||||||
|
│
|
||||||
|
┌───────────┴────────────┐
|
||||||
|
│ informed-decision │ PEP-shaped
|
||||||
|
│ (this repository) │
|
||||||
|
└──┬────┬────┬────────┬──┘
|
||||||
|
auth-code + PKCE │ │ │ │ presentation records,
|
||||||
|
│ │ │ │ dispositions, stance
|
||||||
|
▼ │ │ ▼ applications
|
||||||
|
key-cape │ │ audit-core ◄──── independent
|
||||||
|
(identity)│ │ evidence path
|
||||||
|
│ │ (limit L3)
|
||||||
|
▼ ▼
|
||||||
|
access-engine approval-engine
|
||||||
|
(decision — (read + entry;
|
||||||
|
consumed, never /consume)
|
||||||
|
never made)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 `key-cape` — identity
|
||||||
|
|
||||||
|
Authorization-code + **S256 PKCE**, public client, exact registered redirect.
|
||||||
|
Access token only; `id_token` is never evidence. Scopes
|
||||||
|
`[openid, approval:read, approval:approve]`. **Never `approval:consume`.**
|
||||||
|
|
||||||
|
Token must carry `aud=approval-engine`, `principal_type: human`, non-empty `sub`
|
||||||
|
and `scope`, and an `assurance` object.
|
||||||
|
|
||||||
|
**`assurance` shape is `key-cape`'s**, adopted as published in their
|
||||||
|
`docs/approval-engine-auth-contract.md` — `level` (`aal1|aal2`), `methods`,
|
||||||
|
`mfa`, `source`, `at`. This blueprint **cites** that document rather than
|
||||||
|
restating the shape, so it cannot drift. `at` is authentication time, not token
|
||||||
|
mint time; consumers needing freshness compare `at` rather than assuming it.
|
||||||
|
|
||||||
|
**Open — blocking T07.** A human access token cannot carry `tenant:platform`
|
||||||
|
today: the tenant claim on human tokens resolves from the directory user record,
|
||||||
|
which no adapter populates, so tokens fall back to `tenant:coulomb`.
|
||||||
|
`approval-engine` compares by exact equality and refuses near-misses. Resolution
|
||||||
|
is registration-bound (preferred here) or directory-sourced; it is not this
|
||||||
|
repository's alone to decide. See §7.
|
||||||
|
|
||||||
|
### 2.2 `access-engine` — the decision, consumed
|
||||||
|
|
||||||
|
A decision is obtained **before an approval is rendered to a person**. A `200`
|
||||||
|
from `approval-engine` is not entitlement; that engine never answers "may this
|
||||||
|
actor do X".
|
||||||
|
|
||||||
|
Consuming a decision is not rendering one. There is no local fallback: if
|
||||||
|
`access-engine` is unreachable, the stance applies (§5).
|
||||||
|
|
||||||
|
**Declared gap, `GH-DEC-2026-010`.** Obligation 1 requires the decision be
|
||||||
|
*attributable* to `access-engine`. No consumer can satisfy that today — the
|
||||||
|
envelope is unsigned (`FLEX-WP-0024`). This surface records
|
||||||
|
`decision_attributable: false`. **The decision path is not validated and must
|
||||||
|
not be described as such** while that is open.
|
||||||
|
|
||||||
|
### 2.3 `approval-engine` — the object and the entry
|
||||||
|
|
||||||
|
| Route | Method | Scope |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `/v1/approvals/{id}` | GET | `approval:read` |
|
||||||
|
| `/v1/approvals/{id}/claim` | GET | `approval:read` |
|
||||||
|
| `/v1/approvals/{id}/entries` | POST | `approval:approve` |
|
||||||
|
| `/v1/approvals/{id}/consume` | POST | **never called** |
|
||||||
|
|
||||||
|
Paths containing `check` or ending in `/authorize` return 404 by design; there
|
||||||
|
is nothing there to call.
|
||||||
|
|
||||||
|
`POST /entries` **discards its request body** — approver identity, `assurance`
|
||||||
|
and `evidence_ref` come only from the verified token. So `view_hash` does not
|
||||||
|
ride into the entry. Correlation is `(approval_id, subject, approved_at)`.
|
||||||
|
|
||||||
|
**No inbox.** Get-by-id only; no list, no search, no "awaiting me". Approval ids
|
||||||
|
arrive from whatever requested the approval. This component must never poll.
|
||||||
|
|
||||||
|
Only `accept` becomes a `POST …/entries`. `return`, `discuss`, `escalate` and
|
||||||
|
the rest are memo-level and are not represented in that engine at all.
|
||||||
|
|
||||||
|
### 2.4 `audit-core` — evidence, on an independent path
|
||||||
|
|
||||||
|
**Limit L3 is the architectural constraint of this design.** The evidence copy
|
||||||
|
must reach `audit-core` independently of this repository, because here the actor
|
||||||
|
being audited and the evidence source are the same component. The copy that is
|
||||||
|
evidence must not be reachable only through the party it is evidence about.
|
||||||
|
|
||||||
|
Consequences, binding on implementation:
|
||||||
|
|
||||||
|
1. **The claim endpoint and the evidence path are separate.** Serving a
|
||||||
|
presentation record over this component's API does not discharge the evidence
|
||||||
|
obligation, and emitting evidence does not remove the need for a claim.
|
||||||
|
2. **A reader must be able to obtain the evidence copy without this component's
|
||||||
|
cooperation.** If the only route to a presentation record is an API this
|
||||||
|
component controls, an adversary who controls it controls the evidence about
|
||||||
|
it.
|
||||||
|
3. **Emission is not best-effort.** A presentation or disposition that can be
|
||||||
|
recorded here while its evidence is lost is a state change that never
|
||||||
|
happened as far as an auditor can tell. `approval-engine`'s local-outbox
|
||||||
|
reasoning applies unchanged and is adopted rather than rediscovered: the
|
||||||
|
queue is local, in the same transaction as the state change, with no
|
||||||
|
synchronous `audit-core` dependency inside it.
|
||||||
|
4. **Atomicity covers accident, not compromise.** It does not close the residual
|
||||||
|
in `layer.yaml`, and must never be described as doing so.
|
||||||
|
|
||||||
|
Open for implementation: whether the independent path is `audit-core` pull,
|
||||||
|
a separately-credentialed push, or a third party drain. **T08 must not ship
|
||||||
|
without one chosen**, because "we will add the independent path later" is how
|
||||||
|
limit 3 quietly becomes limit 3-in-principle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Internal shape
|
||||||
|
|
||||||
|
```
|
||||||
|
informed_decision/
|
||||||
|
├── canonicalize.py view_hash / awareness_hash (governed, tested)
|
||||||
|
├── stance.py the shipped unreachable-engine map (tested == published)
|
||||||
|
├── memo/ Decision Memo object, versions, lifecycle
|
||||||
|
├── presentation/ presentation records; the only writer of view_hash
|
||||||
|
├── disposition/ verb legality, guards G_*, thread
|
||||||
|
├── evidence/ local outbox, bundle export
|
||||||
|
└── clients/ key-cape, access-engine, approval-engine, audit-core
|
||||||
|
```
|
||||||
|
|
||||||
|
**`presentation/` is the only writer of `view_hash`.** One writer, one
|
||||||
|
canonicalizer, one place to audit. A second path that computes a hash is a
|
||||||
|
defect, not an optimisation.
|
||||||
|
|
||||||
|
**`clients/approval_engine` must not expose a validity cache.** No memoisation
|
||||||
|
of "is this approval valid". The engine is asked.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Storage
|
||||||
|
|
||||||
|
Stage 1 holds: memos and their versions; presentations with both hashes and
|
||||||
|
acked highlight ids; dispositions with their coded reasons; thread entries;
|
||||||
|
the local outbox; stance applications.
|
||||||
|
|
||||||
|
Properties required:
|
||||||
|
|
||||||
|
- **Presentations are append-only.** A presentation is a record of an event that
|
||||||
|
happened. Editing one is falsifying evidence.
|
||||||
|
- **Memo versions are immutable once presented.** A change creates version *n+1*
|
||||||
|
and invalidates outstanding presentations (guard `G_PRES`).
|
||||||
|
- **The outbox is in the same transactional store**, written in the same
|
||||||
|
transaction as the state change it evidences.
|
||||||
|
- **No approval state.** Not cached, not mirrored, not derived.
|
||||||
|
|
||||||
|
Engine choice is deferred to implementation; the constraint is that it be a
|
||||||
|
single transactional store, so the outbox cannot separate from the mutation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Failure modes — stance per dependency
|
||||||
|
|
||||||
|
Default is **fail closed**. Every stance in `pep-stance.yaml` is `fail_closed`,
|
||||||
|
and that is a conclusion: refusing to bind delays a decision, it does not remove
|
||||||
|
a capability anyone needs to keep a system running. There is no continuity
|
||||||
|
argument here of the kind that justifies `fail_open` elsewhere.
|
||||||
|
|
||||||
|
| Dependency | Unavailable | Surface behaviour |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `key-cape` | no authentication | No session. Nothing rendered. |
|
||||||
|
| `access-engine` | no decision | **Stance applies: `fail_closed`.** Not rendered, not bound. Recorded as a stance application, never as a decline. |
|
||||||
|
| `approval-engine` 5xx / `503` | no object, or store down | Fail closed. Never present success. |
|
||||||
|
| `audit-core` | evidence destination down | Rendering and binding continue; evidence queues in the **local** outbox. An `audit-core` outage must not block a binding act — the same reasoning that keeps it from blocking a revocation upstream. |
|
||||||
|
|
||||||
|
**A fail-closed outcome is never rendered as a decline by the approver.** No
|
||||||
|
disposition is recorded, because the human did not make one. Conflating the two
|
||||||
|
would put a refusal in the record that no person authored.
|
||||||
|
|
||||||
|
Engine response mapping — `409 duplicate_approver` is **success**, `409
|
||||||
|
conflict` is terminal, `503` is fail-closed — is specified in `PR-06` and is not
|
||||||
|
restated here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Deployment
|
||||||
|
|
||||||
|
`approval-engine` has no Ingress and no external origin, which is why it could
|
||||||
|
not own the browser client. **This component has both, and that is the point.**
|
||||||
|
|
||||||
|
- Public HTTPS origin, TLS terminated at ingress.
|
||||||
|
- Exact registered redirect URI, scheme and path included. Redirects match
|
||||||
|
exactly at `/authorize`, so a near-miss fails closed.
|
||||||
|
- **`client_id` and the callback URI are deployment inputs, not implementation
|
||||||
|
details.** Once chosen they are stable; changing one silently breaks login
|
||||||
|
rather than degrading it.
|
||||||
|
- Outbound to `approval-engine` and `access-engine` on cluster-internal
|
||||||
|
addresses; no public exposure of either is implied by this component's.
|
||||||
|
|
||||||
|
The concrete origin is fixed in `T07` alongside the client registration, and is
|
||||||
|
gated on the tenant question in §7.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Open and blocking
|
||||||
|
|
||||||
|
**O-01 — Human token tenant.** Blocks T07. Registration-bound (this
|
||||||
|
repository's stated preference, as the tenant is then a property of the surface
|
||||||
|
and its registration — which is exactly what the pre-sign binding slice commits)
|
||||||
|
versus directory-sourced (which makes tenant a property of the person and
|
||||||
|
changes it everywhere). Not ours alone; raised with `key-cape`,
|
||||||
|
`approval-engine` and `gate-house`. If registration-bound is chosen, the
|
||||||
|
condition that it holds *only* because registrations are static and
|
||||||
|
deployment-owned should be written into the contract, not left as reasoning in a
|
||||||
|
message.
|
||||||
|
|
||||||
|
**O-02 — The independent evidence path.** Its mechanism is unchosen. Must be
|
||||||
|
resolved before T08 ships.
|
||||||
|
|
||||||
|
**O-03 — Requester identity for an engine-originated memo.** `approval-engine`
|
||||||
|
does not model a requester. Likely the approval's `principal`; needs confirming
|
||||||
|
against the object.
|
||||||
|
|
||||||
|
**O-04 — `abstain`.** In the state tables and real in Mitzeichnung practice, but
|
||||||
|
with no obvious `approval-engine` counterpart. Stage 1 scope undecided.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. What would make this architecture wrong
|
||||||
|
|
||||||
|
- A route that answers whether an act is permitted.
|
||||||
|
- A validity cache for approvals.
|
||||||
|
- A second writer of `view_hash`.
|
||||||
|
- An evidence path reachable only through this component.
|
||||||
|
- `view_hash` nested inside, or recomputed from, `approval-engine`'s binding
|
||||||
|
digest — co-reference only (`GH-DEC-2026-012` R3).
|
||||||
|
- A polling loop against `approval-engine` synthesising an inbox.
|
||||||
|
- A fail-closed outcome recorded as an approver's decline.
|
||||||
|
- Describing the decision path as validated while `GH-DEC-2026-010` is open.
|
||||||
|
|
@ -206,7 +206,7 @@ scale-invariance claim is checkable rather than asserted.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: INFD-WP-0001-T05
|
id: INFD-WP-0001-T05
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "20ea118e-0657-5054-8aa2-b316444f4000"
|
state_hub_task_id: "20ea118e-0657-5054-8aa2-b316444f4000"
|
||||||
```
|
```
|
||||||
|
|
@ -233,11 +233,27 @@ token audiences, scopes and principal types match `approval-engine`'s
|
||||||
stated unavailable-stance; the blueprint names which parts are Stage 1 and which
|
stated unavailable-stance; the blueprint names which parts are Stage 1 and which
|
||||||
are placeholders.
|
are placeholders.
|
||||||
|
|
||||||
|
Completed 2026-09-09: `docs/specs/ArchitectureBlueprint.md`. Written after the
|
||||||
|
ruling, as intended — the layer answer shaped it rather than being retrofitted.
|
||||||
|
|
||||||
|
The constraint that did most of the work is `GH-DEC-2026-012` limit 3: the
|
||||||
|
evidence copy must reach `audit-core` independently of this component, because
|
||||||
|
here the actor being audited and the evidence source are the same. That is
|
||||||
|
booked as four binding implementation consequences plus an open item (`O-02`)
|
||||||
|
that must be resolved before T08 ships, rather than as a principle — "we will
|
||||||
|
add the independent path later" is how limit 3 becomes limit-3-in-principle.
|
||||||
|
|
||||||
|
Also fixed: `presentation/` is the only writer of `view_hash`; the
|
||||||
|
`approval-engine` client must expose no validity cache; a fail-closed outcome is
|
||||||
|
never recorded as an approver's decline, because the human did not make one; and
|
||||||
|
the `assurance` shape is cited from `key-cape`'s contract rather than restated,
|
||||||
|
so it cannot drift.
|
||||||
|
|
||||||
## Evidence model, schema promotion and canonicalization under test
|
## Evidence model, schema promotion and canonicalization under test
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: INFD-WP-0001-T06
|
id: INFD-WP-0001-T06
|
||||||
status: progress
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "47cb3f7a-e349-5c81-a304-86275e058a85"
|
state_hub_task_id: "47cb3f7a-e349-5c81-a304-86275e058a85"
|
||||||
```
|
```
|
||||||
|
|
@ -299,6 +315,13 @@ Two things worth recording rather than burying:
|
||||||
|
|
||||||
Remaining for `done`: rewrite `SCOPE.md` after the T02 ruling.
|
Remaining for `done`: rewrite `SCOPE.md` after the T02 ruling.
|
||||||
|
|
||||||
|
2026-09-09 — **done.** `SCOPE.md` rewritten now the ruling and the specs have
|
||||||
|
fixed the real boundary. It carries a "What this repository does not claim"
|
||||||
|
section, because a scope file listing only capabilities overstates them: the
|
||||||
|
decision path is not validated while `GH-DEC-2026-010` is open, the residual is
|
||||||
|
not closed, `view_hash` is not inside the approval entry, and nothing is
|
||||||
|
deployed.
|
||||||
|
|
||||||
## Publish the OIDC browser-client contract to key-cape
|
## Publish the OIDC browser-client contract to key-cape
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue