Guides
Generated integration code
Uses the Ahena CLI (beta). Install it with npm install -g @ahena/cli, or prefix commands with npx @ahena/cli. See Installation. The dashboard covers projects, connections, Doctor, the Stack Graph, plans and approvals without it.
ahena generate [-e development] [--framework nextjs|node] [--dry-run] [--force]
ahena generate <provider> # just one provider
Writes readable TypeScript into your app for every provider connected to the environment:
src/ahena/
client.ts # import { ahena } from "./ahena/client"
providers/<provider>.ts # SDK client setup, keys from env
database/ auth/ # Supabase
storage/ # Cloudflare R2 (S3-compatible)
email/ # Resend
generated/
config.ts # project + providers (no secrets)
env.ts # AHENA_ENV, missingEnv(), assertEnv()
src/app/api/webhooks/resend/route.ts # with --framework nextjs
.env.example # one block per provider, merged in place
.ahena/generated.json # hashes of what Ahena wrote (commit this)
Where generated files may go
Generated files come from the Ahena API, so the CLI only writes paths the generators actually emit and refuses everything else before writing anything:
src/ahena/**(.ts,.tsx,.sql,.md,.json).env.exampledocs/ahena/<pack>.md(feature packs)src/app/api/webhooks/{stripe,resend}/route.tsorapp/api/webhooks/…(webhook routes)src/app/api/ahena/**/route.tsorapp/api/ahena/…(feature-pack routes)GoogleService-Info.plistandgoogle-services.jsonin the configurediosDir/androidDir
Absolute paths, .., any dot-directory or dotfile other than .env.example (.git/,
.github/, .npmrc, .env), node_modules/, package.json and any other path are refused
with "Refusing to write …". Feature-pack migrations are written by the CLI from the packs it
ships (supabase/migrations/<timestamp>_ahena_<pack>.sql), not from API responses.
import { ahena } from "./ahena/client";
await ahena.email.send({ to: user.email, subject: "Welcome", text: "Hi!" });
await ahena.storage.upload("avatars/1.png", bytes, "image/png");
const { data } = await ahena.database.from("profiles").select("*"); // anon role on the server
const s3 = ahena.storage.raw; // escape hatch: the provider's own client
Runtime
The generated code calls each provider directly with its official SDK.
ahena.email.send() → Resend. Nothing goes through Ahena's servers, and an Ahena outage
doesn't affect your app.
Supabase sessions on the server
In the browser, the generated Supabase layer keeps one shared client that holds the user's session.
On the server, every supabase() call returns a new client that never stores a session, so one
request's sign-in can't leak into another request and row level security never runs as the wrong
user. To act as the signed-in user on the server, pass their access token:
supabaseForRequest(accessToken).from("profiles"), ahena.auth.getUser(accessToken). For Next.js
cookie-based sessions use @supabase/ssr, which Ahena doesn't install. See
providers/supabase.md.
Regenerating safely
- Unchanged files are left alone.
- A file you edited since the last generate is kept, and the CLI tells you.
--forcereplaces it. .env.exampleblocks between# --- ahena:<provider> …and# --- /ahena:<provider> ---are replaced. Everything outside them is yours.- Generated code never contains secrets. Values come from your environment, and
assertEnv()fails fast with the missing names.
Verified
All eight generators' output is typechecked against the real provider SDKs in CI. Supabase,
Cloudflare, Resend and Stripe, plus a usage file in the style above, are checked in
packages/cli/test/generate.test.ts. Firebase, OpenAI, Anthropic and Ollama are checked in their
own provider suites.
The generated code is also run, with the real SDKs and fakes only at the network boundary:
- Validation flow E (
packages/cli/test/validation/e-offline.test.ts) runs the generated Supabase, Resend and Ollama code with Ahena stopped. packages/cli/test/generated-runtime.test.tsbundles the generated layer and runs Cloudflare R2 (upload, download, missing key, delete, presigned upload/download URLs) through the AWS SDK, Firebase Cloud Messaging throughfirebase-admin(a signed token request, thensend), and the OpenAI and Anthropic adapters behindahena.ai(including a refusal). It posts signed, unsigned, tampered, wrong-secret and stale requests to the generated Stripe and Resend webhook routes: a valid signature is handled (204), anything else gets 400 (Stripe) or 401 (Resend), and a missing secret gets 500.- The Supabase suite also runs the generated layer against a fake Supabase and checks that two server callers never share a session.
Not yet run: Stripe Checkout and billing portal calls (the Stripe SDK's own HTTP client is only constructed), and the generated Supabase auth helpers against a real auth server.