Documentation menu

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 plain https:// 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).