diff --git a/scripts/wp0039-seed-worker-tokens.sh b/scripts/wp0039-seed-worker-tokens.sh new file mode 100755 index 0000000..efa6764 --- /dev/null +++ b/scripts/wp0039-seed-worker-tokens.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# ACTIVITY-WP-0039-T03 (option b): mint fresh ops_run worker tokens into OpenBao. +# +# Founder-attended only, through the silent admin lane (orientation section 5): +# +# BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 \ +# warden access openbao-platform-admin-login --exec -- \ +# sh scripts/wp0039-seed-worker-tokens.sh +# +# Silent by design: warden fails closed on any child output. The value is +# generated inside a pipe and sent to bao on stdin, so it never appears in +# argv, output, Git, or the hub. Idempotent: an existing path is never +# overwritten. Rerunning after success is a no-op that re-verifies. +# +# Exit codes: 0 both paths present and verified · 3 a path exists without a +# usable token field (left untouched) · 4 write failed · 5 verification failed. +exec >/dev/null 2>&1 +set -u + +MOUNT=platform +BASE=workloads/activity-core/ops-run-workers +PATHS="rein-aharness-railiance01 rein-aharness-metered-railiance01" + +token_len() { + bao kv get -mount="$MOUNT" -field=token "$BASE/$1" 2>/dev/null | tr -d '\n' | wc -c +} + +for slug in $PATHS; do + if bao kv metadata get -mount="$MOUNT" "$BASE/$slug"; then + [ "$(token_len "$slug")" -eq 64 ] || exit 3 + continue + fi + openssl rand -hex 32 | tr -d '\n' \ + | bao kv put -mount="$MOUNT" "$BASE/$slug" token=- || exit 4 +done + +for slug in $PATHS; do + [ "$(token_len "$slug")" -eq 64 ] || exit 5 +done +exit 0 diff --git a/workplans/ACTIVITY-WP-0039-multi-worker-identity-and-token-custody.md b/workplans/ACTIVITY-WP-0039-multi-worker-identity-and-token-custody.md index 42b4bcd..f6d80dc 100644 --- a/workplans/ACTIVITY-WP-0039-multi-worker-identity-and-token-custody.md +++ b/workplans/ACTIVITY-WP-0039-multi-worker-identity-and-token-custody.md @@ -123,6 +123,24 @@ Founder-attended, through `warden access openbao-platform-admin-login --exec` Done when both paths hold a value and no value has been printed or logged. +**Decision 2026-09-23 (founder): option (b).** Both tokens are minted fresh, so +the hand-generated claim-loop value is retired at cutover. The reviewed script +`scripts/wp0039-seed-worker-tokens.sh` does the minting. It runs silently, never +overwrites an existing path, and exits 0 when both paths are verified, 3 on an +unusable existing path, 4 on a write failure, and 5 on a verification failure. +It generates each value inside a pipe and sends it to `bao kv put ... token=-` +on stdin. The founder runs it in their own 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 -- \ + sh scripts/wp0039-seed-worker-tokens.sh +``` + +Read warden's printed line rather than its exit code (orientation section 5). +Minting does not depend on T02, but ESO cannot read the paths until the policy +lands. + ## Roll out and prove both identities ```task @@ -143,4 +161,17 @@ Prove four things: - A metered token paired with the loop identity is rejected with HTTP 403. - The previous hand-set Secret key is now owned by ESO. +Cutover order for option (b), which keeps the claim loop's gap to seconds. +The pods read `actcore-runtime-secret` only at start. + +1. Apply `15-externalsecret-worker-tokens.yaml`, then force a refresh. The + Secret now holds the new tokens, and the running API still uses the old one. +2. On railiance01, write the new `ACTIVITY_CORE_WORKER_TOKEN` from the Secret + into `~/.config/rein-aharness/claim-loop.env`. Use a go-template read piped + into the file, and never print the value. +3. Apply `ACTIVITY_CORE_WORKERS` to `actcore-runtime-config`, then restart + `deployment/actcore-api`. +4. Restart `rein-aharness-claim-loop.service`, and confirm its first poll + returns HTTP 200. + Report the revision to secrets-engine on threads `914853d9` and `6e694682`.