rotate AI_GATEWAY_TOKEN and WORKER_GITEA_TOKEN — both were printed into an operator transcript #159

Open
opened 2026-08-23 14:24:05 +02:00 by ltms · 0 comments
Owner

Operational task, not a code defect. Both values are still live.

What happened

On 2026-08-23 I printed two credentials in full into an operator transcript, from two different commands, neither of which looks like a command that touches secrets.

1. AI_GATEWAY_TOKEN — I used ${VAR:-UNDEFINED} to test whether the variable was set. That form expands to the value when the variable is set, so it printed the token instead of reporting presence.

The safe form, which is what scripts/redeploy-bridged.sh already uses:

[ -n "$V" ] && echo "set (${#V} chars)"

Never ${V:-...} and never ${V:+...$V} for a presence check.

2. WORKER_GITEA_TOKEN — I ran git remote -v while surveying fleet01. The token is embedded in the kb clone's https remote URL, so a routine inspection command printed it. That leak path is a defect in its own right and is filed as #157.

What to rotate

variable scope where it lives
AI_GATEWAY_TOKEN all of llm.ltms.dev — every gateway-backed profile on both hosts Mac ${SHARED_ENV}/tools/secrets.sh; fleet01 ~/.fleet/secrets.sh
WORKER_GITEA_TOKEN repo-scoped forge token (repo.code/issues/pulls = write via the agents team) same two files, plus ~/LTMS/kb/.git/config on fleet01 and both worktrees under ~/LTMS/.fleet-worktrees/

AI_GATEWAY_TOKEN is the wider exposure of the two: it is the single key for the gateway and is used by gx, xf, sol, terra and local across both hosts.

Order of operations

Rotating WORKER_GITEA_TOKEN alone will break worker pushes on fleet01 until the embedded remote URLs are updated too. So do #157 in the same pass, or accept a window where pushes fail:

  1. Mint both new values.
  2. Update the Mac's store and fleet01's ~/.fleet/secrets.sh.
  3. Fix the git remotes (#157) — switch to ssh, which removes the embedded credential rather than replacing it.
  4. Restart both daemons from a login shell, or they keep the old values: the daemon reads its environment once, at start. scripts/redeploy-bridged.sh --check reports whether each named variable resolves, without printing it.
  5. Revoke the old forge token in Gitea.
  6. Prove it: a worker opens a PR (forge token), and a gateway-backed profile spawns and answers (gateway token). A green /healthz proves neither.

Also worth doing while here

GITEA_ACCESS_TOKEN — the Mac-only Gitea admin token — has been on the "should be rotated" list since the Release 1 close-out and has not been done. It was not exposed here, but it is the highest-value credential in the system and it is overdue.

Prevention

Two rules, both cheap:

  • Presence checks use [ -n "$V" ] && echo "set (${#V} chars)". Nothing else.
  • Any host-survey output is piped through a redactor before it is run, not after it surprises someone. git remote -v, git config --list, git remote show, and anything dumping .git/config are credential commands whenever an https remote is in use.
Operational task, not a code defect. Both values are still live. ## What happened On 2026-08-23 I printed two credentials in full into an operator transcript, from two different commands, neither of which looks like a command that touches secrets. **1. `AI_GATEWAY_TOKEN`** — I used `${VAR:-UNDEFINED}` to test whether the variable was set. That form expands to the **value** when the variable is set, so it printed the token instead of reporting presence. The safe form, which is what `scripts/redeploy-bridged.sh` already uses: ```bash [ -n "$V" ] && echo "set (${#V} chars)" ``` Never `${V:-...}` and never `${V:+...$V}` for a presence check. **2. `WORKER_GITEA_TOKEN`** — I ran `git remote -v` while surveying `fleet01`. The token is embedded in the kb clone's https remote URL, so a routine inspection command printed it. That leak path is a defect in its own right and is filed as **#157**. ## What to rotate | variable | scope | where it lives | |---|---|---| | `AI_GATEWAY_TOKEN` | all of `llm.ltms.dev` — every gateway-backed profile on both hosts | Mac `${SHARED_ENV}/tools/secrets.sh`; fleet01 `~/.fleet/secrets.sh` | | `WORKER_GITEA_TOKEN` | repo-scoped forge token (`repo.code/issues/pulls = write` via the `agents` team) | same two files, **plus** `~/LTMS/kb/.git/config` on fleet01 and both worktrees under `~/LTMS/.fleet-worktrees/` | `AI_GATEWAY_TOKEN` is the wider exposure of the two: it is the single key for the gateway and is used by `gx`, `xf`, `sol`, `terra` and `local` across both hosts. ## Order of operations Rotating `WORKER_GITEA_TOKEN` alone will break worker pushes on `fleet01` until the embedded remote URLs are updated too. So do #157 in the same pass, or accept a window where pushes fail: 1. Mint both new values. 2. Update the Mac's store and `fleet01`'s `~/.fleet/secrets.sh`. 3. Fix the git remotes (#157) — switch to ssh, which removes the embedded credential rather than replacing it. 4. Restart both daemons **from a login shell**, or they keep the old values: the daemon reads its environment once, at start. `scripts/redeploy-bridged.sh --check` reports whether each named variable resolves, without printing it. 5. Revoke the old forge token in Gitea. 6. Prove it: a worker opens a PR (forge token), and a gateway-backed profile spawns and answers (gateway token). A green `/healthz` proves neither. ## Also worth doing while here `GITEA_ACCESS_TOKEN` — the Mac-only Gitea **admin** token — has been on the "should be rotated" list since the Release 1 close-out and has not been done. It was not exposed here, but it is the highest-value credential in the system and it is overdue. ## Prevention Two rules, both cheap: - Presence checks use `[ -n "$V" ] && echo "set (${#V} chars)"`. Nothing else. - Any host-survey output is piped through a redactor **before** it is run, not after it surprises someone. `git remote -v`, `git config --list`, `git remote show`, and anything dumping `.git/config` are credential commands whenever an https remote is in use.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#159