Record KeyCape owner acceptance of NK-WP-0049 isolated restore (issuer side)

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4515@bnt-lap001
Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
This commit is contained in:
tegwick 2026-10-27 21:40:11 +01:00
parent 7190f5dfb7
commit fc9f09a8c2

View file

@ -0,0 +1,55 @@
# KeyCape owner acceptance: NK-WP-0049 isolated restore (issuer side)
**Date:** 2026-10-27 · **Owner:** key-cape · **Scope:** NK-WP-0049-T15 (KeyCape lane) and the issuer-side part of T18
**Estate mode:** builder (not production). Acceptance is proportional to that.
## Verdict
**Accept with named residuals** for KeyCape's issuer-side restore. This is not an
acceptance of consumer continuity (T18 remainder) or MFA (T19).
## Evidence assessed
- NetKingdom verifier output `docs/evidence/2026-10-23-nk49-t18-keycape-attempt/verifier-output.json`:
receipt sha256 `16d72f7c…1651`, verdict `verified`, no findings, `claims_not_made` lists consumer
continuity, MFA and owner acceptance.
- Native check source `net-kingdom/tools/bootstrap-recovery/isolated-keycape.py` (reviewed T38 pin `86f37add…`), read
against KeyCape source (`src/cmd/keycape/main.go`, `src/internal/server/oidc/jwks.go`,
`src/internal/server/policy/policy.go`, `src/internal/server/oidc/session.go`).
- No secret value, private receipt or live system was read for this review.
## What KeyCape considers a correct restore, and coverage
| KeyCape state | Where it lives | Covered by the run |
| --- | --- | --- |
| Signing key | `key.pem` → single JWKS key `kid=key-1` (`main.go:97`) | Yes. `key.pem` byte-equal to archive; live JWKS has exactly one RS256 key whose modulus/exponent equal the archived private key; a native exchange then verified against it. |
| Key rotation state | none: KeyCape constructs one key, no previous/retired keys | Nothing to restore. Single-key check is complete for this release. |
| Client registry | `clients:` list inside `config.yaml` | All entries byte-equal (whole file). One confidential `client_credentials` client exercised natively. |
| Client secrets | `KEYCAPE_*` env from referenced Secrets | Full `KEYCAPE_*` environment equal to archive; the tested client's secret accepted, unrelated secret and predecessor secret refused. |
| Policy / enrollment-mode store | `/var/lib/keycape-policy/` (`policy.json`, mutable via policy API) | Entire durable tree byte-equal, no extra files; service started, so `policy.Open` validated it against the restored clients. |
| Factor read token | `/etc/keycape-factor/admin-token` | Byte-equal when present in archive; its use is T19 (passed 2026-10-24). |
| Issuer / TLS trust | `issuer` in config, `/recovery-tls` | Original HTTPS issuer and pinned trust preserved; TLS reissued in isolated namespace, production cert unchanged. |
| Binary | admitted image digest | Image equals pinned digest; `/keycape` verifier hash checked. |
| Sessions, auth codes, token status | in memory (`session.go`) | Not durable by design; loss on restore is expected (users sign in again; issued tokens stay verifiable because the key is preserved). |
## Named residuals (do not block issuer-side acceptance)
1. **Only one client exercised natively.** Other registry entries (browser/PKCE
relying parties such as portal and Vergabe, other service clients) are proven
byte-preserved, not natively exercised. Authorization-code + PKCE through the
restored chain is the T18 consumer run (RPF-WP-0064 phase B / SECRETS-WP-0015-T04).
2. **Recovery point.** The restore equals the selected archive. Any client or
policy change made after that archive (registry additions, enrollment-mode
changes via the policy API) would be lost; the operator must reconcile against
the current repo-held client definitions after a real restore.
3. **Release coupling.** The single-key check (`len(keys)==1`, `kid=key-1`) is
correct only for the current release. If KeyCape adds key rotation or a
second key, the native check and this acceptance must be revisited.
4. **Receipt custody.** The receipt is held outside Git on the founder
workstation (founder decision 2026-10-23); this acceptance relies on the
verifier output's binding to its sha256.
## Not accepted here
Consumer continuity (T18 remainder), caller admission (T21), credential
dispositions (T22) and final acceptance (T03) stay with their owners.