Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
11 KiB
Playwright registration owner contract
Source preparation under NK-WP-0050; live custody, rollout and consumer acceptance remain open. Implements the registration part of RPF-WP-0056 and CUST-WP-0080-T05. The existing browser SecurityCaseType remains unchanged: its HTTPS/standard-scope admission does not cover this loopback/custom-scope use. No case acceptance record is fabricated or borrowed from Forgejo/OpenBao.
Prepare the non-secret records
Run python3 sso-mfa/k8s/keycape/playwright-registration.py for the public
client, or add --agents <identity-references.json> for the complete proposed
Playwright agent inventory. The latter is an array with one object per existing
registered identity:
[
{
"agent_id": "test-agent",
"service_subject": "service:test-agent:platform",
"tenant": "tenant:test",
"identity_record": "synthetic/agent-record.json"
}
]
This example is synthetic; it is not an admitted consumer. The identity owner must confirm each real record, subject, tenant and agent assignment. A reference string is not evidence of registration. Initial eligibility already covers all identified platform humans and coding agents; no new membership decision is needed. The compiler does not provision identities or retrieve/mint credentials.
| Field | Public human client | Each coding agent |
|---|---|---|
| client ID | playwright-cli |
playwright-agent-<agent_id> |
| grant | authorization code, public PKCE | client credentials, confidential |
| audience | playwright-kubernetes |
playwright-kubernetes |
| scope | openid profile groups playwright:connect |
playwright:connect |
| redirect | exact http://127.0.0.1:18764/callback |
none |
| subject/tenant | existing directory user and tenant | exact registered subject/tenant supplied by owner |
| role | existing human roles | only coding-agent |
| lifetime | existing issuer setting, explicit and at most one hour | 15 minutes |
The public client has no per-client lifetime override; KeyCape supports that only for client-credentials clients. Existing issuer lifetime outside the accepted bound refuses preparation and needs owner reconciliation; this tool does not change every client's lifetime. No public tenant override or MFA opt-out is added. The callback, dedicated audience and custom scope match Platform's candidate. The resource uses access tokens; ID tokens retain the client audience.
Each agent gets a distinct proposed env binding from
keycape-<client-id>-client, key client-secret, to
KEYCAPE_<CLIENT_ID_WITH_UNDERSCORES>_CLIENT_SECRET. These names describe a
proposed delivery contract, not existing Secrets or admitted custody. Platform
must bind an exact OpenBao/ESO and consumer delivery route with its own source
task and acceptance. No existing OpenBao client or secret is reused. Secret
values must never enter this identity-reference file, Git, command arguments,
coordination messages or receipts.
Registry and activation boundary
The pure prepare_registry(secret, identities, operation) adapter reuses
keycape-client.py's byte-preserving insertion/removal. It validates the entire
inventory first, refuses differing existing records, ambiguous YAML, duplicate
IDs, shared secret references and unreviewed registrations for this dedicated
audience. An identical registration is a no-op. A separate existing OpenBao
registration for the same agent subject is preserved, with its distinct audience
and secret reference. Unrelated configuration, including signing material, is
unchanged. The encoded result contains private registry data and must stay inside
the protected activation process; the CLI never accepts or emits that registry.
This is not a live apply command. NK-WP-0050-T02 must finish the exact owner activation path using existing registry/Deployment operations:
- Bind verified identity inventory and source digests to the proposal. Obtain the governed per-agent custody and delivery references from Platform.
- Pin current cluster, issuer image and object identities. Read the registry
only within the existing protected owner execution path. Run the actual
pinned KeyCape
check-configon live and proposed configurations, with the exact Deployment env names and policy-store mode; suppress private diagnostics. - Prepare UID/resource-version/data-bound registry mutation and exact env bindings. Preserve all unrelated data and authentication methods. Retain created-only provenance, restart prerequisites, readback and guarded rollback before proposing any live action. A compare-and-swap conflict means reconcile, never replay a stale whole registry.
- Obtain the scoped live action disposition under the existing supervision rules, execute its exact revision, verify config/env/image/readiness and existing human/service login. No new runtime authority comes from this file.
operation=remove is an exact inventory removal proposal, not automatic
rollback: the activation adapter must remove only registrations it created,
preserve pre-existing identical entries and refuse intervening drift. Keep
custody material until recovery/retention requirements are met. Disabling/removing
registration stops new issuance after the issuer loads the change; issued JWTs
remain valid until expiry, and established connections need separate termination
through the transport owner. No immediate global revocation is claimed.
Verification and completion
Guarded activation patch preparation
sso-mfa/k8s/keycape/playwright-activation.py now implements the private proposal,
ordered patch preparation and readback checks for the existing protected owner
path. It has no CLI or Kubernetes connection. build takes original Secret and
Deployment API objects, the complete identity-reference inventory and the exact
reviewed source revision. Its result contains the private original registry and
must remain inside that execution path. Only summary is a public receipt.
Source revision and digest bindings do not constitute an approval or custody grant.
Inventory checks use the issuer's effective audience, including its client-ID
fallback when audience is absent/empty. A second issuer environment variable
pointing to the same proposed agent Secret/key is refused even if its name differs.
These checks do not replace Platform's wider custody/consumer isolation review.
The adapter admits one replica, Recreate, the pinned immutable KeyCape image,
the expected registry mount and explicit environment bindings. It preserves
unrelated settings and refuses differing bindings. check_registry pins the
native verifier's binary digest, checks both configurations using their exact
environment-variable names and policy-store mode, passes private input on stdin,
clears ambient environment overrides and contains diagnostics. The operator must
separately establish verifier compatibility with the deployed image; a locally
built verifier is not production artifact admission.
The source deployment cannot atomically update its registry and environment: an environment edit starts a rollout, and pending authorization codes are process-local. The prepared sequence therefore requires an explicit exclusive maintenance window with a planned authentication interruption:
stop: bind the original object UIDs, resource versions, data and complete Deployment spec; require the original single replica Ready; scale to zero.- Wait for the controller to observe that generation with zero replicas and
for every pod consuming
keycape-configto disappear. Supply an unfiltered, completessoPodList; terminating pods still count. Direct/projected volumes, env references and init/ephemeral containers are checked. registry: conditionally write the exact proposed registry data while stopped.bindings: conditionally add the agent environment bindings while still stopped.resume: after fresh matching registry/spec and quiescence checks, restore one replica. Then the protected executor must verify generation, exact pod/image, readiness, real issuance and existing human/service login. Scaling is not that acceptance proof.
Fresh reads of both objects precede every propose_step. Each patch tests
the target UID, resourceVersion and complete data/spec before replacement. Use
verify_response on the API response and subsequent fresh GET, retaining each
sanitized receipt. Refuse and stop on any mismatch or unknown outcome; no automatic
retry or stale whole-registry replay. Kubernetes offers no cross-object transaction,
so concurrent issuer writers must be excluded by the owner maintenance procedure.
Rollback uses the retained original proposal, never a newly inferred baseline:
rollback-stop, observed quiescence, rollback-registry, rollback-bindings,
rollback-resume, then the same native readiness/login verification. Exact
original/candidate matching preserves registrations and bindings that predated
this proposal. Any unrelated intervening data/spec change refuses rollback for
owner reconciliation. No completed changes means no rollback outage. Retain
private recovery state after failure; the adapter neither writes it to disk nor
destroys credentials. Token expiry and connection-termination limits above remain.
Before wiring this into live execution, T02 still requires real identity/custody
references, the protected executor and recovery-state custody, cluster/current
image pinning, the exact supervisor disposition, and source/GitOps reconciliation.
Do not invoke the generic browser helper with a fabricated --case-acceptance.
Activation tests use the independent jsonpatch Python package to apply RFC 6902
patches to synthetic API objects (test prerequisites: PyYAML and jsonpatch).
They check ordered/partial recovery, created-only preservation, stale writes,
quiescence, revision/readback refusal and verifier containment. Controller state
is simulated; neither these tests nor native check-config replace live API and
issuer acceptance.
Registration and consumer checks
Python tests exercise preservation, add/no-op/remove, lifetime bounds, identity
conflicts, distinct credentials and contained invalid-input output. The native
Go fixture tests/playwright_registration_native_test.go is copied into a
disposable committed KeyCape checkout at src/internal/authclient/ and consumes
the merged synthetic configuration through PLAYWRIGHT_SYNTHETIC_CONFIG.
It loads the config and domain registrations, performs native token/JWKS
exchanges for both client kinds, and checks dedicated claims, wrong secret,
scope, audience, signature, PKCE and callback refusals. It uses synthetic users
and seeded authorization sessions; it does not prove upstream login, production
image compatibility, native Kubernetes CEL validation or consumer eligibility.
T03 needs actual issuer issuance and native API allow/deny receipts, one real human and one real registered coding agent independently running Platform's consumer command and provider responsive/keyboard trial, renewal, revocation and active connection termination. Cluster authentication and ops-bridge transport remain their owners' implementations. Platform/provider must accept the receipts before managed discovery is published. Same-image abrupt provider recovery is already evidenced; cross-version rollback is still explicitly unproven.