~/bin/vault is a thin SSH shim into curious@sbl0:~/.vault/vault.sh. Plaintext never lives on sbl1; it stays in age-encrypted .env.age files on sbl0.
Plaintext credentials,
on disk & in logs.
Plaintext credentials on disk, conformance to the sbl0-only vault rule, and exfiltration risk from local logs and backups.
Vault is healthy
The architecture is correct. 5 systems verified — preserve these as you remediate the rest.kimi, kimi-cli, krishna-ask, openclaw-discord-gateway, and every user systemd unit call sbl-secret-env <ENV>=sbl0-<name>. No inline tokens in any service unit or wrapper.
New secrets are encrypted to age1y9qu5gsr8feuraym2682hr3vgqm59f85fslzlr3wppdtjr348eyq5v7nxy before they touch disk on sbl0. The plaintext only exists in RAM on sbl1 and inside the SSH tunnel.
The 'sbl1 is the commit tier' rule is observed at the vault. All live entries are sbl0-*, sbl3-*, sbl4-*, or sbl10-*. Five legacy unprefixed entries remain.
wrangler.jsonc files annotate CF_API_TOKEN as 'set via wrangler secret put' — no inline keys. Repo .gitignore excludes .env, .env.*, .dev.vars, .wrangler/.
Findings
24 findings across 8 categories. Each card declares the file owner and the live consumers so rotation can be sequenced without breaking dependent services.Shell history leak
1 finding~/.bash_history (lines 157-1541)15+ commands echoed literal ghp_* and github_pat_* GitHub PATs (export GITHUB_TOKEN=…, gh auth login --with-token, scp …bashrc, etc.).
Rotation procedure
- Revoke each leaked PAT at github.com/settings/tokens (start with the most recent two).
- gh auth refresh -h github.com (writes a new oauth_token).
- history -c && shred -u ~/.bash_history && touch ~/.bash_history.
Plaintext credential file
11 findings~/.config/gemini/api_key.txtPlaintext AIza* Gemini API key (40 bytes).
Rotation procedure
- Revoke key at Google AI Studio.
- Create new key.
- sbl-secret put sbl0-google-gemini-api-key <new>.
- rm ~/.config/gemini/api_key.txt; have callers fetch via sbl-secret-env.
~/.config/gh/hosts.ymlGitHub oauth_token in plaintext (standard gh CLI location).
Rotation procedure
- gh auth refresh -h github.com -s repo,workflow (issues a new token, revokes old).
- Verify: gh auth status.
- Optionally: sbl-secret put sbl0-github-pat <token from gh auth token>.
~/.config/rclone/rclone.confgdrive OAuth { access_token, refresh_token, expiry } in plaintext.
Rotation procedure
- rclone config reconnect gdrive: (re-runs OAuth, writes new tokens).
- Old refresh_token can be revoked at myaccount.google.com/permissions.
~/.config/gcloud/application_default_credentials.json + legacy_credentials/*/adc.jsongcloud refresh_token + client_secret in plaintext for two identities.
Rotation procedure
- gcloud auth revoke --all.
- gcloud auth login (writes new credentials.db / ADC).
- gcloud auth application-default login if needed.
~/.kaggle/access_tokenKGAT_* Kaggle API key in plaintext (38 bytes).
Rotation procedure
- kaggle.com → Account → Expire API Token, then Create New Token.
- Download replaces the file; alternatively store via sbl-secret put sbl0-kaggle-api-key.
~/.cloudflared/5774c5a1-3631-495b-afca-1daa84563fe7.jsonCloudflare tunnel TunnelSecret + AccountTag in plaintext.
Rotation procedure
- cloudflared tunnel rotate <tunnel-name> — zero-downtime rotation.
- Daemon picks up new <uuid>.json automatically.
~/.cloudflared/cert.pemArgo Tunnel token (-----BEGIN ARGO TUNNEL TOKEN-----).
Rotation procedure
- Only needed if you manage tunnels regularly; can be regenerated with cloudflared tunnel login.
~/.claude/.credentials.jsonAnthropic OAuth access token (sk-ant-oat01-*) in plaintext.
Rotation procedure
- Do this LAST — risks interrupting in-flight remediation.
- claude /logout then claude /login.
- The old access_token is short-lived; the refresh path is re-established.
~/.codex/auth.jsonChatGPT OAuth id_token (JWT) + tokens object in plaintext.
Rotation procedure
- codex logout && codex login.
~/.openclaw/auth-profiles.json (+ agents/krishna/agent/auth-profiles.json + backups/pre-update-*/)Anthropic OAuth token (sk-ant-oat01-*) in plaintext for openclaw.
Rotation procedure
- systemctl --user stop openclaw-gateway krishna-proxy.
- openclaw auth login (writes new auth-profiles.json).
- systemctl --user start krishna-proxy openclaw-gateway.
- Verify: krishna-ask 'hello' replies normally.
~/.antigravity-server/.15487b30…tokenNumeric session token in plaintext.
Rotation procedure
- Re-bootstrap antigravity to issue a new token.
SSH key hygiene
3 findings~/.ssh/id_ed25519Unencrypted private key (no passphrase). Same key authenticates to sbl0, sbl1, sbl2, sbl3, sbl4, sbl10, vast-2x5090, and vast.
Rotation procedure
- Generate ~/.ssh/id_ed25519.new with a passphrase.
- ssh-copy-id -i id_ed25519.new.pub to every fleet host while old key still works.
- Test new key end-to-end against each host before swapping.
- Atomic swap; ssh-add for gcr-ssh-agent.
- Remove old pubkey from each host (sed -i on authorized_keys).
- sbl-secret put sbl0-curious-ssh-ed25519-private < id_ed25519; shred old key.
Reverse order = locks you out of sbl0 = loses vault access.
~/.ollama/id_ed25519Unencrypted private key for ollama identity.
Rotation procedure
- If unused: shred and remove.
- If used: regenerate with passphrase.
~/.local/dev-tls/key.pemUnencrypted OpenSSH-format private key (dev TLS).
Rotation procedure
- If mkcert-managed: re-issue.
- If hand-rolled: regenerate or remove if unused.
Active agent session logs
2 findings~/.claude/history.jsonl + projects/-home-curious/*.jsonlMultiple Gemini AIza* keys and Anthropic sk-ant-oat01-* tokens appear verbatim inside Claude session transcripts (the agent streamed credential files into its context).
Rotation procedure
- Only after the underlying keys are rotated and revoked.
- Per-token sed scrub (use the helper script scrub-history).
- Or, rotate the relevant Claude project IDs and discard the older jsonl files.
~/.codex/sessions/2026/*/*/*.jsonlOlder Codex session rollouts (Feb-Apr 2026) contain hf_*, gsk_*, xai-*, sk-ant-*, AIza* fragments.
Rotation procedure
- Older rollouts are no longer needed for replay — safe to delete after rotation.
- rm -rf ~/.codex/sessions/2026/0[2-4] (after confirming nothing depends on them).
Stale credential dump on disk
2 findings~/workspace/antimony-labs-org-nondashboard-20260423-185551/backups/sbl2-migration/sbl2-backup-wipe-prep-20260404/82 GB full disk dump of sbl2. Contains an unencrypted id_ed25519, .bashrc exporting ANTHROPIC_API_KEY=sk-ant-api03-…, an older .claude/.credentials.json, Codex shell snapshots, 20+ openclaw agent auth-profiles.json files, and Downloads/Chase exports with hf_* tokens.
Rotation procedure
- Pure dead data — safe to delete.
- Move to a quarantine dir first if you want to inspect: mv …/sbl2-backup-wipe-prep-20260404 /tmp/quarantine/.
- If you must keep, re-archive as a single age-encrypted blob on sbl0.
…/sbl2-migration/sbl2-backup-20260418-133603/ (70 MB) + …-133643/ (116 MB)Sibling sbl2 backups; each contains .bash_history + .bashrc with sk-ant-api03-* tokens.
Rotation procedure
- Same as above — safe to delete.
Vault hygiene
3 findingssbl0 vault: github-token, discord-bot-token, cloudflare-global-api-key, contabo-deploy-ssh-key, ssh-ed25519-privateFive unprefixed entries violate the sbl0-* convention. Wrapper scripts already use `sbl0-…|legacy` fallback chains, so these are alive on purpose.
Rotation procedure
- Migrate each: sbl-secret get <legacy> | sbl-secret put sbl0-<scope>-<purpose>.
- Edit wrappers to drop the |legacy fallback once consumers verified.
- sbl-secret delete <legacy>.
~/console/registry/secrets.tsv (5 rows with name starting `sbl1-`)Names 5 sbl1-* secrets (krishna groq/xai, openclaw discord/gateway/ollama). None of these exist in the live vault, but the file documents the forbidden prefix.
Rotation procedure
- Delete the 5 sbl1-* rows from secrets.tsv.
~/bin/openclaw-discord-statusReferences sbl1-openclaw-discord-token + sbl1-openclaw-gateway-token. Both keys are absent from vault, so the script fails at runtime.
Rotation procedure
- Rewrite to use the sbl0-…|fallback pattern used by openclaw-discord-gateway, or delete.
Trust boundary
1 finding~/.ssh/config (vast-2x5090, vast)The same SSH key that authenticates to sbl0 (vault host) is also used to log into rented vast.ai root-on-public-IP instances.
Rotation procedure
- Use a separate key (vast_id_ed25519) when renting vast.ai compute.
- Configure ssh-config with IdentityFile per Host.
Dashboard drift
1 finding~/console/src/data/platform.ts (line 491)platform.ts asserts 'local OpenClaw/Kaggle/GitHub token files removed'. Audit shows ~/.kaggle/access_token, ~/.config/gh/hosts.yml, and ~/.openclaw/auth-profiles.json still exist.
Rotation procedure
- After the underlying files are removed or migrated, update the claim or replace it with a live-derived check.
Staged remediation
Lowest blast-radius first. Each stage is reversible up to the moment a secret is revoked at the provider.Free wins — no rotation needed
blast: nonePure dead data and dead references. No live consumer reads any of these.
- Delete sbl2-backup-wipe-prep-20260404/ (82 GB).
- Delete the two smaller sbl2-migration backups.
- Delete the 5 `sbl1-*` rows from registry/secrets.tsv.
- Fix or delete ~/bin/openclaw-discord-status.
Low-risk API key rotations
blast: single toolVerify the rotation methodology on credentials with one consumer before touching anything cross-cutting.
- Kaggle KGAT_* — only the dashboard wrapper reads it.
- Gemini AIza* — confirm consumers first; replace api_key.txt content via vault-fetched env.
- HuggingFace hf_* if any are live (most appearances were in old backups).
Medium-risk: Cloudflare + Google
blast: deploy scripts; rclone copyCloudflared tunnel rotation is online; gcloud/rclone re-auth is one-shot.
- sbl-secret put new sbl0-cloudflare-api-token, then revoke old in CF dashboard.
- cloudflared tunnel rotate <name> — daemon picks up new secret automatically.
- gcloud auth revoke --all && gcloud auth login.
- rclone config reconnect gdrive:.
GitHub PATs
blast: git push/pull until refresh completesMost painful daily-use disruption; do it deliberately, not at end of day.
- Revoke leaked PATs visible in bash_history (newest first).
- gh auth refresh -h github.com.
- Verify: gh auth status; gh repo list -L 1.
Anthropic / Codex / OpenClaw OAuth
blast: Krishna offline for the openclaw stepEach manager owns its own file; re-auth swaps the token without disrupting other agents.
- claude /logout && claude /login (do this last in the session).
- codex logout && codex login.
- systemctl --user stop openclaw-gateway krishna-proxy → openclaw auth login → restart.
- Re-bootstrap antigravity.
SSH key replacement
blast: one wrong step = locked out of sbl0The vault depends on SSH to sbl0. Deploy new key first, test on every host, then swap.
- ssh-keygen new key with passphrase under ~/.ssh/id_ed25519.new.
- ssh-copy-id new pubkey to sbl0, sbl1, sbl2, sbl3, sbl4, sbl10, vast hosts.
- Verify new key works against every host.
- Atomic swap locally; ssh-add to gcr-ssh-agent.
- Remove old pubkey from every host's authorized_keys.
- sbl-secret put sbl0-curious-ssh-ed25519-private and shred old key.
Vault hygiene
blast: wrapper falls back to legacy until updatedMigrate the 5 unprefixed entries to sbl0-*. Wrappers already have fallback chains, so this is safe iff scripts are updated first.
- Migrate github-token → sbl0-github-pat; update repo_policy.py.
- discord-bot-token already has sbl0-openclaw-discord-token — drop the |fallback in openclaw-discord-gateway.
- Same pattern for cloudflare-global-api-key and contabo-deploy-ssh-key.
- After confirmation: sbl-secret delete <legacy> for each.
Prevention
blast: noneStop the next leak before it happens.
- Add HISTIGNORE='*ghp_*:*sk-ant-*:*AIza*:*hf_*:*gsk_*:*xai-*:*KGAT_*' to .bashrc.
- Add a DEBUG/PROMPT_COMMAND trap that refuses `export *_TOKEN=…` and `gh auth login --with-token <literal>`.
- Audit every project .gitignore for .env, .dev.vars, credentials.json.
- Optional: install gitleaks or pre-commit secret-detection.
Operating principle
The file is the credential, not the master copy. Revoke at provider → re-auth via the manager → verify → delete prior copies and history. The vault is a backup of the rotated value, not the live source.