Why We Built cvx
Juggling a personal Convex account, a work account, and client accounts meant constant logout/login or leaving deploy keys lying around in files. cvx switches accounts by folder, automatically.
By the time we had EnvPilot, Wryte, and OurTrail all running on Convex, plus a couple of client projects using their own Convex accounts, I was logging out and back into the Convex CLI several times a day. Personal account for one project, work account for another, a client's account for the third — and the CLI's answer to "which account am I on" was whatever I'd last logged into globally, with no per-project memory of it at all.
The workaround everyone reaches for is deploy keys — generate one per project, drop it in an env var, skip the login flow entirely. That works until you actually look at where those keys end up: .env files that get git add'd by accident, shell history, Slack messages ("here's the deploy key for staging"), sitting around in plaintext far longer than anyone intends. I'd built an entire product, EnvPilot, around the idea that secrets shouldn't live loosely in files and chat — and here I was doing exactly that with Convex deploy keys, because the CLI gave me no better option for a problem this specific.
What existed, and why it wasn't enough
The Convex CLI itself supports multiple accounts, but the model is global-login, not per-project. npx convex login authenticates you, on that machine, full stop — there's no built-in concept of "this folder belongs to this account," so switching projects means switching your entire CLI identity, every time, even for a thirty-second convex dev in a different repo.
Deploy keys solve the "don't want to log in interactively" problem but trade it for a worse one — a long-lived credential sitting in a file, with no rotation prompt, no expiry nudging you to regenerate it, and every incentive to just leave it there once it works. For personal/hobby projects that's a minor risk. For client work, where a leaked deploy key means someone else's production database, it's not a risk I was willing to keep taking just to save myself a login prompt.
I looked for a tool that did what direnv does for environment variables, but for Convex accounts specifically — bind an account to a folder, once, and have it just work from then on. Nothing existed, so I wrote it.
What we built
cvx (also known as convex-switch) is a CLI that binds a folder to a specific Convex account, once, and then activates the right account automatically whenever you cd into that folder.
A zsh cd-hook does the switching. Once a folder is bound, cvx hooks into your shell's chpwd behavior so that moving into a bound directory silently activates the matching account before your next convex command runs. There's no "remember to switch accounts" step because there's no step — the mechanism is the same one nvm or rbenv use for per-directory tool versions, applied to Convex account identity instead.
Tokens live in an encrypted local vault, not in project files. cvx stores auth tokens itself, encrypted, outside the project directory entirely — nothing account-related ends up in a repo, an env file, or anything that could get committed or pasted somewhere by accident. The project folder holds a reference to which account it's bound to; the credential itself lives in the vault.
One tool, one job. cvx doesn't try to replace the Convex CLI or wrap convex dev — it manages account context and gets out of the way. You still run convex commands exactly as documented; cvx's only responsibility is making sure the account underneath them is the right one before you run them.
It ships with a small mascot, Vex, mostly because a CLI that quietly swaps your auth identity in the background benefits from some visible confirmation that it did the right thing, and a one-line "switched to: client-acme" with a bit of personality is more reassuring than silence.
Install is via Homebrew, npm, or bun — whichever fits the project you're already in.
The honest tradeoffs
It's zsh-first, and that's a real limitation, not a preference. The cd-hook mechanism is built around zsh's chpwd hooks. Bash and fish support is on the list but isn't there from day one, which means the automatic-switching behavior — the actual point of the tool — doesn't work out of the box for everyone. This was a scope call: get the zsh path solid rather than build a mediocre version for three shells at once.
Folder-bound accounts assume one account per project, which is usually but not always true. If you genuinely need to switch accounts within the same folder for different tasks, cvx's model doesn't fit that well — it's built around "this repo belongs to this account," not "let me pick per-command." For the actual problem we had — permanent project-to-account mapping — that's the right shape; for more dynamic multi-tenant workflows, it might not be.
Storing tokens locally, even encrypted, is still a tradeoff versus not storing them at all. We chose local encrypted storage over re-authenticating via the browser every time, because the entire motivation was reducing friction on account switching. That's a deliberate bet that a well-encrypted local vault is safer in practice than the alternative most people were actually doing — bare deploy keys in .env files — even though "nothing stored anywhere" would be the theoretically safest option if it weren't so annoying that nobody would use it.
Where it's useful
If you only ever touch one Convex account, cvx solves a problem you don't have. If you're bouncing between a personal project, a work account, and one or more client accounts — which describes most of the small teams and freelancers we talked to before building this — it removes the specific daily friction of remembering which account you're supposed to be on and paying for that mistake with either a failed deploy or, worse, a successful deploy to the wrong account.
It's open source under the MIT license, and issues, shell support, and feature requests are welcome at rafay99-epic/convex-switch.
Ask AI