Providers
Switching providers is a migration, not a config edit
Why changing the provider behind a capability means moving data and code, and exactly what ahena switch changes, checks and leaves for you.
In short
Changing the provider behind a capability looks like a one-line edit, but the data, the provider-specific setup and any code that bypasses your abstraction don't move with it. ahena switch treats it as a migration: it finds code that bypasses the generated interface, classifies the switch (compatible, partially compatible, manual migration or unsupported), connects and checks the new provider, updates ahena.config.ts, regenerates the adapter and writes a migration report. It never moves data, and it never disconnects or deletes the old provider.
Why it isn't a config edit
Most apps describe their providers in configuration: an environment variable, a provider name in a config file. That makes switching look like a one-line change. For some capabilities it nearly is. For most, the line is the smallest part of the work, because three things don't move when the line does:
- Data. Rows in a database, users and their sessions in an auth service, files in a bucket, customers and subscriptions at a payment provider. They live at the old provider until someone moves them.
- Provider-specific setup. A new email provider needs its own domain verification and DNS records, its own webhooks, and has its own suppression list. Rate limits, model behaviour and pricing differ even between providers that look interchangeable.
- Code that bypasses the abstraction. Wherever the app imports the old provider's SDK directly, or reaches for a provider-specific feature, the abstraction leaks and the code has to change.
Treating a switch as a config edit is how a team ends up with an app pointed at an empty database, or sending email from a domain the new provider never verified. Treating it as a migration means listing those three things before anything changes.
Why there's no proxy to flip
Some platforms offer "switch providers without code changes" by routing your traffic through themselves. Ahena deliberately doesn't: it's a control plane, not a runtime proxy, and your app calls each provider directly through generated code built on the provider's official SDK (see Control plane vs runtime proxy). The consequence, stated in Ahena's architecture decision record, is that switching needs code changes instead of a proxy flip. The generated adapters keep those changes small, and ahena switch does the mechanical parts. A proxy flip wouldn't move your data either; it would only hide that it hadn't moved.
What ahena switch does
ahena switch ai ollama openai -e development
ahena switch ai openai anthropic -e production --dry-runahena switch <capability> <from> <to> works through these steps:
- Finds where
ahena.config.tsuses the old provider for the capability, in every environment or only the one you pass with-e. - Scans your code for anything that bypasses the common interface, such as
ahena.ai.rawor direct imports of the old provider's SDK, and reports each one asfile:line. - Classifies the switch (below) and lists the data that stays with the old provider.
- Asks before changing anything.
--dry-runstops here. - Connects and verifies the new provider where it isn't connected yet. Credentials come from you, as always.
- Updates
ahena.config.ts, per environment if only some environments move. For AI it also sets the new provider's default model (choose another with--model). - Regenerates the adapter and the environment schema (
.env.exampleand the generated env module). - Runs Doctor against the new provider and, with
--test, your project's test script. - Writes a migration report to
docs/ahena/switches/: what was done, what's left, the data that didn't move, verification and rollback.
The report is the part to keep. It's the checklist for the work a tool can't do, committed next to the code it concerns.
How a switch is classified
| Class | Meaning | Today |
|---|---|---|
| Compatible | The app only uses the common interface; the new adapter is a drop-in. Still test behaviour, limits and cost. | AI (OpenAI, Anthropic, Ollama) |
| Partially compatible | The adapter is generated, but some code or setup is manual. | AI with direct SDK use; email (domain verification, webhooks, suppression lists) |
| Manual migration | Data lives at the old provider and must be moved. Ahena switches config and code, never the data. | Database, auth, storage, payments, push |
| Unsupported | Ahena can't connect the target provider yet. Nothing is changed. | Any provider Ahena doesn't ship |
"Compatible" is the strongest claim Ahena makes, and it only makes it for AI, where the generated interface covers what most apps use. Even then, a different model answers differently, has different limits and costs differently. Ahena never calls a switch effortless when data or application changes are required.
Note what "unsupported" covers today. Ahena manages one provider per capability for most capabilities (Supabase for database and auth, Cloudflare R2 for storage, Resend for email, Stripe for payments, Firebase for push), and three for AI. A switch to a provider outside that list, such as from Resend to another email service, is classified unsupported and changes nothing.
What moves, and what stays behind
What Ahena changes in a switch:
ahena.config.ts, for the environments that move;- the generated adapter and environment schema;
- the new provider's connection, where one was needed.
What Ahena never does:
- move data (rows, users, files, customers, subscriptions, device registrations);
- rewrite your code that bypasses the interface (it lists each place instead);
- disconnect or delete the old provider. Disconnecting is yours to do once you're done, and even then a disconnect in Ahena only removes the credential Ahena stored. It never deletes anything at the provider.
That last rule is what makes a switch reversible. Both providers stay connected and intact until you decide otherwise.
Example: a different AI provider per environment
AI is where switching is closest to a config edit, and where Ahena supports mixing providers by environment. A common setup runs a local model in development and a hosted one in production:
ai: {
provider: { development: "ollama", production: "openai" },
model: { development: "llama3.2", production: "gpt-5-mini" },
},The generated code exposes one interface, ahena.ai.generate(), with an adapter per provider present, and the AI_PROVIDER environment variable picks one at runtime. Calls go straight from your app to the provider. To move production to Anthropic, preview the switch first:
ahena switch ai openai anthropic -e production --dry-run
ahena switch ai openai anthropic -e production --testIf the app only calls ahena.ai.generate(), the switch is compatible. If it uses ahena.ai.raw or imports the OpenAI SDK directly, it's partially compatible, and the scan lists each place to change. Either way, test the prompts that matter to you against the new model before shipping.
Rolling back
Roll back with the reverse switch: ahena switch ai anthropic openai -e production. Because the old provider was never disconnected, its connection, its resources and its data are where you left them. When you're confident in the new provider, disconnect the old one yourself. Disconnecting deletes the credential Ahena stored and tells you how to revoke the key at the provider, which Ahena can't do for you.
Try it on your own stack
Start free with one project. Connect the providers you already use, run Doctor, and see the plan before anything changes.