Activate Playwright pilot with scoped consumer group and native evidence

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13690-09e0-7cd1-964b-ec83cdbcd348
This commit is contained in:
codex 2026-10-13 20:55:31 +02:00
parent b98d17c09f
commit 60101b0932
21 changed files with 1055 additions and 28 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ helm/*.yaml
!helm/*-probe.yaml
!helm/vergabe-demo-company-role.yaml
!helm/vergabe-demo-company-database.yaml
!helm/playwright-consumer-access.yaml
# ArgoCD repository credentials — encrypt locally, never commit
argocd/repositories/*.repository.sops.yaml

View file

@ -69,7 +69,7 @@
| task | RPF-WP-0035-T13 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md |
| task | RPF-WP-0036-T01 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T02 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T03 | todo | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T03 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T04 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T05 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md |
| task | RPF-WP-0036-T06 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md |
@ -94,12 +94,12 @@
| task | RPF-WP-0043-T05 | done | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md |
| task | RPF-WP-0044-T01 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T02 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T03 | todo | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T04 | todo | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T05 | todo | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T06 | todo | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T03 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T04 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T05 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T06 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T07 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T08 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0044-T08 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
| task | RPF-WP-0045-T01 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
| task | RPF-WP-0045-T02 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
| task | RPF-WP-0045-T03 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
@ -116,7 +116,7 @@
| task | RPF-WP-0047-T02 | done | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md |
| task | RPF-WP-0047-T03 | done | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md |
| task | RPF-WP-0048-T01 | done | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md |
| task | RPF-WP-0048-T02 | todo | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md |
| task | RPF-WP-0048-T02 | wait | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md |
| task | RPF-WP-0048-T03 | done | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md |
| task | RPF-WP-0049-T01 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md |
| task | RPF-WP-0049-T02 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md |

View file

@ -0,0 +1,54 @@
---
id: RPLAT-ADR-0006
title: "ADR-0006 — Give Playwright consumers a dedicated namespace-scoped group"
status: accepted
revision: "1.0"
owner: railiance-platform
date: "2026-10-13"
last_reviewed: "2026-10-13"
review_interval: 6m
---
## Context
rapp-playwright's published private pilot has a namespace Role but deliberately
no consumer binding. The user requested establishing a group during the Platform
open-work review. Existing cluster documentation provides administrative access;
the live binding inventory contains no non-system consumer group.
## Decision
Establish Kubernetes RBAC group subject `playwright-consumers`, bound only to
the package-owned `playwright-consumer` Role in namespace `playwright`. Platform
owns the RoleBinding; rapp-playwright retains the Role and workload source.
The initial membership policy includes all identified platform humans and coding
agents, as explicitly directed by the user. No per-use-case membership restriction
is introduced now. The identity/cluster owners must implement an authenticated
group claim for those entities before general consumer availability is accepted.
## Consequences
The Role permits pod get/list, pod port-forward creation and get of Deployment
`playwright`. It grants no Secrets, pod exec, workload writes, identity
impersonation or other namespaces. The namespace must remain dedicated to this
service because pod discovery and forwarding cover all its pods. Membership
confers browser-job submission for the reviewed trusted targets; it does not
authorize private targets, hostile pages or mutually untrusted tenant workloads.
Kubernetes has no separate Group object to create. This establishes an RBAC
subject and permissions, not a KeyCape group, login integration or membership.
Administrator impersonation can verify the permission boundary but cannot prove
consumer authentication. RPF-WP-0056 retains those outstanding acceptance steps.
Rollback removes only the Platform RoleBinding after reviewing its exact live
identity; it does not delete the Namespace, workload or artifact PVC. Existing
port-forwards require separate termination when revoking a consumer's active
access. Do not treat removal of a binding as proof that a running tunnel closed.
## Alternatives considered
Binding all authenticated users would broaden browser access beyond the human/coding-agent
identity population. Sharing the administrator kubeconfig would convey unrelated cluster
authority. Both are rejected. Selecting OIDC configuration or issuing client
certificates without an owner-supported membership route remains outside this
bounded group-binding change.

View file

@ -0,0 +1,184 @@
{
"id": "b11fd28b-58fe-4b20-a15e-32b18ea76f2b",
"api_version": "1",
"status": "passed",
"error": null,
"label": "independent-browser-trial",
"target": "fixture",
"viewports": [
{
"name": "desktop",
"width": 1280,
"height": 800
},
{
"name": "narrow",
"width": 390,
"height": 844
}
],
"timeout_ms": 60000,
"created_at": "2026-10-13T18:52:59.076Z",
"expires_at": "2026-10-13T19:52:59.076Z",
"service_version": "0.1.0",
"playwright_version": "1.64.0",
"release": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"execution_location": "railiance01/playwright",
"browser_version": "156.0.8078.4",
"completed_steps": 14,
"steps": [
{
"viewport": "desktop",
"index": 0,
"action": "visible",
"status": "passed",
"at": "2026-10-13T18:53:01.091Z",
"selector": "h1"
},
{
"viewport": "desktop",
"index": 1,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:53:01.214Z",
"selector": "#count"
},
{
"viewport": "desktop",
"index": 2,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:53:01.323Z",
"key": "Tab"
},
{
"viewport": "desktop",
"index": 3,
"action": "focused",
"status": "passed",
"at": "2026-10-13T18:53:01.360Z",
"selector": "#increment"
},
{
"viewport": "desktop",
"index": 4,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:53:01.393Z",
"key": "Enter"
},
{
"viewport": "desktop",
"index": 5,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:53:01.407Z",
"selector": "#count"
},
{
"viewport": "desktop",
"index": 6,
"action": "screenshot",
"status": "passed",
"at": "2026-10-13T18:53:01.454Z"
},
{
"viewport": "narrow",
"index": 0,
"action": "visible",
"status": "passed",
"at": "2026-10-13T18:53:02.526Z",
"selector": "h1"
},
{
"viewport": "narrow",
"index": 1,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:53:02.786Z",
"selector": "#count"
},
{
"viewport": "narrow",
"index": 2,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:53:02.877Z",
"key": "Tab"
},
{
"viewport": "narrow",
"index": 3,
"action": "focused",
"status": "passed",
"at": "2026-10-13T18:53:02.891Z",
"selector": "#increment"
},
{
"viewport": "narrow",
"index": 4,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:53:02.896Z",
"key": "Enter"
},
{
"viewport": "narrow",
"index": 5,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:53:02.905Z",
"selector": "#count"
},
{
"viewport": "narrow",
"index": 6,
"action": "screenshot",
"status": "passed",
"at": "2026-10-13T18:53:02.915Z"
}
],
"artifacts": [
{
"name": "desktop-6-keyboard-result.png",
"bytes": 26622
},
{
"name": "desktop-final.png",
"bytes": 26622
},
{
"name": "narrow-6-keyboard-result.png",
"bytes": 24280
},
{
"name": "narrow-final.png",
"bytes": 24280
}
],
"started_at": "2026-10-13T18:52:59.078Z",
"blocked_requests": 12,
"finished_at": "2026-10-13T18:53:03.351Z",
"downloads": [
{
"name": "desktop-6-keyboard-result.png",
"bytes": 26622,
"sha256": "b15998824256b86c74f2bbb75f061061be65db0b4f3bb5444529ac69a723cdad"
},
{
"name": "desktop-final.png",
"bytes": 26622,
"sha256": "b15998824256b86c74f2bbb75f061061be65db0b4f3bb5444529ac69a723cdad"
},
{
"name": "narrow-6-keyboard-result.png",
"bytes": 24280,
"sha256": "49d27cb169b8e180ce93969c6d2b8619a7684cf035bb9dcae9cd91d8bd9952e8"
},
{
"name": "narrow-final.png",
"bytes": 24280,
"sha256": "49d27cb169b8e180ce93969c6d2b8619a7684cf035bb9dcae9cd91d8bd9952e8"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,8 @@
{
"wrong_token": true,
"wrong_artifact_token": true,
"ui_failure_distinguished": true,
"bounded_cancellation": true,
"private_target_refused": true,
"preflight_healthy": true
}

View file

@ -0,0 +1,24 @@
{
"available": true,
"error": null,
"checked_at": "2026-10-13T18:49:56.214Z",
"browser_version": "156.0.8078.4",
"api_version": "1",
"service_version": "0.1.0",
"playwright_version": "1.64.0",
"execution_location": "railiance01/playwright",
"release": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"allowed_origins": [
"https://onco.cc"
],
"fixture": true,
"active": 0,
"queued": 0,
"limits": {
"concurrency": 1,
"max_queue": 4,
"max_timeout_ms": 120000,
"retention_ms": 3600000,
"max_runs": 64
}
}

View file

@ -0,0 +1,184 @@
{
"id": "6a51e164-cec5-46ed-89b1-92b93dd16d23",
"api_version": "1",
"status": "passed",
"error": null,
"label": "independent-browser-trial",
"target": "fixture",
"viewports": [
{
"name": "desktop",
"width": 1280,
"height": 800
},
{
"name": "narrow",
"width": 390,
"height": 844
}
],
"timeout_ms": 60000,
"created_at": "2026-10-13T18:50:40.554Z",
"expires_at": "2026-10-13T19:50:40.554Z",
"service_version": "0.1.0",
"playwright_version": "1.64.0",
"release": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"execution_location": "railiance01/playwright",
"browser_version": "156.0.8078.4",
"completed_steps": 14,
"steps": [
{
"viewport": "desktop",
"index": 0,
"action": "visible",
"status": "passed",
"at": "2026-10-13T18:50:42.655Z",
"selector": "h1"
},
{
"viewport": "desktop",
"index": 1,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:50:42.815Z",
"selector": "#count"
},
{
"viewport": "desktop",
"index": 2,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:50:42.851Z",
"key": "Tab"
},
{
"viewport": "desktop",
"index": 3,
"action": "focused",
"status": "passed",
"at": "2026-10-13T18:50:42.911Z",
"selector": "#increment"
},
{
"viewport": "desktop",
"index": 4,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:50:42.924Z",
"key": "Enter"
},
{
"viewport": "desktop",
"index": 5,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:50:42.937Z",
"selector": "#count"
},
{
"viewport": "desktop",
"index": 6,
"action": "screenshot",
"status": "passed",
"at": "2026-10-13T18:50:42.949Z"
},
{
"viewport": "narrow",
"index": 0,
"action": "visible",
"status": "passed",
"at": "2026-10-13T18:50:43.919Z",
"selector": "h1"
},
{
"viewport": "narrow",
"index": 1,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:50:44.063Z",
"selector": "#count"
},
{
"viewport": "narrow",
"index": 2,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:50:44.180Z",
"key": "Tab"
},
{
"viewport": "narrow",
"index": 3,
"action": "focused",
"status": "passed",
"at": "2026-10-13T18:50:44.228Z",
"selector": "#increment"
},
{
"viewport": "narrow",
"index": 4,
"action": "press",
"status": "passed",
"at": "2026-10-13T18:50:44.260Z",
"key": "Enter"
},
{
"viewport": "narrow",
"index": 5,
"action": "text",
"status": "passed",
"at": "2026-10-13T18:50:44.275Z",
"selector": "#count"
},
{
"viewport": "narrow",
"index": 6,
"action": "screenshot",
"status": "passed",
"at": "2026-10-13T18:50:44.314Z"
}
],
"artifacts": [
{
"name": "desktop-6-keyboard-result.png",
"bytes": 26622
},
{
"name": "desktop-final.png",
"bytes": 26622
},
{
"name": "narrow-6-keyboard-result.png",
"bytes": 24280
},
{
"name": "narrow-final.png",
"bytes": 24280
}
],
"started_at": "2026-10-13T18:50:40.557Z",
"blocked_requests": 12,
"finished_at": "2026-10-13T18:50:44.840Z",
"downloads": [
{
"name": "desktop-6-keyboard-result.png",
"bytes": 26622,
"sha256": "b15998824256b86c74f2bbb75f061061be65db0b4f3bb5444529ac69a723cdad"
},
{
"name": "desktop-final.png",
"bytes": 26622,
"sha256": "b15998824256b86c74f2bbb75f061061be65db0b4f3bb5444529ac69a723cdad"
},
{
"name": "narrow-6-keyboard-result.png",
"bytes": 24280,
"sha256": "49d27cb169b8e180ce93969c6d2b8619a7684cf035bb9dcae9cd91d8bd9952e8"
},
{
"name": "narrow-final.png",
"bytes": 24280,
"sha256": "49d27cb169b8e180ce93969c6d2b8619a7684cf035bb9dcae9cd91d8bd9952e8"
}
]
}

View file

@ -0,0 +1,40 @@
{
"observed_at": "2026-10-13T18:52:58.325239+00:00",
"image": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"replica_ready": true,
"pvc_phase": "Bound",
"pvc_uid": "da36929a-1d69-457a-ad99-7580ef86ab18",
"pvc_size": "2Gi",
"initial_pod_uid": "3475ad46-d9d8-4b97-99b7-54b4d984d2f2",
"restarted_pod_uid": "a22030a4-c12d-4e97-be8d-98a8d80808c2",
"same_image_restart_completed_artifacts_verified": true,
"preflight_after_restart": {
"available": true,
"error": null,
"checked_at": "2026-10-13T18:51:59.595Z",
"browser_version": "156.0.8078.4",
"api_version": "1",
"service_version": "0.1.0",
"playwright_version": "1.64.0",
"execution_location": "railiance01/playwright",
"release": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"allowed_origins": [
"https://onco.cc"
],
"fixture": true,
"active": 0,
"queued": 0,
"limits": {
"concurrency": 1,
"max_queue": 4,
"max_timeout_ms": 120000,
"retention_ms": 3600000,
"max_runs": 64
}
},
"access_path": "Existing authenticated operator SSH; administrator impersonation of playwright-consumers; loopback-only port-forward",
"actual_consumer_authentication_proven": false,
"cross_version_rollback_proven": false,
"abrupt_interruption_recovery_proven": false,
"source_workload_sha256": "d5d3f3d816dd4320059b4fcdc8409fb84d6075aeb1f409f4d112263d7e14191d"
}

View file

@ -0,0 +1,194 @@
{
"schema": "platform-playwright-rbac/v1",
"observed_at": "2026-10-13T18:49:52.369304+00:00",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"role_uid": "d24e8b51-7456-4000-8816-b4a49a06ca37",
"binding_uid": "190eb32f-8649-4b36-a525-8936cf3ceb51",
"group": "playwright-consumers",
"checks": [
{
"member": true,
"attributes": {
"verb": "get",
"resource": "pods",
"subresource": "",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": true,
"allowed": true,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "list",
"resource": "pods",
"subresource": "",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": true,
"allowed": true,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "create",
"resource": "pods",
"subresource": "portforward",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": true,
"allowed": true,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "get",
"resource": "deployments",
"subresource": "",
"group": "apps",
"namespace": "playwright",
"name": "playwright"
},
"expected": true,
"allowed": true,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "get",
"resource": "deployments",
"subresource": "",
"group": "apps",
"namespace": "playwright",
"name": "other"
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "get",
"resource": "secrets",
"subresource": "",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "create",
"resource": "pods",
"subresource": "exec",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "create",
"resource": "pods",
"subresource": "",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "patch",
"resource": "deployments",
"subresource": "",
"group": "apps",
"namespace": "playwright",
"name": "playwright"
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "create",
"resource": "pods",
"subresource": "portforward",
"group": "",
"namespace": "default",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": true,
"attributes": {
"verb": "create",
"resource": "rolebindings",
"subresource": "",
"group": "rbac.authorization.k8s.io",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": false,
"attributes": {
"verb": "list",
"resource": "pods",
"subresource": "",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
},
{
"member": false,
"attributes": {
"verb": "create",
"resource": "pods",
"subresource": "portforward",
"group": "",
"namespace": "playwright",
"name": ""
},
"expected": false,
"allowed": false,
"passed": true
}
],
"passed": true,
"actual_consumer_authentication_proven": false
}

View file

@ -0,0 +1,16 @@
{
"schema": "platform-playwright-source-review/v1",
"source_commit": "70268154c06cf78c5d9e1f4eb900213ffd436d1b",
"source_sha256": {
"declarations/rapp.yaml": "5b030686f1e3ca737805e681fc8b714e1452f879a3fa30bbac8ca30798bb3343",
"manifests/workload.yaml": "7d76ad38e25597b629660d31782867d84d7241020a2e450e346a3792ce43290c",
"releases/0.1.0.json": "364dcbc16fd50155b7d8eddb0ecb87b1709f0efdc8a41c9f15afd81e5a18506e",
"docs/platform-admission.md": "c37dc9f77d5be5bc55c873af98e8ecb0bc945ddd37ea42010d169f5d472b53c0",
"docs/operations.md": "fea5f9e25752c0199839559da5bba0ebca77be7c4906dbce4b7b6278ce5b4dab"
},
"image": "forgejo.coulomb.social/coulomb/rapp-playwright@sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441",
"provider_unit_tests_passed": 3,
"managed_runtime_accepted": false,
"group": "playwright-consumers",
"member_identities": []
}

View file

@ -0,0 +1,47 @@
# Playwright consumer access
The release 0.1.0 pilot is running in namespace `playwright` on railiance01.
Platform owns its lifecycle and the `playwright-consumers` group binding;
rapp-playwright owns the browser implementation, package Role and client.
The initial membership policy includes all identified platform humans and coding
agents. The group binding is installed and tested. Mapping authenticated platform
identities into that Kubernetes group is still open in RPF-WP-0056-T03; a KeyCape
group alone would not establish Kubernetes access. No administrator kubeconfig
is distributed and no broad `system:authenticated` binding is installed.
Once the caller has the supported group identity and API route:
```sh
kubectl -n playwright port-forward deployment/playwright 18081:8080 --address 127.0.0.1
```
In another terminal, from the existing rapp-playwright checkout:
```sh
python3 client/rapp_playwright.py preflight
python3 client/rapp_playwright.py trial --out .local/first-managed-trial
```
Use a new output directory each time. Keep `run.json` private: it contains the
run capability. Download retained receipts/screenshots before the one-hour
artifact expiry. Current targets are the bundled fixture and public OnCo home
page; additional target admission remains separate from group membership.
Platform can recheck the native permission boundary without changing grants:
```sh
python3 scripts/verify_playwright_access.py --output /tmp/playwright-rbac.json
```
The verifier uses administrator impersonation solely for authorization checks.
Its success is not consumer-login proof. It checks the cluster UID, exact Role
and group binding, allowed discovery/forwarding and denied exec, Secrets,
workload writes, foreign scope and nonmembers.
October 13 evidence: [RBAC](evidence/2026-10-13-playwright-rbac.json),
[runtime and restart](evidence/2026-10-13-playwright-managed/runtime.json),
[responsive keyboard trial](evidence/2026-10-13-playwright-managed/receipt.json).
Native operator-driven trials passed before and after a same-image restart;
completed screenshots survived with identical hashes. Actual consumer login,
abrupt-interruption recovery and cross-version rollback remain unproven.

View file

@ -0,0 +1,18 @@
# Group membership and authentication are managed by the identity/cluster owners.
# This binding does not issue credentials or make anyone a member.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: playwright-consumers
namespace: playwright
labels:
app.kubernetes.io/part-of: rapp-playwright
app.kubernetes.io/managed-by: railiance-platform
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: playwright-consumer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: playwright-consumers

View file

@ -0,0 +1,38 @@
---
id: RPF-IN-0012
kind: intake
type: intake
title: "Return independent task-execution contract and wrapped-delivery owner review"
repo: railiance-platform
status: open
owner: railiance-platform
requested_by: secrets-engine
origin: handoff
origin_ref: SECRETS-WP-0013-T01
priority: high
created: "2026-10-11"
updated: "2026-10-11"
---
Review the proposed SECRETS-EXEC-20261011-v1 metadata-only mediated execution
contract and return the exact Platform owner workplan/task binding and reviewed
revision. Identity/approval/PDP owners still owe independent returns. This intake
records no foreign owner assent and requests no credential value or native action.
D3 P3=b separately selects response-wrapped issuer delivery. The existing issuer
primitive is hardened in Secrets Engine: native KV-v2/AppRole creation path and
TTL/accessor are verified, a fresh private recipient file is exclusively reserved
and fsynced before mint, and uncertain attempts cannot silently overwrite/remint.
Disposable local OpenBao proves a first unwrap and specific consumed-token denial;
it does not prove estate custody or delivery. Review the exact actor/path/audience,
deadline, authorization/consume, destination, audit, replay/crash reconciliation and
rollback requirements for a separate delivery operation. Metadata execute-ccr
explicitly excludes wrap/KV-value delivery and cannot lend its grant to this lane.
Source: secrets-engine/docs/task-scoped-openbao-execution-contract.md and
src/secrets_engine/{openbao,wrap}.py; tests/test_wrapped_issuer_native.py is an
isolated test, not a production proof. The already D2-approved repository issuer
has its own prepared bounded command and attended Platform login requirement;
no second repository scope approval is requested and its waiting task is not
reopened by this source review. Owner-prepared execution must precede any new
attended item. Genuinely expanded scope belongs in the single daily red batch.

View file

@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Read-only native RBAC checks; impersonation is not consumer login proof."""
import argparse
from datetime import datetime, timezone
import json
from pathlib import Path
import shlex
import subprocess
CLUSTER_UID = 'a553c742-0115-43d4-99a4-a5ca56fe0786'
GROUP = 'playwright-consumers'
ROLE = 'playwright-consumer'
def verify(target):
def kubectl(args, body=None):
result = subprocess.run(
['ssh', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', target,
shlex.join(['kubectl', '--request-timeout=10s', *args])],
input=json.dumps(body) if body is not None else None,
capture_output=True, text=True, timeout=20, check=True)
return json.loads(result.stdout)
uid = kubectl(['get', 'ns', 'kube-system', '-o', 'json'])['metadata']['uid']
if uid != CLUSTER_UID:
raise RuntimeError('wrong cluster')
binding = kubectl(['get', 'rolebinding', GROUP, '-n', 'playwright', '-o', 'json'])
role = kubectl(['get', 'role', ROLE, '-n', 'playwright', '-o', 'json'])
if binding['subjects'] != [{'apiGroup': 'rbac.authorization.k8s.io',
'kind': 'Group', 'name': GROUP}]:
raise RuntimeError('consumer binding subjects differ')
if binding['roleRef'] != {'apiGroup': 'rbac.authorization.k8s.io',
'kind': 'Role', 'name': ROLE}:
raise RuntimeError('consumer binding role differs')
expected = [
{'apiGroups': [''], 'resources': ['pods'], 'verbs': ['get', 'list']},
{'apiGroups': [''], 'resources': ['pods/portforward'], 'verbs': ['create']},
{'apiGroups': ['apps'], 'resources': ['deployments'],
'resourceNames': ['playwright'], 'verbs': ['get']},
]
if role['rules'] != expected:
raise RuntimeError('consumer role rules differ')
cases = [
(True, 'get', 'pods', '', '', 'playwright', '', True),
(True, 'list', 'pods', '', '', 'playwright', '', True),
(True, 'create', 'pods', 'portforward', '', 'playwright', '', True),
(True, 'get', 'deployments', '', 'apps', 'playwright', 'playwright', True),
(True, 'get', 'deployments', '', 'apps', 'playwright', 'other', False),
(True, 'get', 'secrets', '', '', 'playwright', '', False),
(True, 'create', 'pods', 'exec', '', 'playwright', '', False),
(True, 'create', 'pods', '', '', 'playwright', '', False),
(True, 'patch', 'deployments', '', 'apps', 'playwright', 'playwright', False),
(True, 'create', 'pods', 'portforward', '', 'default', '', False),
(True, 'create', 'rolebindings', '', 'rbac.authorization.k8s.io', 'playwright', '', False),
(False, 'list', 'pods', '', '', 'playwright', '', False),
(False, 'create', 'pods', 'portforward', '', 'playwright', '', False),
]
checks = []
for member, verb, resource, subresource, group, namespace, name, expected_allowed in cases:
attributes = dict(verb=verb, resource=resource, subresource=subresource,
group=group, namespace=namespace, name=name)
args = ['--as=rpf-playwright-rbac-probe', '--as-group=system:authenticated']
if member:
args.append('--as-group=' + GROUP)
# Raw review endpoint avoids kubectl's unrelated CRD-discovery permission.
review = kubectl([*args, 'create', '--raw',
'/apis/authorization.k8s.io/v1/selfsubjectaccessreviews',
'-f', '-'], {
'apiVersion': 'authorization.k8s.io/v1', 'kind': 'SelfSubjectAccessReview',
'spec': {'resourceAttributes': attributes}})
status = review['status']
if status.get('evaluationError') or type(status.get('allowed')) is not bool:
raise RuntimeError('authorization review incomplete')
checks.append(dict(member=member, attributes=attributes,
expected=expected_allowed, allowed=status['allowed'],
passed=status['allowed'] == expected_allowed))
return dict(schema='platform-playwright-rbac/v1',
observed_at=datetime.now(timezone.utc).isoformat(), cluster_uid=uid,
role_uid=role['metadata']['uid'], binding_uid=binding['metadata']['uid'],
group=GROUP, checks=checks, passed=all(c['passed'] for c in checks),
actual_consumer_authentication_proven=False)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--target', default='railiance01', choices=['railiance01'])
parser.add_argument('--output', required=True, type=Path)
args = parser.parse_args()
result = verify(args.target)
args.output.write_text(json.dumps(result, indent=2) + '\n')
print(json.dumps({'passed': result['passed'], 'checks': len(result['checks']),
'evidence': str(args.output)}))
raise SystemExit(0 if result['passed'] else 1)

View file

@ -1,25 +1,27 @@
# Current platform work
Reviewed September 27, 2026. Eight workplans remain blocked with 23 waiting
tasks. No active, ready or proposed source workplan remains. Completed work
retains its IDs and managed UUIDs, including plans under `archived/`.
Reviewed October 13, 2026 against source workplans and the live inbox. Source
files remain authoritative; the generated brief contains historical aliases.
Completed plans retain their IDs and managed UUIDs, including archived plans.
| Workplan | Remaining tasks | Next dependency |
| Workplan | Open tasks | Next work or dependency |
| --- | --- | --- |
| [RPF-WP-0015](RPF-WP-0015-audit-core-custody-and-recovery-coordination.md) | T02/T03 | Fresh recovery windows, synthetic sender/abort owner and snapshot/quorum/access evidence |
| [RPF-WP-0027](RPF-WP-0027-keycape-live-secret-exposure-recovery.md) | T03/T06 | Complete historical resolver custody/routing contract; T05 closed from September 23 operator disposition and owner receipt |
| [RPF-WP-0035](RPF-WP-0035-credential-lane-implementation.md) | T02/T03/T06/T07/T08 | Exact service registration/custody, human memo acceptance, reader/disablement returns, governed npm migration and formerly-valid audit-bearer revocation proof |
| [RPF-WP-0036](RPF-WP-0036-platform-service-assurance.md) | T03/T04/T06 | Current recovery/cadence/custody evidence, Q2 production delivery and owner/projection acceptance; local archive adapter implemented |
| [RPF-WP-0038](RPF-WP-0038-forgejo-scaleway-primary-coverage.md) | T04 | Durable scheduled caller, canonical verified inventory and quota/retention gates; September 15 cutover/expiration hold persists |
| [RPF-WP-0043](RPF-WP-0043-policy-nexus-argocd-onboarding.md) | T02/T03/T04 | Cross-owner chart/values agreement, committed values and source access, zero-diff proof and adoption |
| [RPF-WP-0044](RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md) | T03–T08 | Per-app promotion gates, issue-core credentials/ownership, target-revenue hook acceptance, ESO adoption/observation and readable old-controller inventory |
| [RPF-WP-0048](RPF-WP-0048-activity-core-gitops-adoption.md) | T02 | Healthy observation through September 28 at 16:06:22 Berlin plus bounded authenticated release broker/admission and rollback proof |
| [RPF-WP-0015](RPF-WP-0015-audit-core-custody-and-recovery-coordination.md) (active) | T02, T03 | Recovery windows, sender/abort owner and snapshot/quorum/access evidence |
| [RPF-WP-0027](RPF-WP-0027-keycape-live-secret-exposure-recovery.md) (blocked) | T03, T06 | Resolver and custody owner contracts |
| [RPF-WP-0035](RPF-WP-0035-credential-lane-implementation.md) (blocked) | T02, T03, T07, T08, T10, T12 | Execution integration and owner acceptance; verified-source compiler correction tested October 13 |
| [RPF-WP-0036](RPF-WP-0036-platform-service-assurance.md) (blocked) | T03, T04, T06 | Bounded scheduler, telemetry receiving contract and ownership returns |
| [RPF-WP-0038](RPF-WP-0038-forgejo-scaleway-primary-coverage.md) (blocked) | T04 | Durable caller, verified inventory and quota/retention; expiration/cutover hold persists |
| [RPF-WP-0043](RPF-WP-0043-policy-nexus-argocd-onboarding.md) (blocked) | T03, T04 | Owner values/render diff, then root automation before adoption |
| [RPF-WP-0044](RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md) (blocked) | T04, T06, T07 | Attended issue-core issuer login, ordered ESO adoption and root automation |
| [RPF-WP-0048](RPF-WP-0048-activity-core-gitops-adoption.md) (blocked) | T02 | Activity-core promotion evidence |
| [RPF-WP-0053](RPF-WP-0053-coulomb-paired-backup-admission.md) (proposed) | T01, T02, T03, T04, T05 | Paired archive execution/custody, retrieval/recovery and monitoring admission |
| [RPF-WP-0055](RPF-WP-0055-keycape-client-secret-rotation-residual.md) (backlog) | T01 | Unpromoted rotation residual; do not implement without demand/risk promotion |
| [RPF-WP-0056](RPF-WP-0056-playwright-consumer-admission.md) (blocked) | T03 | Dedicated Playwright consumer group, managed pilot and authenticated identity route |
Latest [review and evidence](../history/2026-09-27-loose-end-review.md).
No new task or workplan was created. Existing completed credential reviews,
delivery, retention projection and rotations are not reopened by these waits.
October 13 review corrected WP-0038-T04's stale `todo` state to the documented
`wait`. RPF-IN-0012 remains an open independent execution/wrapped-delivery review;
the compiler fix does not close it. Playwright publication is resolved; its new
Platform workplan records the user-requested group and actual activation evidence.
Source files are authoritative. The generated brief still contains retired
aliases and stale statuses; do not recreate work from it. Routine synchronization
uses the exact-commit Repo Manager receipt described in AGENTS.md. Legacy alias
repair remains the scoped owner dependency in WP-0036-T06.
Routine synchronization uses the exact-commit Repo Manager receipt described in
AGENTS.md. Legacy alias repair remains the scoped WP-0036-T06 dependency.

View file

@ -8,7 +8,7 @@ status: blocked
flavor: implementation
owner: codex
created: "2026-09-06"
updated: "2026-10-03"
updated: "2026-10-13"
state_hub_workstream_id: "7beec1a7-aa82-5a36-9a66-6b60008a2455"
---
@ -72,7 +72,8 @@ removed. Evidence: `docs/evidence/forgejo-scaleway-restore-2026-09-06.json`.
```task
id: RPF-WP-0038-T04
status: todo
status: wait
blocking_reason: "Durable scheduled caller, verified inventory and quota/retention gates remain open; September 15 prohibits expiration and scheduled secondary cutover."
priority: high
state_hub_task_id: "a4807df0-ec96-58f1-9cbd-42b1dcde0d6f"
```

View file

@ -0,0 +1,122 @@
---
id: RPF-WP-0056
type: workplan
title: "Admit the Playwright pilot and establish scoped consumer group access"
domain: financials
repo: railiance-platform
status: blocked
owner: codex
topic_slug: railiance
created: "2026-10-13"
updated: "2026-10-13"
---
Incoming owner request: State Hub c82d75f3-3191-410d-a8c8-fd647bf7f3ef,
superseding publication return a282c300-eaef-4cd6-99b0-f4fb255203b3;
consumer RAPP-PLAYWRIGHT-WP-0004-T01. The user selected establishing a group
in this session. Platform owns lifecycle admission; implementation remains in
rapp-playwright. Publication is complete, and must not remain a blocker.
## Review the immutable package and pilot boundary
```task
id: RPF-WP-0056-T01
status: done
priority: high
```
Reviewed rapp-playwright revision 70268154c06cf78c5d9e1f4eb900213ffd436d1b:
declaration, release, workload, admission and operations contract. The published
image is sha256:8d6fd5a8172cf906bb503642ed620761945c5138b86f6396c36f65cbe6def441.
Three provider contract/network/proxy tests pass independently in this session.
One Recreate replica requests 100m CPU/256Mi memory, limits 1 CPU/1Gi, and uses
a 2Gi transient artifact PVC with one-hour retention. Non-root, read-only root,
no service-account token, dropped capabilities, loopback listener, denied ingress
and public HTTPS/DNS egress match the proposed trusted-target pilot.
Chromium's OS sandbox is disabled; this is no hostile-page or cross-tenant
isolation acceptance. Initial targets remain the bundled fixture and public
https://onco.cc; no private preview, source import or data-license grant follows.
## Establish and verify the consumer group RBAC boundary
```task
id: RPF-WP-0056-T02
status: done
priority: high
```
User requested a group; use exact Kubernetes group `playwright-consumers`.
Platform binding: helm/playwright-consumer-access.yaml, referring to the package's
existing namespace Role `playwright-consumer`. Bootstrap only its reviewed
Namespace and Role, then bind the group. No cluster-wide role, Secret access,
pod execution, workload mutation, credential issuance or group membership grant.
Verify live allowed discovery/port-forward and denied execution/Secrets/foreign
namespace/workload changes, plus nonmember denial. Impersonated authorization
checks prove RBAC only, not actual consumer authentication or connectivity.
## Connect platform identities and prove managed consumer operation
```task
id: RPF-WP-0056-T03
status: wait
priority: high
blocked_on: message-from:railiance-cluster
blocking_reason: "User admitted all identified platform humans and coding agents; their supported Kubernetes authentication/group-mapping route remains unimplemented."
```
The session's initial readback found only built-in/system user/group bindings;
the cluster documentation describes an administrator kubeconfig. A group subject
in RBAC does not create an identity-provider group or convey membership. The user admitted all identified platform humans and coding agents; establish
their existing-owner authentication and group-mapping route,
including expiration/revocation. Do not distribute the admin kubeconfig or infer
that a KeyCape group is already understood by the Kubernetes API.
The immutable pilot is now deployed after fresh capacity, server dry-run and
diff: 1/1 Ready, 2Gi PVC Bound and matching imageID. A real group-identity trial
is still required; administrator impersonation is recorded separately.
Retain screenshots/keyboard receipts, wrong-token/assertion/target negatives,
cancellation and restart/interrupted/completed-artifact recovery. One available
release supports same-image recovery only; cross-version rollback stays unproven.
Keep RAPP-PLAYWRIGHT-WP-0004-T01 open until its owner accepts those receipts.
## October 13 native implementation
ADR-0006 records the group decision. Namespace, package Role and Platform binding
are applied on the pinned railiance01 cluster. Thirteen native authorization
checks pass: intended discovery/forwarding is allowed; Secrets, exec, writes,
foreign namespaces, foreign deployment and nonmember access are denied. The
initial verifier's kubectl schema discovery needed unrelated CRD access; using
the native SelfSubjectAccessReview endpoint corrected the verifier without any
permission expansion. Re-run scripts/verify_playwright_access.py with --output.
Fresh capacity before deployment: 3460m/4000m CPU requested, 9472Mi memory
requested and 126Gi free storage. Adding 100m/256Mi plus a 2Gi PVC fits that
scheduling headroom; aggregate CPU/memory limits were already overcommitted.
The declared single-browser limits remain in force. Full package server dry-run
and diff passed and the published immutable image reached Ready.
Initial fixture trial through administrator-impersonated group port-forward
passed desktop/narrow keyboard and screenshot checks. This demonstrates native
RBAC and browser operation; it does not turn the administrative SSH identity
into a generally available consumer authentication route. T03 retains that gap.
## Activate the bounded pilot and verify operator-driven recovery
```task
id: RPF-WP-0056-T04
status: done
priority: high
```
The published package is running. Thirteen RBAC checks and six native negative/
health checks pass, including wrong receipt/artifact token, deliberate assertion
failure, bounded cancellation and private-target refusal. Actual Deployment
restart reached Ready with a new pod UID, the same immutable image and the same
PVC; all four completed artifacts were retrieved and their hashes matched.
Evidence: docs/evidence/2026-10-13-playwright-managed/ and
docs/evidence/2026-10-13-playwright-rbac.json. This is same-image recovery, not
cross-version rollback or abrupt-interruption proof. T03 remains open for actual
consumer identity/group mapping and the remaining package-owner acceptance.
Group decision mirrored to State Hub d2426028-0b04-4ea2-b199-d7f9e19e38e6.