ADHOC-2026-11-07: adopt NK-WP-0058-T07 runbook conventions; unstale CCR seed test
One cluster route (NK_KUBECONFIG, default ~/.kube/config-railiance01) and server-side apply throughout. Fixes the Makefile dry-run defaulting to the coulombcore kubeconfig. Lane-run scripts take no default and fail closed when NK_KUBECONFIG is unset. net-kingdom convention lint: 14 findings -> 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 73091@bnt-lap001 Assistant-Session: 9f48906c-6737-449f-8b97-fb86877ca59a
This commit is contained in:
parent
782458d327
commit
5649ef90c4
13 changed files with 110 additions and 27 deletions
6
Makefile
6
Makefile
|
|
@ -27,8 +27,8 @@ help: ## Show this help
|
|||
IMAGE_REGISTRY ?= forgejo.coulomb.social/coulomb/audit-core
|
||||
GIT_COMMIT := $(shell git rev-parse HEAD)
|
||||
GIT_COMMIT_SHORT := $(shell git rev-parse --short HEAD)
|
||||
# railiance01 k3s API is forwarded by ops-bridge tunnel k3s-api-railiance01.
|
||||
KUBECONFIG_RAILIANCE ?= $(HOME)/.kube/config-hosteurope
|
||||
# One cluster route (NK-WP-0058-T07): NK_KUBECONFIG, default the railiance01 kubeconfig.
|
||||
NK_KUBECONFIG ?= $(HOME)/.kube/config-railiance01
|
||||
|
||||
image-build: ## Build the immutable image, labelled with the current commit
|
||||
docker build -f Containerfile \
|
||||
|
|
@ -43,7 +43,7 @@ image-publish: image-build ## Push the commit-tagged image to Forgejo
|
|||
@echo "Pin the printed digest in deploy/audit-core.yaml and deploy/migrate-job.yaml"
|
||||
|
||||
deploy-dry-run: ## Server-side validate the railiance01 manifests
|
||||
KUBECONFIG=$(KUBECONFIG_RAILIANCE) kubectl apply --dry-run=server --validate=strict \
|
||||
KUBECONFIG=$(NK_KUBECONFIG) kubectl apply --server-side --dry-run=server --validate=strict \
|
||||
-f deploy/audit-core.yaml \
|
||||
-f deploy/senders-scope.yaml \
|
||||
-f deploy/networkpolicies.yaml \
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
| workplan | ADHOC-2026-09-14 | finished | — | workplans/ADHOC-2026-09-14.md |
|
||||
| workplan | ADHOC-2026-09-24 | finished | — | workplans/ADHOC-2026-09-24.md |
|
||||
| workplan | ADHOC-2026-10-15 | finished | — | workplans/ADHOC-2026-10-15.md |
|
||||
| workplan | ADHOC-2026-11-07 | finished | — | workplans/ADHOC-2026-11-07.md |
|
||||
| workplan | AUDIT-WP-0001 | finished | — | workplans/AUDIT-WP-0001-statehub-bootstrap.md |
|
||||
| workplan | AUDIT-WP-0002 | finished | — | workplans/AUDIT-WP-0002-pluggable-audit-backend.md |
|
||||
| workplan | AUDIT-WP-0003 | finished | — | workplans/AUDIT-WP-0003-user-engine-event-ingestion-service.md |
|
||||
|
|
@ -32,6 +33,8 @@
|
|||
| task | ADHOC-2026-09-14-T01 | done | — | workplans/ADHOC-2026-09-14.md |
|
||||
| task | ADHOC-2026-09-24-T01 | done | — | workplans/ADHOC-2026-09-24.md |
|
||||
| task | ADHOC-2026-10-15-T01 | done | — | workplans/ADHOC-2026-10-15.md |
|
||||
| task | ADHOC-2026-11-07-T01 | done | — | workplans/ADHOC-2026-11-07.md |
|
||||
| task | ADHOC-2026-11-07-T02 | done | — | workplans/ADHOC-2026-11-07.md |
|
||||
| task | AUDIT-WP-0001-T01 | done | — | workplans/AUDIT-WP-0001-statehub-bootstrap.md |
|
||||
| task | AUDIT-WP-0001-T02 | done | — | workplans/AUDIT-WP-0001-statehub-bootstrap.md |
|
||||
| task | AUDIT-WP-0001-T03 | done | — | workplans/AUDIT-WP-0001-statehub-bootstrap.md |
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# railiance01 package
|
||||
|
||||
Target: railiance01 only. The workstation kubeconfig that talks to that API
|
||||
is the `k3s-api-railiance01` tunnel (local port **16444**).
|
||||
`~/.kube/config-hosteurope` currently points at 16443 (coulombcore); rewrite
|
||||
the server port or export a copy before applying.
|
||||
Target: railiance01 only. Reach it through the one cluster route
|
||||
(NK-WP-0058-T07): `NK_KUBECONFIG`, default `~/.kube/config-railiance01`. Do not
|
||||
use any other kubeconfig file or a `--server` override. Every apply is
|
||||
`kubectl apply --server-side`.
|
||||
|
||||
Apply order is documented in `docs/operator-runbook.md`. Do not apply the
|
||||
Deployment until:
|
||||
|
|
|
|||
|
|
@ -339,15 +339,21 @@ run `nk_bind_executor_custody.py fence` for the coding-agent boundary entries.
|
|||
dry-run first, then the apply:
|
||||
|
||||
```bash
|
||||
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl apply --dry-run=server -f -
|
||||
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl apply -f -
|
||||
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" kubectl apply --server-side --dry-run=server -f -
|
||||
cd /home/worsch/audit-core && python3 -c 'import sys,yaml; d=[x for x in yaml.safe_load_all(open("deploy/networkpolicies.yaml")) if x and x["metadata"]["name"]=="audit-core-nk-bounded-executor-ingress"]; assert len(d)==1; yaml.safe_dump(d[0],sys.stdout)' | KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" kubectl apply --server-side -f -
|
||||
```
|
||||
|
||||
All cluster commands here use the one cluster route (NK-WP-0058-T07):
|
||||
`NK_KUBECONFIG`, default `~/.kube/config-railiance01`. Every apply is
|
||||
server-side. The 2026-11-04 apply was client-side, so the live object still
|
||||
carries a `last-applied-configuration` annotation. A later server-side apply
|
||||
takes the object over, so nothing needs to be done now.
|
||||
|
||||
**2. Read-only preflight** (prints no secrets):
|
||||
|
||||
```bash
|
||||
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get configmap audit-core-senders-scope -o jsonpath='{.data.senders-scope\.json}' | python3 -c 'import sys,json; print([e for e in json.load(sys.stdin) if e["name"]=="nk-bounded-executor"])'
|
||||
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get deploy audit-core -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
|
||||
KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" kubectl -n audit-core get configmap audit-core-senders-scope -o jsonpath='{.data.senders-scope\.json}' | python3 -c 'import sys,json; print([e for e in json.load(sys.stdin) if e["name"]=="nk-bounded-executor"])'
|
||||
KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" kubectl -n audit-core get deploy audit-core -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
|
||||
bridge status
|
||||
```
|
||||
|
||||
|
|
@ -360,7 +366,7 @@ This is one silent command. Read the result from the receipt, not from the
|
|||
terminal:
|
||||
|
||||
```bash
|
||||
BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- /home/worsch/audit-core/scripts/attended-seed-nk-bounded-executor-sender.sh /home/worsch/audit-core/docs/evidence/nk-bounded-executor-sender-seed-r1.json
|
||||
BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- env NK_KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" /home/worsch/audit-core/scripts/attended-seed-nk-bounded-executor-sender.sh /home/worsch/audit-core/docs/evidence/nk-bounded-executor-sender-seed-r1.json
|
||||
```
|
||||
|
||||
The receipt is created exclusively. If you rerun, use `-r2.json`. The receiver
|
||||
|
|
@ -370,7 +376,7 @@ restart is part of this step. No separate rollout is needed.
|
|||
|
||||
```bash
|
||||
python3 -c 'import json; r=json.load(open("/home/worsch/audit-core/docs/evidence/nk-bounded-executor-sender-seed-r1.json")); print(r["status"], r.get("error"), r.get("receiver_probe"), r.get("registry_secret_hash_match"), r.get("sso_secret_hash_match"), r["credential_values_emitted"])'
|
||||
KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl -n audit-core get networkpolicy audit-core-nk-bounded-executor-ingress
|
||||
KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}" kubectl -n audit-core get networkpolicy audit-core-nk-bounded-executor-ingress
|
||||
```
|
||||
|
||||
The expected status is `seeded_pending_sso_delivery`, or `seeded_and_delivered`
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# openbao-ui-railiance01 → http://127.0.0.1:18200. railiance-platform's
|
||||
# openbao-attended-exec.py pins that address for both OIDC and the child.
|
||||
set -euo pipefail
|
||||
export RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-$HOME/.kube/config-railiance01}"
|
||||
export NK_KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}"
|
||||
cd "$(dirname "$0")/.."
|
||||
exec python3 /home/worsch/railiance-platform/scripts/openbao-attended-exec.py -- \
|
||||
"$PWD/scripts/mint-tenant-engine-sender.py"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Attended overlap-then-drop proof that a formerly valid audit bearer is 401.
|
||||
# Uses the operator OpenBao tunnel. Production tokens are not replaced.
|
||||
set -euo pipefail
|
||||
export RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-$HOME/.kube/config-railiance01}"
|
||||
export NK_KUBECONFIG="${NK_KUBECONFIG:-$HOME/.kube/config-railiance01}"
|
||||
cd "$(dirname "$0")/.."
|
||||
exec python3 /home/worsch/railiance-platform/scripts/openbao-attended-exec.py -- \
|
||||
"$PWD/scripts/prove-sender-bearer-revocation.py"
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@
|
|||
# NK-WP-0056-T15, CCR-2026-0063). Founder-run under
|
||||
# warden access openbao-platform-admin-login --exec -- <this script> <receipt>
|
||||
# Results go to the receipt only; any output would fail the warden envelope closed.
|
||||
# Inside the lane HOME is the warden envelope, so the kubeconfig default is absolute.
|
||||
# Inside the lane HOME is the warden envelope, so there is no kubeconfig default:
|
||||
# the caller passes the one cluster route (NK-WP-0058-T07), expanded outside the lane:
|
||||
# warden access ... --exec -- env NK_KUBECONFIG="$HOME/.kube/config-railiance01" <this script> <receipt>
|
||||
# Unset, it exits 64 silently before anything runs.
|
||||
set -euo pipefail
|
||||
receipt="${1:?receipt path required}"
|
||||
[[ -n "${NK_KUBECONFIG:-}" ]] || exit 64
|
||||
exec python3 "$(dirname "$0")/seed-nk-bounded-executor-sender.py" \
|
||||
--kubeconfig "${RAILIANCE01_KUBECONFIG:-/home/worsch/.kube/config-railiance01}" \
|
||||
--kubeconfig "$NK_KUBECONFIG" \
|
||||
--confirm 'SEED nk-bounded-executor CCR-2026-0063' \
|
||||
--receipt "$receipt" >/dev/null 2>&1
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ TRADE = (
|
|||
"declared at tenant-engine/docs/evidence-emission.md"
|
||||
)
|
||||
EVIDENCE = Path("/home/worsch/audit-core/docs/evidence/2026-09-15-tenant-engine-sender-mint.json")
|
||||
KUBECONFIG = os.environ.get("RAILIANCE01_KUBECONFIG", "/home/worsch/.kube/config-railiance01")
|
||||
# One cluster route (NK-WP-0058-T07), set by the parent wrapper outside the lane.
|
||||
KUBECONFIG = os.environ.get("NK_KUBECONFIG", "")
|
||||
RECEIVER_IMAGE = (
|
||||
"forgejo.coulomb.social/coulomb/audit-core@"
|
||||
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
|
||||
|
|
@ -77,6 +78,8 @@ def bao(args: list[str], *, payload: dict | None = None) -> dict:
|
|||
|
||||
|
||||
def kube(args: list[str]) -> subprocess.CompletedProcess:
|
||||
if not KUBECONFIG: # an empty --kubeconfig would fall back to a second, default route
|
||||
raise SystemExit(64)
|
||||
return subprocess.run(
|
||||
["kubectl", "--kubeconfig", KUBECONFIG, "--request-timeout=30s", *args],
|
||||
stdout=subprocess.PIPE,
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@ from datetime import datetime, timezone
|
|||
from pathlib import Path
|
||||
|
||||
REGISTRY = "platform/data/workloads/audit-core/senders"
|
||||
KUBECONFIG = os.environ.get(
|
||||
"RAILIANCE01_KUBECONFIG", "/home/worsch/.kube/config-railiance01"
|
||||
)
|
||||
# One cluster route (NK-WP-0058-T07), set by the parent wrapper outside the lane.
|
||||
KUBECONFIG = os.environ.get("NK_KUBECONFIG", "")
|
||||
IMAGE = (
|
||||
"forgejo.coulomb.social/coulomb/audit-core@"
|
||||
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
|
||||
|
|
@ -94,6 +93,8 @@ def bao(args: list[str], *, payload: dict | None = None) -> dict:
|
|||
|
||||
|
||||
def kube(args: list[str], *, input_bytes: bytes | None = None) -> subprocess.CompletedProcess:
|
||||
if not KUBECONFIG: # an empty --kubeconfig would fall back to a second, default route
|
||||
raise SystemExit(64)
|
||||
return subprocess.run(
|
||||
["kubectl", "--kubeconfig", KUBECONFIG, "--request-timeout=30s", *args],
|
||||
input=input_bytes,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import uuid
|
|||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
KUBECONFIG = os.environ.get("RAILIANCE01_KUBECONFIG", str(Path.home() / ".kube/config-railiance01"))
|
||||
KUBECONFIG = os.environ.get("NK_KUBECONFIG", str(Path.home() / ".kube/config-railiance01"))
|
||||
IMAGE = (
|
||||
"forgejo.coulomb.social/coulomb/audit-core@"
|
||||
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
|
||||
|
|
|
|||
|
|
@ -12,14 +12,16 @@
|
|||
# even when the command succeeds. Persist metadata only. Never print a token.
|
||||
#
|
||||
# warden access openbao-platform-admin-login --exec -- \
|
||||
# env RAILIANCE01_KUBECONFIG="$HOME/.kube/config-railiance01" \
|
||||
# env NK_KUBECONFIG="$HOME/.kube/config-railiance01" \
|
||||
# BAO_ADDR=http://127.0.0.1:18200 \
|
||||
# /home/worsch/audit-core/scripts/renew-runtime-lease.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
export RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-/home/worsch/.kube/config-railiance01}"
|
||||
export KUBECONFIG="$RAILIANCE01_KUBECONFIG"
|
||||
# One cluster route (NK-WP-0058-T07). No default: HOME inside the lane is the
|
||||
# warden envelope. Unset, the kubeconfig check below fails closed with evidence.
|
||||
export NK_KUBECONFIG="${NK_KUBECONFIG:-}"
|
||||
export KUBECONFIG="$NK_KUBECONFIG"
|
||||
# bao.coulomb.social is retired (2026-09-15); the tunnel is the operator path.
|
||||
export BAO_ADDR="${BAO_ADDR:-http://127.0.0.1:18200}"
|
||||
# Contained login persists the session in $HOME/.vault-token and unsets
|
||||
|
|
@ -68,7 +70,7 @@ if ! command -v kubectl >/dev/null 2>&1; then
|
|||
_write_evidence failed '{"reason": "kubectl_missing"}'
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$RAILIANCE01_KUBECONFIG" ]]; then
|
||||
if [[ ! -f "$NK_KUBECONFIG" ]]; then
|
||||
_write_evidence failed '{"reason": "kubeconfig_missing"}'
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -181,7 +181,10 @@ def approved_ccr():
|
|||
def test_ccr_contract_as_drafted_matches_and_refuses_until_approved():
|
||||
pytest.importorskip("yaml")
|
||||
SEED.check_ccr(approved_ccr())
|
||||
proposed, _ = SEED.load_ccr()
|
||||
# The live CCR was approved after this test was written (railiance-platform
|
||||
# f3b0c4f), so the unapproved case is a synthetic copy, not the live file.
|
||||
proposed = approved_ccr()
|
||||
proposed["status"] = "proposed"
|
||||
with pytest.raises(SEED.Refused, match="approved_ccr_required"):
|
||||
SEED.check_ccr(proposed)
|
||||
partial = approved_ccr()
|
||||
|
|
|
|||
61
workplans/ADHOC-2026-11-07.md
Normal file
61
workplans/ADHOC-2026-11-07.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
id: ADHOC-2026-11-07
|
||||
type: workplan
|
||||
title: "Adopt NetKingdom runbook conventions (NK-WP-0058-T07)"
|
||||
domain: infotech
|
||||
repo: audit-core
|
||||
status: finished
|
||||
owner: claude-code
|
||||
topic_slug: audit-evidence-dependencies
|
||||
created: "2026-11-07"
|
||||
updated: "2026-11-07"
|
||||
---
|
||||
|
||||
# Adopt NetKingdom runbook conventions (NK-WP-0058-T07)
|
||||
|
||||
## Fix the 14 convention-lint findings
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-11-07-T01
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Asked by net-kingdom (hub message 9e57fd89): `python3
|
||||
~/net-kingdom/tools/conventions/lint.py ~/audit-core` reported
|
||||
`one-cluster-route` 11 and `server-side-apply` 3. It now exits 0 with no
|
||||
exceptions claimed.
|
||||
|
||||
- **One cluster route:** `NK_KUBECONFIG`, default
|
||||
`~/.kube/config-railiance01`. It replaces `RAILIANCE01_KUBECONFIG`,
|
||||
`KUBECONFIG_RAILIANCE` and the hard-coded `/home/worsch/.kube/...` defaults.
|
||||
- The Makefile's `deploy-dry-run` pointed at `config-hosteurope`, which
|
||||
reaches coulombcore, not railiance01. That was a real wrong-cluster
|
||||
default, now fixed.
|
||||
- Scripts that run inside a `warden access --exec` lane, where `HOME` is the
|
||||
envelope, have no default. The caller passes `NK_KUBECONFIG`, expanded
|
||||
outside the lane. If it is unset, the scripts fail closed: the seed wrapper
|
||||
exits 64, the lease renewal writes `kubeconfig_missing` evidence, and the
|
||||
Python children raise before running kubectl. An empty `--kubeconfig`
|
||||
would fall back to the default route.
|
||||
- **Server-side apply:** the runbook in `docs/bounded-executor-receipts.md` and
|
||||
the Makefile dry-run now use `kubectl apply --server-side`. The live ingress
|
||||
policy was applied client-side on 2026-11-04 and still carries a
|
||||
`last-applied-configuration` annotation. A later server-side apply takes it
|
||||
over, and no live change is made here.
|
||||
|
||||
## Unstale the CCR-2026-0063 seed test
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-11-07-T02
|
||||
status: done
|
||||
priority: low
|
||||
```
|
||||
|
||||
`test_ccr_contract_as_drafted_matches_and_refuses_until_approved` expected the
|
||||
live CCR-2026-0063 to be unapproved. It was approved in railiance-platform
|
||||
f3b0c4f. The refusal case now uses a synthetic `proposed` copy.
|
||||
|
||||
Full suite: 374 passed, 46 skipped, 1 failed. The failure is
|
||||
`test_capability_review_against_live_catalog`, which needs `infospace-bench==0.1.0`
|
||||
installed. It is environmental and fails the same way without these changes.
|
||||
Loading…
Add table
Add a link
Reference in a new issue