informed-decision/docs/gate-house-decision-request-layer-placement.md

176 lines
8.5 KiB
Markdown
Raw Permalink Normal View History

Raise INFD-IN-0001: layer placement and approver-UI ownership File the gate-house decision request that gates INFD-WP-0001-T02, before any architecture is written, so the ruling constrains the design rather than being retrofitted to it. Three rulings requested: layer and role (proposed PEP-shaped, §6.4/companion §5); whether a presentation attestation also makes this a PIP or must reach consumers only through audit-core; and the relationship between view_hash and approval-engine's binding digest. The third is the highest risk and the reason this is filed first. Both digests claim to canonicalize "the binding" but cover different material — the approval digest exists without a human in the loop, view_hash covers the brief, packet, highlights, locale and UI release. Three candidate rulings are set out with what each costs; the proposal is distinct attestations with an explicit authority rule, but any of the three is implementable. The outcome to avoid is both shipping with no stated relationship. The self-dealing objection is argued against ourselves rather than left for review, and the residual is stated plainly: a compromised surface can present X and attest Y, structurally the same residual approval-engine names for adversarial omission at a compromised source. No claim is made to close it. 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
2026-09-09 14:07:13 +02:00
# Decision request to gate-house — layer placement and approver-UI ownership
**From:** informed-decision
**Raised:** 2026-09-09
**Intake:** `INFD-IN-0001`
**Blocks:** `INFD-WP-0001-T02`, and through it T05 and T07
**Standard:** `net-kingdom/canon/standards/security-layer-model_v0.7.md`,
companion v0.2
---
## 1. The gap being closed
`approval-engine` names an approvals inbox under Non-Goals, deliberately and
correctly — an engine that renders its own object is self-dealing, and the same
reasoning kept the approval object out of `access-engine`.
The consequence is that the surface a human actually touches has no owner. This
is no longer theoretical:
- `approval-engine` is a bearer-token resource server with no Ingress, no
redirect endpoint and no browser client (`deploy/approval-engine.yaml` is
Namespace + ClusterIP + StatefulSet).
- On 2026-09-08 `key-cape` (`KEY-WP-0013-T02`) asked `approval-engine` for the
human approver client's `client_id` and full callback URI.
- `approval-engine` declined to invent them and recorded in
`docs/keycape-service-registrations.md`: *"The human approver flow therefore
belongs to whichever browser-facing approver UI presents `approval:approve`
tokens to this engine. That component is not in this repo and is not named in
`APPROVAL-WP-0002`."*
- `APPROVAL-WP-0002-T01` remains `progress` in part because of it.
`informed-decision` exists to take this. **We are not asking whether the work is
ours to do. We are asking what layer the statute says we are, and asking it
before we build, so the answer constrains the architecture rather than being
retrofitted to it.**
## 2. What is being asked
Three rulings, in dependency order. R3 is the one we least want to get wrong.
### R1 — Layer and role
**Proposal:** `informed-decision` is **PEP-shaped** under statute §6.4 and owes
companion §5.
Reasoning: it is browser-facing and it causes a protected side effect on the far
side of a decision — it submits an approval entry to `approval-engine` carrying
an authenticated human's identity. `approval-engine`'s own INTENT anticipates
exactly this class of caller: *"Callers that do are PEP-shaped and owe
companion §5 / statute §6.4."*
We are **not** proposing an Engine row. We hold no state another layer reads at
runtime to reach a verdict.
### R2 — Does a presentation record make this a PIP as well?
`informed-decision` produces one fact other layers may want as a claim: **what
was presented, to whom, in what version** — carried as `view_hash`. We do not
evaluate that fact; we emit it.
**Proposal:** the presentation attestation is a PIP-like claim emitted by a
PEP-shaped component, not a second catalog row. If the statute does not permit
a component to be both, we ask which one governs and whether the presentation
claim must instead be published as evidence to `audit-core` and read from there.
We flag §17: the shared request-claim schema has **no assigned owner**. Whatever
shape we publish must yield to that schema when it exists rather than becoming a
permanent local invention — the same position `approval-engine` took in
`APPROVAL-IN-0001`.
### R3 — `view_hash` versus the approval binding digest
**This is the highest-risk item in the request and the reason it is filed before
any code.**
`approval-engine`'s claim already carries *"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."*
`informed-decision` computes `view_hash`: a SHA-256 over a canonicalized
*binding document* — the memo id and version, question, requested act, binding
level, brief, locale, UI release, normalized packet and highlights, and the
identity/scope being committed. Algorithm and vectors:
`history/20260909-initial-exploration/canonicalize.md`.
These are **not obviously the same hash**, and they are **not obviously
different**. Both claim to canonicalize "the binding". They differ in what they
cover:
| | approval-engine binding digest | informed-decision `view_hash` |
| --- | --- | --- |
| Covers action, target, actor, principal, purpose | yes | yes, as `binding` |
| Covers the brief, packet and highlights shown to the human | no | yes |
| Covers locale and UI release | no | yes |
| Exists when no human is in the loop | yes | no |
| Purpose | replay protection at decision time | proof of what was presented |
Three candidate rulings, with what each costs:
- **(a) They are the same digest.** Cleanest for the estate — one
canonicalization, one authority. Cost: the approval binding digest would have
to cover presentation material that does not exist for machine-to-machine
approvals, or `view_hash` would have to drop the packet and highlights, which
destroys the property this repository exists to provide.
- **(b) `view_hash` is a distinct presentation attestation carried alongside the
binding digest.** Our proposal. Cost: two hashes over one act, requiring an
explicit rule that the binding digest is authoritative for replay protection
and `view_hash` is authoritative only for *what was shown*. Neither may be
substituted for the other.
- **(c) `view_hash` contains the binding digest as a field.** Composition rather
than duplication; `view_hash` becomes a superset commitment. Cost: ordering
dependency — the approval must exist before a memo can be rendered.
**We propose (b) and will implement whichever is ruled.** What we ask gate-house
to prevent is the outcome where both hashes ship without a stated relationship
and the estate later discovers it has two canonicalizations of the same act with
no rule for which one governs.
## 3. The self-dealing objection, stated against ourselves
A surface that renders both the question and the answer sits close to the
objection that kept the approval object out of `access-engine` and the inbox out
of `approval-engine`. We think it is not the same failure, and we would rather
argue it here than be told later:
- `access-engine` could not own the approval object because an evaluator that
owns what it evaluates can grade its own inputs.
- `approval-engine` could not own the inbox because an engine that renders its
own object controls the presentation of the evidence it issues.
- `informed-decision` **holds no state any layer reads as authority.** It does
not evaluate, does not issue the approval, and does not archive the trail.
The presentation record is evidence *about* the act, produced by the party
that performed the presentation — which is the only party that can produce it.
The residual we accept and do not paper over: **a compromised surface can
present X and attest Y.** `view_hash` is computed by the same component that
renders. This is structurally the same residual `approval-engine` names for
adversarial omission at a compromised source, and we make the same claim it
does — atomicity and attestation cover accident and later tampering, not a
compromised source. We do not claim otherwise, and we ask that any ruling not
credit us with closing it.
## 4. What we are not asking
- Not asking for approval doctrine. Which acts need approval, how many
approvers, and which separations of duty apply stay `gate-house`'s.
- Not asking to render decisions. No endpoint here answers "may this actor do
X"; `access-engine` remains the only PDP.
- Not asking to hold approval state. `approval-engine` remains the sole
mutator, and `GH-DEC-2026-003` ordering is honoured unchanged.
- Not asking for a Railiance operational axis.
## 5. What we do on each answer
| Ruling | Consequence |
| --- | --- |
| R1 confirms PEP-shaped | `layer.yaml` written as such; companion §5 declared shapes enumerated in T05 |
| R1 places us elsewhere | `INTENT.md`, `GOAL.md`, `SCOPE.md` amended to match; the ruling wins |
| R2 permits a presentation claim | Published at our boundary, explicitly yielding to §17 |
| R2 refuses it | Presentation evidence goes to `audit-core` only; no claim endpoint |
| R3 = (a) | One digest; `view_hash` is retired or redefined as its input |
| R3 = (b) | Both ship with a stated authority rule, recorded as a decision |
| R3 = (c) | Composition; memo rendering is ordered after approval issuance |
Until R1 and R3 are answered, `INFD-WP-0001-T05` (ArchitectureBlueprint) and
T07 (the `key-cape` client registration) do not proceed. T03, T04 and T06 are
independent of the ruling and continue.
## 6. Cost of delay
`KEY-WP-0013-T02` is blocked today on two strings this repository now owns and
cannot responsibly publish until it knows what it is. Registering a redirect URI
for a component whose layer is unsettled is the drift statute §17 exists to
prevent.