From f56d0b2dc823d4b8d1a6c196cd899fd8af358cb7 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 6 Nov 2026 01:15:03 +0100 Subject: [PATCH] NK-WP-0058-T05: grant implemented in railiance-platform (4e60f69, 2e1ca67); net-kingdom copy removed, test reads the add-on, pins bumped; founder sync steps Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 943911@bnt-lap001 Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0 --- contracts/owner-pins.json | 2 +- .../README.md | 31 +++++-- .../rbac.yaml | 88 ------------------- .../tests/test_agent_status_grant.py | 17 ++-- ...K-WP-0058-bounded-execution-operability.md | 14 ++- 5 files changed, 47 insertions(+), 105 deletions(-) delete mode 100644 docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml diff --git a/contracts/owner-pins.json b/contracts/owner-pins.json index a5affa7..1901e17 100644 --- a/contracts/owner-pins.json +++ b/contracts/owner-pins.json @@ -20,7 +20,7 @@ }, { "repo": "railiance-platform", - "commit": "31cf74d36bb81e9c0e70c200196cd7846df1e4b6" + "commit": "2e1ca67a0ea2a4d69353a0f2a943fdd69f2a844d" }, { "repo": "key-cape", diff --git a/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/README.md b/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/README.md index 65f3274..918475d 100644 --- a/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/README.md +++ b/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/README.md @@ -1,7 +1,8 @@ # NK-WP-0058-T05: read-only status for the agent identity (proposal, 2026-11-06) -**Status: decided 2026-11-06, option A with 1 h tokens** (State Hub `4af1ec01`). Not yet applied: it waits for the -owner's review. +**Status: decided 2026-11-06, option A with 1 h tokens** (State Hub `4af1ec01`). It's implemented in railiance-platform, +on the founder's instruction: `argocd/platform-addons/nk-agent-status/` (`4e60f69`) and its manual, pinned +Application (`2e1ca67`), guarded by `tests/test_nk_agent_status_rbac.py`. Not yet synced: the founder steps below. ## Why @@ -24,7 +25,7 @@ need, and the agent would still act as ops-mason. | Option | Grant | Pod logs | Assessment | | --- | --- | --- | --- | -| **A (recommended)** | ServiceAccount `sso/nk-agent-status` with [`rbac.yaml`](rbac.yaml): read Jobs, pods, Deployments, ExternalSecrets in `sso`; one ConfigMap in `informed-decision`; one Argo Application | founder-only | Covers every status but logs. `bx status` already carries the run's result, and `--preflight-only` replaces diagnostic pods | +| **A (chosen)** | ServiceAccount `sso/nk-agent-status` (railiance-platform `argocd/platform-addons/nk-agent-status/`): read Jobs, pods, Deployments, ExternalSecrets in `sso`; one ConfigMap in `informed-decision`; one Argo Application | founder-only | Covers every status but logs. `bx status` already carries the run's result, and `--preflight-only` replaces diagnostic pods | | B | A plus `pods/log` in `sso` | all `sso` pods | RBAC cannot limit logs to the executor's pods by label: this would also read KeyCape, Authelia, LLDAP and privacyIDEA logs. Not recommended | | C | widen `ops-mason-builder` | — | against SI-14 (two actors on one identity). Not recommended | | D | no grant | founder-only | everything stays as on 2026-11-05 | @@ -61,13 +62,27 @@ It holds no token itself. A longer token is a deviation from SI-14 and would nee **Owner:** railiance-platform. Agent-facing cluster grants live there as platform add-ons, the precedent being `argocd/platform-addons/ops-mason-builder/`, applied by a manually synced, pinned Argo Application. The proposal first -named railiance-cluster, which has no RBAC manifests. railiance-platform is asked to adopt `rbac.yaml` the same way, -as `argocd/platform-addons/nk-agent-status/`. Unlike ops-mason-builder's certificate User, this identity is a -ServiceAccount with 1 h tokens (SI-14). +named railiance-cluster, which has no RBAC manifests. The add-on was written there the same way, as +`argocd/platform-addons/nk-agent-status/`. Unlike ops-mason-builder's certificate User, this identity is a +ServiceAccount with 1 h tokens (SI-14). net-kingdom keeps no copy: its contract test reads the platform's files. -## Founder steps after the owner's commit +## Founder steps -1. **Once:** sync the reviewed Application in Argo CD, as for ops-mason-builder. +Follow railiance-platform's attended pattern for add-ons (RPF-WP-0062, `custodian-agents`): no root-wide sync, apply +strategy, prune and force off. From `~/railiance-platform` after `git pull --ff-only`: + +1. **Once, Argo CD:** + + ```bash + K="kubectl --kubeconfig $HOME/.kube/config-railiance01" + make argocd-pin-drift # read-only; note any DRIFT line (the sync below touches only nk-agent-status) + $K -n argocd patch application railiance-apps-root --type merge -p '{"operation":{"initiatedBy":{"username":"nk-wp-0058-t05"},"sync":{"revision":"2e1ca67a0ea2a4d69353a0f2a943fdd69f2a844d","prune":false,"resources":[{"group":"argoproj.io","kind":"Application","name":"nk-agent-status","namespace":"argocd"}],"syncStrategy":{"apply":{"force":false}}}}}' + $K -n argocd patch application nk-agent-status --type merge -p '{"operation":{"initiatedBy":{"username":"nk-wp-0058-t05"},"sync":{"revision":"4e60f69f93bf21ec245f1991d86dc6fe1fa2a357","prune":false,"syncStrategy":{"apply":{"force":false}}}}}' + $K -n argocd get application nk-agent-status -o jsonpath='{.status.sync.status} {.status.health.status} {.status.sync.revision}{"\n"}' + ``` + + Expected last line: `Synced Healthy 4e60f69f…`. The first patch creates only the `nk-agent-status` Application + from the root, and the second creates its seven objects. 2. **Once:** build the agent's kubeconfig. It holds the cluster's CA and a pointer to the token file, never a token. No command prints a credential: diff --git a/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml b/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml deleted file mode 100644 index b295494..0000000 --- a/docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml +++ /dev/null @@ -1,88 +0,0 @@ -# NK-WP-0058-T05: read-only status for the agent's own Kubernetes identity. DECIDED (option A, State Hub 4af1ec01), -# NOT APPLIED. Owner: railiance-platform, asked to adopt it as argocd/platform-addons/nk-agent-status/. -# -# Option A: status of the bounded-execution path, no pod logs. A dedicated ServiceAccount, not a -# wider ops-mason-builder: SI-14 wants the agent to present as itself, with tokens of at most 60 minutes. -# Never: Secrets, pod logs, exec, port-forward, create, update, patch, delete, or anything cluster-wide. -# Tests: tools/contract-tests/tests/test_agent_status_grant.py. -apiVersion: v1 -kind: ServiceAccount -metadata: - name: nk-agent-status - namespace: sso - labels: - app.kubernetes.io/name: nk-agent-status - app.kubernetes.io/part-of: net-kingdom -automountServiceAccountToken: false # no pod runs as it; the founder mints a 1 h token per session ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: nk-agent-status - namespace: sso -rules: - # executor Jobs and their pods (phase, conditions, exit codes); Deployments for KeyCape/Authelia rollouts - - apiGroups: ["batch"] - resources: ["jobs"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["deployments"] - verbs: ["get", "list", "watch"] - # Ready condition of the custody deliveries; an ExternalSecret holds references, never values - - apiGroups: ["external-secrets.io"] - resources: ["externalsecrets"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: nk-agent-status - namespace: sso -roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status} -subjects: - - {kind: ServiceAccount, name: nk-agent-status, namespace: sso} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: nk-agent-status - namespace: informed-decision -rules: - # the live routing and submitter registry (review-groups.json, intake-submitters.json): non-secret configuration - - apiGroups: [""] - resources: ["configmaps"] - resourceNames: ["informed-decision-workload-registry"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: nk-agent-status - namespace: informed-decision -roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status} -subjects: - - {kind: ServiceAccount, name: nk-agent-status, namespace: sso} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: nk-agent-status - namespace: argocd -rules: - # the informed-decision Application's sync revision and status: is live the pinned revision? (contract row O1) - - apiGroups: ["argoproj.io"] - resources: ["applications"] - resourceNames: ["informed-decision"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: nk-agent-status - namespace: argocd -roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status} -subjects: - - {kind: ServiceAccount, name: nk-agent-status, namespace: sso} diff --git a/tools/contract-tests/tests/test_agent_status_grant.py b/tools/contract-tests/tests/test_agent_status_grant.py index 02443b9..dbf77f5 100644 --- a/tools/contract-tests/tests/test_agent_status_grant.py +++ b/tools/contract-tests/tests/test_agent_status_grant.py @@ -1,15 +1,17 @@ -"""NK-WP-0058-T05: the proposed agent status grant stays read-only and narrow, and what it can read holds no secret. +"""NK-WP-0058-T05: the agent status grant stays read-only and narrow, and what it can read holds no secret. -The grant (docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml) is source only until decided. These -checks keep it from widening by accident, and keep the sso workloads it can read free of literal secret values: -`get` on a pod or Deployment returns its env, so a secret written there would become readable. +The grant is railiance-platform's add-on argocd/platform-addons/nk-agent-status/ (decision 4af1ec01, option A). Its +own guard is railiance-platform tests/test_nk_agent_status_rbac.py; this is net-kingdom's consumer-side check of the +same files, plus the sso workloads it can read: `get` on a pod or Deployment returns its env, so a literal secret +value there would become readable. """ from pathlib import Path +import pytest import yaml REPO = Path(__file__).resolve().parents[3] -GRANT = REPO / 'docs/proposals/2026-11-06-nk58-t05-agent-status-grant/rbac.yaml' +ADDON = REPO.parent / 'railiance-platform/argocd/platform-addons/nk-agent-status' READ = {'get', 'list', 'watch'} NEVER = {'secrets', 'pods/log', 'pods/exec', 'pods/attach', 'pods/portforward', 'serviceaccounts/token'} ALLOWED = {('sso', 'batch', 'jobs'), ('sso', '', 'pods'), ('sso', 'apps', 'deployments'), @@ -25,7 +27,10 @@ def secret_named(name): def docs(): - return [d for d in yaml.safe_load_all(GRANT.read_text()) if d] + if not (ADDON / 'kustomization.yaml').exists(): + pytest.skip('railiance-platform checkout with the nk-agent-status add-on required') + resources = yaml.safe_load((ADDON / 'kustomization.yaml').read_text())['resources'] + return [d for r in resources for d in yaml.safe_load_all((ADDON / r).read_text()) if d] def test_only_namespaced_read_rules_on_the_listed_resources(): diff --git a/workplans/NK-WP-0058-bounded-execution-operability.md b/workplans/NK-WP-0058-bounded-execution-operability.md index 9403e66..de879c4 100644 --- a/workplans/NK-WP-0058-bounded-execution-operability.md +++ b/workplans/NK-WP-0058-bounded-execution-operability.md @@ -435,9 +435,9 @@ priority: medium assignee: claude-code needs_human: true blocked_on: human -blocking_reason: "Decided (A, 4af1ec01); waits for railiance-platform to adopt the grant as a platform add-on, then the founder's sync and token." +blocking_reason: "Add-on in railiance-platform (4e60f69, 2e1ca67); waits for the founder's Argo sync, agent kubeconfig and first token." decision_id: "4af1ec01-e8fb-4352-a09b-ae9cda53d984" -intervention_note: "railiance-platform adopts argocd/platform-addons/nk-agent-status/; founder syncs it, builds the agent kubeconfig once, mints 1 h tokens per session." +intervention_note: "Founder runs the steps in docs/proposals/2026-11-06-nk58-t05-agent-status-grant/README.md; then the agent verifies with can-i." depends_on: [NK-WP-0058-T10] state_hub_task_id: "30f8c62e-5707-5395-9d86-178cd650fb59" ``` @@ -471,6 +471,16 @@ After the decision: railiance-cluster reviews the change, the founder applies it - **Then the agent verifies:** `auth can-i --list` shows only the grant, and Secrets and pod logs in `sso` answer `no`. +**2026-11-06: add-on implemented** on the founder's instruction, in railiance-platform: +- `argocd/platform-addons/nk-agent-status/` (`4e60f69`) with `tests/test_nk_agent_status_rbac.py`; +- the manual, pinned Application (`2e1ca67`). + +net-kingdom's copy of the grant is deleted: its contract test reads the platform's files, and the owner pins move to +`2e1ca67`. The founder steps are in the proposal README: +- a selective root sync and the child sync, with prune and force off; +- the agent kubeconfig, once; +- `make agent-status-token`. + Propose a read-only grant for the agent's Kubernetes identity in the namespaces the path uses. The grant covers: - the status and conditions of Jobs, pods, ExternalSecrets and Deployments;