2026-10-13 23:13:48 +02:00
|
|
|
# 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:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"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:
|
|
|
|
|
|
|
|
|
|
1. Bind verified identity inventory and source digests to the proposal. Obtain
|
|
|
|
|
the governed per-agent custody and delivery references from Platform.
|
|
|
|
|
2. 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-config` on live and proposed configurations, with the
|
|
|
|
|
exact Deployment env names and policy-store mode; suppress private diagnostics.
|
|
|
|
|
3. 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.
|
|
|
|
|
4. 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
|
|
|
|
|
|
2026-10-13 23:39:58 +02:00
|
|
|
### 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.
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
1. `stop`: bind the original object UIDs, resource versions, data and complete
|
|
|
|
|
Deployment spec; require the original single replica Ready; scale to zero.
|
|
|
|
|
2. Wait for the controller to observe that generation with zero replicas and
|
|
|
|
|
for **every** pod consuming `keycape-config` to disappear. Supply an unfiltered,
|
|
|
|
|
complete `sso` PodList; terminating pods still count. Direct/projected volumes,
|
|
|
|
|
env references and init/ephemeral containers are checked.
|
|
|
|
|
3. `registry`: conditionally write the exact proposed registry data while stopped.
|
|
|
|
|
4. `bindings`: conditionally add the agent environment bindings while still stopped.
|
|
|
|
|
5. `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
|
|
|
|
|
|
2026-10-13 23:13:48 +02:00
|
|
|
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.
|