Problem
This repo has a battle-tested multi-account pattern for GitHub:
github_accounts: {alias: username} map in localhost.yml
play-github-cli-multi.yml generates per-alias bash functions
(gh-<alias>, git-<alias>, clone-<alias>, gh-token-<alias>, …)
- Per-account OAuth scope audit + programmatic
gh ssh-key add
- Auto-detection in a
git() wrapper via git-account-helper
That pattern landed via Plans 00034 and 00035 and is the canonical way
the daily-driver talks to multiple GitHub identities without crossing wires.
We now need the same shape on Cloudflare — multiple accounts, each
needing their own wrangler auth context, with wrangler-<alias>
aliases routing every invocation to the correct account. And the same
pain is coming for npm publish accounts, aws profiles, gcloud configs,
supabase, fly.io, etc.
Proposal
A top-level project_personas map in localhost.yml:
project_personas:
<alias-a>:
name: \"<Display Name A>\"
tools:
gh:
username: <gh-username-a>
wrangler:
account_id: \"<cloudflare-account-id-a>\"
account_name: \"<CF Display Name A>\"
<alias-b>:
name: \"<Display Name B>\"
tools:
gh:
username: <gh-username-b>
# no wrangler — this persona has no Cloudflare account
Each per-tool playbook reads that map directly, filters to personas
declaring its tool, and generates the bash function family in the
established `-` shape.
KISS migration: fail-fast, no compat shim
`play-github-cli-multi.yml` is updated to consume
`project_personas` directly. If it sees only the legacy
`github_accounts` map, it fail-fasts with a copy-pasteable
migration YAML — the user makes the one-line edit. No two
sources of truth, no silent derivation, no `set_fact`
gymnastics. The fail-fast message IS the migration guide.
Wrangler: explicit env-var injection + GNOME Keyring secrets
Wrangler is fundamentally unlike gh — it has no per-config-dir
OAuth, so the `-` model uses explicit env-var
export per invocation:
function wrangler-<alias>() {
local token
if ! token=\$(secret-tool lookup persona <alias> tool wrangler attr api-token 2>/dev/null); then
echo \"ERROR: no wrangler API token stored for persona '<alias>'.\" >&2
echo \"Run: persona-setup.bash --set-token=<alias> wrangler\" >&2
return 1
fi
CLOUDFLARE_API_TOKEN=\"\$token\" \\
CLOUDFLARE_ACCOUNT_ID=\"<from project_personas>\" \\
command wrangler \"\$@\"
}
Secret storage: tokens live in the GNOME Keyring via
`secret-tool` — encrypted at rest, unlocked at GNOME login (no
per-call password prompt), per-user isolation enforced by the
keyring daemon, never written to disk in plaintext, never on
the command line where `ps` could see them. Same mechanism
`gh` itself uses on this machine.
Alternatives considered and rejected: plaintext 0600 files
(backups grab plaintext), `pass`/GPG (extra dep), ansible-vault
(awkward at bash-function call time, `vault-pass.secret` on
disk anyway).
Decision gate
Phase 1 is a research gate. The remaining unknowns:
- Wrangler API token scopes — confirm minimum scopes for
Workers / Pages / KV / R2 / D1.
- `secret-tool` availability — confirm libsecret is
reliably present and the keyring is unlocked at function
call time. Fallback to 0600 files if not.
Implementation phases are blocked on user approval after Phase 1.
Plan
Full plan with phases, tasks, technical decisions (incl.
Decision 4 on secret storage), and success criteria lives at
`CLAUDE/Plan/00045-project-personas-multi-tool-accounts/PLAN.md`
(committed once user approves).
Out of scope
- npm / aws / gcloud / supabase / fly support (follow-up plans
each using this plan's persona schema + keyring pattern)
- Renaming SSH keys (`~/.ssh/github_` stays)
- GUI/wizard (CLI-only, consistent with `run.bash`)
- Touching `play-cloudflare-warp.yml` (that's the VPN client,
unrelated to wrangler)
Problem
This repo has a battle-tested multi-account pattern for GitHub:
github_accounts: {alias: username}map inlocalhost.ymlplay-github-cli-multi.ymlgenerates per-alias bash functions(
gh-<alias>,git-<alias>,clone-<alias>,gh-token-<alias>, …)gh ssh-key addgit()wrapper viagit-account-helperThat pattern landed via Plans 00034 and 00035 and is the canonical way
the daily-driver talks to multiple GitHub identities without crossing wires.
We now need the same shape on Cloudflare — multiple accounts, each
needing their own
wranglerauth context, withwrangler-<alias>aliases routing every invocation to the correct account. And the same
pain is coming for npm publish accounts, aws profiles, gcloud configs,
supabase, fly.io, etc.
Proposal
A top-level
project_personasmap inlocalhost.yml:Each per-tool playbook reads that map directly, filters to personas
declaring its tool, and generates the bash function family in the
established `-` shape.
KISS migration: fail-fast, no compat shim
`play-github-cli-multi.yml` is updated to consume
`project_personas` directly. If it sees only the legacy
`github_accounts` map, it fail-fasts with a copy-pasteable
migration YAML — the user makes the one-line edit. No two
sources of truth, no silent derivation, no `set_fact`
gymnastics. The fail-fast message IS the migration guide.
Wrangler: explicit env-var injection + GNOME Keyring secrets
Wrangler is fundamentally unlike gh — it has no per-config-dir
OAuth, so the `-` model uses explicit env-var
export per invocation:
Secret storage: tokens live in the GNOME Keyring via
`secret-tool` — encrypted at rest, unlocked at GNOME login (no
per-call password prompt), per-user isolation enforced by the
keyring daemon, never written to disk in plaintext, never on
the command line where `ps` could see them. Same mechanism
`gh` itself uses on this machine.
Alternatives considered and rejected: plaintext 0600 files
(backups grab plaintext), `pass`/GPG (extra dep), ansible-vault
(awkward at bash-function call time, `vault-pass.secret` on
disk anyway).
Decision gate
Phase 1 is a research gate. The remaining unknowns:
Workers / Pages / KV / R2 / D1.
reliably present and the keyring is unlocked at function
call time. Fallback to 0600 files if not.
Implementation phases are blocked on user approval after Phase 1.
Plan
Full plan with phases, tasks, technical decisions (incl.
Decision 4 on secret storage), and success criteria lives at
`CLAUDE/Plan/00045-project-personas-multi-tool-accounts/PLAN.md`
(committed once user approves).
Out of scope
each using this plan's persona schema + keyring pattern)
unrelated to wrangler)