| Exposed class | Authoritative provider write | Consumer state that must change | Reload/roll | Predecessor-denial proof |
| --- | --- | --- | --- | --- |
| KeyCape RS256 signing key | KeyCape/railiance-platform guarded bundle apply using the KeyCape-approved signing strategy | `sso/keycape-config``key.pem`; downstream JWKS caches | KeyCape and every declared downstream verifier | KeyCape-owned controlled old JWT rejection after explicit invalidation or overlap expiry |
| LLDAP bind/admin credential | Persistent LLDAP `admin` account through a reviewed official password-change path | `sso/lldap-secrets`; Authelia's `ldap_password`; KeyCape `lldap.bindPW`; identity-provisioner `LLDAP_ADMIN_PASSWORD`; privacyIDEA resolver `lldap-coulomb``BINDPW` | Authelia, KeyCape, identity-provisioner; update/test privacyIDEA resolver; LLDAP only if its declared provider method requires it | Protected old-value LDAP bind fails; replacement lookup passes in Authelia, KeyCape, identity-provisioner, and privacyIDEA |
| Authelia KeyCape client credential | Authelia client registration hash in `sso/authelia-secrets` | KeyCape `authelia.clientSecret` in the one guarded bundle | Authelia and KeyCape | Protected predecessor produces `invalid_client`; replacement completes the authorization-code exchange |
| privacyIDEA administrative JWT | privacyIDEA `POST /auth` while predecessor remains valid | KeyCape `privacyidea.adminToken` in the one guarded bundle; any separately maintained `keycape-pi-token` must be reconciled or retired as a duplicate source | KeyCape | Replacement performs the required MFA API call; predecessor rejects only after observed expiry unless global JWT-signing-secret invalidation is separately approved |
## Exact NetKingdom-owned sequence
These steps describe the required shape; they are not a live GO.
### Prepare without provider writes
1. Pin the reviewed non-secret KeyCape client configuration and all three
repositories named by the platform contract.
2. Create a mode-`0700` private workspace with a cleanup trap. Receive each
replacement through protected input into a separate mode-`0600` file.
3. Produce the Authelia bcrypt client hash from protected stdin/file, never a
command argument. Do not record the hash as evidence.
4. Build field-specific replacements for `authelia-secrets` and
`lldap-secrets`. Preserve their unrelated fields from an owner-controlled
input, not from captured agent output and not from a stale bootstrap bundle.
5. Build exactly one complete KeyCape configuration and signing-key pair from
the pinned non-secret client source and the replacement files.
6. Prepare the privacyIDEA resolver update for `lldap-coulomb` with its complete
non-secret resolver settings and the replacement bind value. Do not run the
broad realm bootstrap, which also rewrites realms and policies.
### Bounded cutover
1. Guard all prepared writes on the approved Kubernetes UIDs/resource
versions and Git revisions.
2. Install the field-specific Authelia Secret changes without restarting it.
3. Change the persistent LLDAP `admin` password using the reviewed provider
path. Do not assume a Deployment restart applies the new value.
`lldap-coulomb`, and restart identity-provisioner. The resolver update must
be a dedicated `/resolver/lldap-coulomb` operation, not full realm repair.
5. Apply the single guarded `sso/keycape-config` bundle without rendering a
manifest, then roll Authelia and KeyCape. The Authelia Secret update and the
KeyCape bundle must contain the same LLDAP and client-credential revisions.
6. Prove replacement LLDAP access through all four consumers, replacement
Authelia client authentication, and replacement privacyIDEA access.
7. Prove old LLDAP and Authelia credentials fail using protected inputs.
Handle the old privacyIDEA JWT according to the corrected T02 decision.
After any provider write, abort is forward-only: finish or repair with the new
revision. The exposed bundle and exposed predecessors are never rollback
material.
## Existing helper assessment
| Helper | Assessment for this incident |
| --- | --- |
| `sso-mfa/bootstrap/creds-rotate.sh` | Prohibited. It prints replacements; its LLDAP branch omits identity-provisioner and privacyIDEA; its LLDAP provider change is manual; and its signing branch deliberately invalidates immediately. |
| `sso-mfa/k8s/authelia/create-secrets.sh` | Not suitable as the cutover writer. It reconstructs the whole Secret from local bootstrap files, may generate unrelated signing material, and passes plaintext/hash values in command arguments. Its field mapping is useful as design evidence only. |
| `sso-mfa/k8s/keycape/create-secrets.sh` | Not suitable. It reconstructs the full bundle from local files, can generate a new key implicitly, and its inline client list can lag the live reviewed source. |
| `sso-mfa/k8s/keycape/refresh-pi-token-live.sh` | Not suitable. It reads the exposed live KeyCape payload, rebuilds the full Secret, and has no predecessor-expiry/revocation proof. |
| `sso-mfa/k8s/privacyidea/repair-realm-live.sh` | Too broad. It updates the resolver but also reapplies realm, default-realm, and policy state. A dedicated resolver-only path is required. |
| `sso-mfa/k8s/lldap/break-glass.sh` password mutation | Not accepted as the provider path. It passes the password in process arguments and assumes a GraphQL cleartext mutation that is not the documented general LLDAP password interface. |
## Remaining gates
- KeyCape must publish and acknowledge its signing/JWKS/cache strategy.
- Railiance-platform must correct the LLDAP consumer list and the privacyIDEA
predecessor disposition in its owner contract.
- NetKingdom must implement and review provider-specific, file-input helpers
before T03 can complete. They must have dry-run/static tests proving that
values cannot reach argv or stdout.
- A named operator window, driver, abort operator, provider authority, and
protected verification method are still required before any live action.