Reference
ahena.config.ts
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.
What your app needs from each capability. Committed to git, so it must never hold a secret.
Any value can be given per environment: { development: …, production: … }.
ahena.config.ts runs as code when the CLI loads it, like any TypeScript config file.
No secrets
Ahena refuses to load (and the API refuses to store) a config that contains:
- a value in a known credential format, anywhere: Stripe, Resend, OpenAI, Anthropic, Ahena
(
ahena_ci_…,ahena_cli_…and every other Ahena token), Supabase (sbp_…,sb_secret_…), GitHub, Slack, Google access tokens, AWS key ids, JWTs, private keys, and URLs with a password (postgres://user:pass@host); - any string under a secret-like key name (
secret,token,password,apiKey/api_key,privateKey,secretAccessKey,webhookSecret,clientSecret,credentials,serviceAccount,connectionString, …) unless it's a reference: an environment variable name ("STRIPE_WEBHOOK_SECRET"),"env:NAME","$NAME"/"${NAME}", or a plainhttps://URL (such as an OAuth token endpoint).
Store the value itself with ahena env set <environment> <NAME> (Secrets).
key, lookupKey, publicKey and publishableKey aren't treated as secret names.
import type { AhenaConfig } from "@ahena/config";
export default {
organization: "acme",
project: "leo",
type: "marketplace", // what the app is (set from a recipe)
framework: "nextjs", // detected by ahena init
database: { provider: "supabase" },
auth: {
provider: "supabase",
siteUrl: { production: "https://leo.app" },
redirectUrls: { production: ["https://leo.app/auth/callback"] },
},
storage: {
provider: "cloudflare",
bucket: { development: "leo-dev", production: "leo" },
corsOrigins: { production: ["https://leo.app"] },
},
dns: { provider: "cloudflare", zone: "leo.app" },
email: {
provider: "resend",
domain: { production: "leo.app" },
from: "Leo <hello@leo.app>",
webhook: { production: { endpoint: "https://leo.app/api/webhooks/resend" } },
},
payments: {
provider: "stripe",
webhook: { production: { endpoint: "https://leo.app/api/webhooks/stripe" } },
products: [{ key: "pro", name: "Pro", prices: [{ lookupKey: "pro_monthly", currency: "usd", unitAmount: 1900, interval: "month" }] }],
connect: false,
},
push: { provider: "firebase", ios: { bundleId: "app.leo" }, android: { packageName: "app.leo" } },
ai: { provider: { development: "ollama", production: "openai" }, model: { development: "llama3.2", production: "gpt-5-mini" } },
features: ["auth", "profiles", "subscriptions"],
} satisfies AhenaConfig;
| Key | Provider | Settings |
|---|---|---|
database |
supabase | none (migrations are read from supabase/migrations) |
auth |
supabase | siteUrl, redirectUrls |
storage |
cloudflare (cloudflare-r2) |
bucket, corsOrigins |
dns |
cloudflare | zone, records ({ type, name, content }) |
email |
resend | domain, from, webhook ({ endpoint, events? }) |
payments |
stripe | webhook, products, connect |
push |
firebase | ios.bundleId, android.packageName, iosDir, androidDir |
ai |
openai, anthropic, ollama | model |
features |
none | feature packs added with ahena add |
Other capability keys (sms, jobs, monitoring, deployment) are reserved for future
providers. Settings are read by each provider's intent and used by Doctor (expectations),
ahena diff/configure (desired state) and ahena generate.
Related files: ahena.lock (Stack Blueprint), .ahena/generated.json
(hashes of generated files, so your edits are kept), .env.example (generated blocks).