Core concepts
Doctor
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 doctor [-e production] [--json] [--skip-local] # exit code 1 if anything FAILs
One run per environment. Every finding has an id, provider, environment, title, description, evidence, severity (PASS / INFO / WARNING / FAIL), whether it's fixable, its fix classification (SAFE / CONFIRMATION_REQUIRED / MANUAL / DESTRUCTIVE / BILLABLE), and documentation.
What runs
| Group | Source | Checks |
|---|---|---|
| CONFIGURATION | core | Every capability in ahena.config.ts (email: { provider: "resend" } …) has a working connection in this environment |
| PROJECT / DATABASE / AUTHENTICATION / STORAGE / EMAIL / DNS / WEBHOOKS | providers | Each connected provider's doctor(), given the intent from ahena.config.ts and the repository |
| SECURITY | core | Environment separation: test keys in production, live keys elsewhere, the same secret value in production and another environment (compared only for owners and admins, see environments.md). Values are compared inside core and never returned. |
| SECURITY | CLI (local) | .env is git-ignored, no .env* files committed, no credentials in tracked files (locations only, values never leave your machine) |
Every finding explains itself
Each failure, warning or "not verified" finding answers six questions, in the CLI, the
dashboard and MCP's run_doctor (as explanation):
✕ Production CORS allows every origin (*)
Why: Any website can make browser requests to this bucket.
Where: Cloudflare · Production · bucket: leo-production
Impact: Other websites can use your bucket from their visitors' browsers, and you pay for the traffic.
Ahena can fix it: Yes, after you approve it: ahena doctor --fix
→ Run ahena configure cloudflare with your exact origins.
The text comes from explainCheck in @ahena/doctor: impacts are written per check, not
generated. "Ahena can fix it" follows the fix's classification, and MANUAL means you.
Not verified (ℹ, counted separately in RESULT) means Ahena couldn't check something
because ahena.config.ts doesn't say enough. For example, an imported Stripe setup with no
declared webhook lists the webhook endpoints Stripe has that Ahena doesn't manage. Health
doesn't count these, and the summary says how many there are.
SECURITY also flags production and another environment connected to the same project
(SUPABASE_PROJECT_REF, FIREBASE_PROJECT_ID). It's a failure when viewed from production
and a warning from the other environment.
Health
health = (passed + 0.5 × warnings) / (passed + warnings + failed), as a percentage.
INFO doesn't count. The dashboard shows it per environment.
History and run status
Runs are stored (doctor_runs, doctor_checks) and audited (doctor.environment_run).
The dashboard's Doctor page shows the latest run per environment and can run the
provider and separation checks on demand. Local repository checks need the CLI.
Every run records how it went:
| Status | Meaning |
|---|---|
running |
Started, not finished. Never part of the environment's picture. |
completed |
Every check ran. |
partial |
Finished, but some provider's checks couldn't run (it didn't answer, or its checks failed). That provider gets a FAIL <provider>.doctor (or <provider>.reachability) finding; everything else is kept. error names the providers. |
failed |
Stopped by an error. What it had collected is stored with a short, redacted error, instead of being lost. Never part of the environment's picture, so a failed run can't hide findings. |
Runs also record trigger (manual: dashboard, CLI, CI or an agent; scheduled: continuous
Doctor) and finishedAt. A run left running by a stopped worker is marked failed
("Interrupted") once it's older than 30 minutes.
Continuous Doctor
Ahena runs Doctor and drift on a schedule, per environment, and emails people when something changes. It checks configuration health (credentials, settings, drift, coverage), not whether your app is up.
Settings (dashboard: project → Settings → Monitoring; API below):
| Setting | Default |
|---|---|
| On / off | On, daily, for production; off for every other environment until someone turns it on |
| Schedule | daily or hourly (each next run gets a few minutes of jitter) |
| Who's emailed | the organization's owners and admins, and/or chosen members |
Only owners and admins change production's settings; developers change other environments'.
Viewers, CI tokens and agents (ahena mcp) can't change them: an agent could otherwise
silence the alerts about its own changes. Everyone emailed must be a member when the email is
sent (removed members stop getting them) and have a verified email address.
What a scheduled run does: for each connected provider, it checks that the stored
credentials still work, then runs the provider's Doctor checks with the intent from
ahena.config.ts as the CLI last shared it (the same run as the dashboard's "Run now"); then
capability coverage, required secrets and environment separation, and drift against
ahena.lock. It never changes anything at a provider and never changes connection states.
Secret values are never compared across environments in scheduled runs (that comparison runs
only for owners and admins; their last result still counts). Runs are stored with
trigger: scheduled and audited as system with the organization, project and environment.
Notifications are sent only on change, compared with the previous scheduled run:
- health got worse (healthy → warning or failing, warning → failing), with the new findings' titles;
- new drift (settings changed outside Ahena since
ahena.lock); - a provider rejected Ahena's credentials, or they expired;
- a provider stopped answering;
- recovery: problems before, none now (only from a complete run).
The first scheduled run sets the baseline without emailing. A provider that couldn't be read keeps its earlier drift (unknown, not gone), so an outage doesn't produce "new drift" or a false recovery. Emails contain check titles, setting labels and provider names, and a link to the dashboard: never secret values, drift values or provider responses.
Scheduling: a Durable Object alarm in the API Worker (MonitoringClock) wakes every 15
minutes; it isn't a cron trigger, so it doesn't count toward Cloudflare's per-account cron
limit. It starts on the first request after a deploy, and a failed tick is logged and retried
at the next wake-up. Each tick picks due
environments that have something connected, oldest first, at most 5 per tick and for at most
5 minutes, one at a time; an environment with a run in progress (scheduled or someone's) is
skipped. A scheduled run that can't be stored is retried 15 minutes later.
Billing: scheduled runs don't count as actions. They are read-only and Ahena starts them; a run you start (dashboard, CLI, CI) counts as before.
Local development: the Node API server ticks only when AHENA_DEV_MONITOR_INTERVAL_MS is set
(at least 10000) and never when NODE_ENV=production.
API
POST /v1/orgs/:org/projects/:project/environments/:env/doctor { expectations, required, localChecks }
GET /v1/orgs/:org/projects/:project/environments/:env/doctor/runs
GET /v1/orgs/:org/projects/:project/environments/:env/doctor/runs/:id
GET /v1/orgs/:org/projects/:project/doctor/latest
GET /v1/orgs/:org/projects/:project/monitoring # continuous Doctor settings, every environment
GET /v1/orgs/:org/projects/:project/environments/:env/monitoring
PUT /v1/orgs/:org/projects/:project/environments/:env/monitoring { enabled, schedule: "hourly"|"daily", notifyAdmins, recipients: [userId] }
Runs and run lists include status, trigger, finishedAt and error.
localChecks ids must start with local., and their evidence is redacted before storage.
ahena doctor --fix
ahena doctor -e production --fix # interactive
ahena doctor -e production --fix --yes # approve CONFIRMATION_REQUIRED fixes (production: then in the dashboard)
ahena doctor -e production --fix --yes --allow-billable
- Runs Doctor.
- Collects machine-readable fixes. A finding's
fix.desiredis a fragment of that provider's desired configuration; fragments are merged per provider.fix.generateis a local code fix (e.g. the missing Resend webhook route). - Cross-provider DNS: if Resend's SPF/DKIM/DMARC aren't verified and Cloudflare holds the zone, the required records are added to the Cloudflare plan, and Cloudflare runs before Resend.
- For each provider: plan → diff → approval → apply, exactly like
ahena configure.- SAFE: applied without asking (e.g. asking Resend to re-check DNS, generating code that keeps your edits)
- CONFIRMATION_REQUIRED: asked, or approved by
--yes - BILLABLE / DESTRUCTIVE: their own approval; with
--yesonly if--allow-billable, otherwise skipped and listed - MANUAL: listed under NEEDS YOU with the exact steps
- Then, for changes that matter (anything beyond SAFE in production, billable or destructive
anywhere): approval in the Ahena dashboard, signed in. The CLI opens the link and
waits;
--yesand--allow-billabledon't replace it. Declined or expired: that provider's fixes aren't applied and--fixstops. See approvals.
- Re-runs Doctor and prints before → after.
Server-side safety is unchanged: stale approvals are refused by plan fingerprint, and production changes and billable actions need the right role. A dashboard approval covers exactly the provider, fingerprint and actions it was asked for, and is used once.