net-kingdom/docs/nk49-phase-b-plan.md
tegwick 095019d134
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
NK-WP-0049-T43: record founder answers; owner requests sent
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 628290@bnt-lap001
Assistant-Session: 70de878b-b4e0-4110-a1ec-bbd553a5f483
2026-10-27 19:05:27 +01:00

8.5 KiB

NK49 phase B: isolated KeyCape and restored OpenBao on one internal network

NK-WP-0049-T43. Drafted 2026-10-24. Preparation only. This grants no authority, starts nothing and is not T18 acceptance. Every number marked proposed is a suggestion for the owners, not a fact.

Purpose

T18 needs a live consumer login against restored dependencies. The consumer is secrets-engine's secrets-engine-openbao workload (SECRETS-WP-0015-T04): it obtains a token from KeyCape, then logs in to OpenBao with the JWT role. Today the two recovered pieces cannot meet:

Piece State Network today
Isolated original KeyCape (NK49 T18 attempt) Verified 2026-10-23, receipt 16d72f7c…1651, then destroyed --network none; TLS served on its own loopback
Restored original OpenBao (RPF-WP-0064-T07) Verified 2026-10-24, receipt 4e048dee…579d, then destroyed --network none; listener on 127.0.0.1:8200 only

OpenBao's keycape-services JWT auth validates against https://kc.coulomb.social/jwks. A networkless OpenBao proves configuration but cannot perform the login. Phase B is the shared network that lets it.

Proposed topology

        docker network create --internal   (no gateway, no published ports)
  ┌───────────────────────────────────────────────────────────────────────┐
  │  keycape-worker   alias kc.coulomb.social   :443 TLS ─▶ issuer :8080  │
  │  (+ issuer sharing its namespace, as in the T18 attempt)              │
  │                                                                       │
  │  restored OpenBao   alias bao-restored   :8200 (unsealed, same window)│
  │                                                                       │
  │  consumer runner    (secrets-engine owned; the only client)           │
  └───────────────────────────────────────────────────────────────────────┘
        no other attachment · egress shown refused before admission

Who owns what

Step Owner Notes
Create and tear down the internal network Proposed: railiance-platform Its design stub already says phase B is "a single docker network create --internal network". One creator avoids a double teardown.
Restore and unseal original OpenBao, keep it up for the window railiance-platform Adapter runs phase A, then does not tear down when phase B is admitted in the same window (its doc). Needs a listener on the internal address, not loopback only.
Start the isolated original KeyCape on that network NetKingdom (this plan) Needs a new variant of the T18 machinery; see below.
Consumer login: KeyCape exchange, OpenBao JWT login, openbao:admin refusal, session revocation secrets-engine SECRETS-WP-0015-T04.
ACME issuance for kc.coulomb.social railiance-cluster Existing owner of the T33 reissue.
Window, abort, and every signature founder One window, several admissions.

What NetKingdom must change (verified from current source)

The T18 machinery is pinned: prepare-isolated-keycape.py lists isolated-keycape.py, isolated-keycape-session.py, run-isolated-keycape.sh, isolated-lldap.py, identity-bundle.py, verify-identity.py and others as the reviewed source closure, and the T38 review and T40 admission bind their hashes. They must stay unedited. Phase B is therefore a new module beside them, with its own review.

Facts read from the code that force changes:

  1. isolated-keycape-session.py launches the worker with --network none and --add-host kc.coulomb.social:127.0.0.1. Phase B needs the worker on the internal network with --network-alias kc.coulomb.social.
  2. isolated-keycape.py inspect_session refuses any worker whose NetworkMode is not none, and checks that dependent containers have container:<worker>. A phase B variant must accept exactly one named internal network and verify, by inspection, that it is Internal=true, that its attached set is exactly the admitted containers, and that the worker has a subnet route but no default route.
  3. The probes assert that only lo exists and /proc/net/route has one line. In phase B an interface and a subnet route exist by design, so "no external route" must be proved differently: no default route, Internal=true, and refused egress probes (to a public address and to a name outside the admitted aliases).
  4. verify-keycape-attempt-receipt.py expects the none topology and must have a phase B counterpart, so the receipt can still be verified independently.
  5. The T40 admission window ended 2026-10-24T05:00Z. Phase B needs a new founder signature; nothing signed before carries over.

Proposed new files (names are suggestions): isolated-keycape-phase-b.py, run-isolated-keycape-phase-b.sh, verify-keycape-phase-b-receipt.py, tests, and a packet section. Effort is moderate to large and touches the most sensitive machinery here, so it should get the same review depth as T38, not a quick patch.

Preconditions and ordering (proposed)

  1. Owners each pass their own preflight (RPF-WP-0064 adapter, the secrets-engine selector with --admission, NK49 prepare-style validation).
  2. One founder window, at most 24 h (owner admission rules). OpenBao's own deadline is 45 minutes from its first custody input, so OpenBao is started last among the long-lived pieces and the consumer run follows immediately.
  3. railiance-platform creates the network and shows egress refusal.
  4. NK49 starts the isolated KeyCape (age key, TLS key and trust in the founder's terminal, as in the T18 ceremony).
  5. railiance-platform restores and unseals OpenBao onto the same network.
  6. secrets-engine runs the consumer login.
  7. Teardown in reverse; each owner returns a safe receipt. The network creator removes the network last and shows it gone.

Evidence and claims

Returned: NK49 phase B receipt (verified independently), railiance-platform's phase B receipt, secrets-engine's consumer result. Together they would show, with no secret values: the original issuer and signing key served over TLS on the internal network; openbao:login accepted; openbao:admin refused; wrong secret refused; a session revoked; egress refused; cleanup of containers, tmpfs and network.

Not claimed: production continuity or redirect, new grants, a live OpenBao read of secret data (platform/workloads/keycape/secrets-engine-openbao stays metadata only), human login through Authelia, or owner acceptance. T18 also needs its owner acceptance.

Decisions (answered 2026-10-27; decision e4db6c9f) and remaining owner items

Founder answers: 1 railiance-platform creates the network; 2 reissue via letsencrypt-prod once railiance-cluster confirms the duplicate count (no self-signed, no retained key); 5 one workload consumer suffices, no Authelia. Item 3 (secret custody) is secrets-engine's to propose and item 4 (ISRG trust) is confirmed in the owner rehearsal. The answers authorize nothing; windows and signatures stay separate.

  1. Network creator. railiance-platform (proposed) or NetKingdom?
  2. TLS identity. The T18 attempt reissued kc.coulomb.social through letsencrypt-prod and destroyed the key. Phase B needs a certificate again: reissue (consumes the duplicate-certificate allowance, limit 5 per week; railiance-cluster should confirm the count) or retain the key from the earlier attempt (a custody decision). No self-signed substitute is allowed (decision 9f9d0d88).
  3. Consumer secret custody. The consumer's client secret keycape-secrets-engine-openbao-client is in the backup's KeyCape secrets. How does the consumer runner receive it without the agent, argv or files seeing it? This is secrets-engine's design to propose; NetKingdom must not hand it over implicitly.
  4. Trust in OpenBao. The restored OpenBao must trust the ISRG Root X1 chain to fetch /jwks. Confirm in the owner's rehearsal rather than assume.
  5. Scope. Is this one workload consumer enough for T18's "selected current consumer", or must a human-login consumer also be admitted? This plan assumes the workload consumer and does not require Authelia in phase B.

Unsigned admission

docs/nk49-phase-b-admission.example.json is a template with placeholders. It is not signed, names no real hashes for unwritten files and authorizes nothing.