Documentation menu

Providers

Neon

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.

Overview

Ahena works with a Neon project you own: it checks the project, gives each environment its own branch, creates the databases and roles your app needs, keeps compute settings in line with ahena.config.ts, and generates a database layer on @neondatabase/serverless. Your app connects to Neon directly with DATABASE_URL. Ahena is never in the query path.

Package: @ahena/provider-neon · Category: database · API: Neon API v2 (console.neon.tech/api/v2) Capabilities: postgres, branches, databases, roles, compute, environment-separation

Maturity: verified against a fake of the API; not yet verified against the real service.

Connection

NEON_API_KEY (secret): a Neon API key. A project-scoped key is recommended. NEON_PROJECT_ID (setting): the project id, e.g. cool-river-123456. If you leave it out, Ahena lists the projects the key can reach and you pick one.

NEON_API_KEY=… ahena connect neon -e production --set NEON_PROJECT_ID=…

Network: only console.neon.tech (enforced).

Permissions

Neon has three kinds of API key: personal (everything your user can reach), organization (every project in the organization) and project-scoped (one project, Editor access). Ahena needs Editor access to one project, so a project-scoped key is the least privilege available.

Neon API keys can't report their own permissions, so Ahena's check is authentication-only: validate() reads the project and GET /auth, and reports the key kind (api-key:personal, api-key:organization; project-scoped keys report as organization keys) and, when Neon returns it, your project permission (project:viewer|editor|admin). A Viewer is told at connect that changes will fail. Other gaps show up as a 403 in Doctor or apply.

A project-scoped key can't create projects, mint API keys, read other projects, delete its project or manage who can access it. It can change and delete everything inside the project; Ahena never uses it to delete anything.

Capabilities

Capability Access Verification Changes Refuses
postgres read-only
branches writable read-back BILLABLE deleting branches, resetting or restoring a branch, changing protected-branch settings
databases writable read-back CONFIRMATION_REQUIRED deleting databases
roles writable read-back CONFIRMATION_REQUIRED deleting roles, revealing or resetting role passwords
compute writable read-back BILLABLE deleting or disabling computes
environment-separation read-only

Inspect shows the project (region, Postgres version, plan, history retention, default compute settings), every branch (default, protected, state, parent), databases (name, owner), roles (names only), and computes (type, state, autoscaling min/max CU, suspend timeout, host). Connection metadata is the host and database name only. Ahena never calls Neon's reveal-password endpoint and never returns a connection string with a password; the connection strings in Neon's create-branch response are dropped where the response is parsed.

ahena lock records the region, Postgres version, branches, and for the environment's branch its protection, databases, roles and compute settings, for drift.

Configure

database: {
  provider: "neon",
  project: "…",                                       // optional; must match NEON_PROJECT_ID
  region: "aws-us-east-2",                            // optional; checked, never changed
  branch: { production: "main", development: "dev" },
  databases: ["leo"],
  roles: ["leo_app"],
  owner: "leo_app",                                   // optional; defaults to the first role
  compute: { production: { minCu: 0.25, maxCu: 2, suspendTimeoutSeconds: 300 } },
},

branch and compute can be one value or keyed by environment. Each environment's plan works on its own branch.

Change Classification Notes
Create the environment's branch from the default branch, with one read-write compute BILLABLE Its compute runs on CU-hours (billed on paid plans, the monthly allowance on Free); a branch beyond the plan's allowance is billed per branch-month; its storage is billed as it diverges. The plan shows a cost notice with the current branch count.
Create a role on the branch CONFIRMATION_REQUIRED Neon returns the password once. Ahena stores it encrypted as NEON_ROLE_PASSWORD_<ROLE>, and for the owner role also DATABASE_URL (role, password, compute host, first database, sslmode=require). Neon drops open connections to the compute while it applies a role.
Create a database on the branch, owned by owner CONFIRMATION_REQUIRED
Change the branch's compute (min/max CU, suspend timeout) BILLABLE Only the fields that differ are sent.

A new branch starts with copies of its parent's databases and roles, so the plan compares with the parent's and doesn't re-create them. Neon applies changes asynchronously: after each write Ahena polls the operations it started (bounded backoff, about 30 seconds) before the next write and before the re-read. A 423 Locked (another operation still running) is retried with bounded backoff, as Neon documents it safe to retry. An operation still running after the wait is reported as applied, and the re-read decides whether it's verified.

Applying is idempotent: each action re-reads first and only creates what's missing. A role that already exists (for example after a lost response) is never duplicated, and no password is stored for it, because Neon only returns a password at creation.

Refused, with the reason: deleting branches, databases, roles or projects; resetting or restoring a branch; changing protected-branch settings; reading role passwords; a region other than the project's (Neon can't move projects); a project that isn't the connected one.

Generate

ahena generate neon:

  • src/ahena/database/index.ts: database.sql (tagged template, values sent as parameters), database.query(text, params), database.transaction(...) and database.raw, using neon(process.env.DATABASE_URL) from @neondatabase/serverless
  • .env.example.neon: DATABASE_URL= (copy the value with ahena env reveal <environment> DATABASE_URL)

The generated code is typechecked against @neondatabase/serverless in Ahena's tests.

Limitations

  • Doesn't create or delete projects, and can't move a project to another region.
  • Doesn't add a compute to an existing branch that has none (Doctor shows the steps); a new branch is created with one.
  • Doesn't protect branches, set IP allow lists, or change project settings such as history retention or logical replication.
  • A role's password is only available when Ahena creates the role. For an existing role, set DATABASE_URL yourself (ahena env set <environment> DATABASE_URL).
  • No schema migrations: use your migration tool against DATABASE_URL.
  • No read replicas, Neon Auth, Data API, snapshots or object storage.
  • Neon API keys can't be revoked by Ahena; revoke them in the Neon Console.

Manual steps

  1. Protect the production branch (paid plans): Neon Console → the project → Branches → the branch → Set as protected.
  2. A branch without a read-write compute: Branches → the branch → Add compute → Read-write.
  3. An existing app role: copy its connection string from the Neon Console (Connect) into ahena env set production DATABASE_URL.

Doctor checks

Id Severity Meaning
neon.project PASS / FAIL The key works and the project is reachable.
neon.key INFO A personal key in production; a project-scoped key would limit exposure.
neon.project.declared FAIL The connected project isn't the one ahena.config.ts names.
neon.project.region PASS / WARNING The project is in the declared region (MANUAL fix: Neon can't move it).
neon.branch PASS / INFO / WARNING / FAIL The environment's branch exists and is ready; missing is FAIL with a BILLABLE fix, init/resetting is WARNING, archived is INFO (WARNING in production).
neon.branch.undeclared INFO No branch declared for the environment, so nothing to check.
neon.branch.protected PASS / INFO / WARNING Production branch protected; WARNING on paid plans, INFO on Free (not available).
neon.branch.separation PASS / WARNING A non-production environment uses the production branch (or the default branch when production's isn't declared).
neon.roles PASS / FAIL Declared roles exist on the branch (CONFIRMATION_REQUIRED fix).
neon.databases PASS / FAIL Declared databases exist on the branch (CONFIRMATION_REQUIRED fix).
neon.compute PASS / FAIL The branch has a read-write compute that isn't disabled.
neon.compute.suspend INFO Scale-to-zero timeout and autoscaling range; scale to zero off means always billed.
neon.compute.size PASS / WARNING Compute settings match ahena.config.ts (BILLABLE fix).
neon.secret.database_url PASS / FAIL DATABASE_URL stored in the environment when databases are declared (checked by name).

Disconnect behavior

Removes the connection and the key Ahena stored. Revoke the key in the Neon Console (Settings → API keys → Revoke). Branches, databases, roles, computes and data are untouched.

Set up step by step

  1. Create the key. In the Neon Console, switch to your organization, open Settings → API keys → Create new, choose Project-scoped and pick the project. Copy the key; Neon shows it once. (Only organization admins can create project-scoped keys; others can use a personal key from Account settings → API keys.)

  2. Minimum access. Editor access to the one project. A project-scoped key has exactly that.

  3. Connect.

    NEON_API_KEY=… ahena connect neon -e development --set NEON_PROJECT_ID=…
    NEON_API_KEY=… ahena connect neon -e production --set NEON_PROJECT_ID=…

    Pipe the key or type it at the hidden prompt; never pass it as an argument.

  4. Verify the connection. ahena inspect neon -e development lists the project, branches, databases, roles and computes.

  5. Run Doctor. ahena doctor -e production (and -e development).

  6. Plan. Add the database section above to ahena.config.ts, then ahena plan -e development (or ahena configure neon -e development to plan and apply one provider).

  7. Apply. ahena apply -e development. Creating a branch is BILLABLE, so with --yes it also needs --allow-billable; creating roles and databases needs confirmation.

  8. Verify. Ahena re-plans after applying; ahena plan -e development should show no Neon changes, and ahena doctor -e development should pass. ahena env secrets development lists DATABASE_URL and NEON_ROLE_PASSWORD_LEO_APP (masked). Then ahena generate neon -e development.

  9. Troubleshooting.

    Symptom Cause Fix
    "Neon rejected the API key" Revoked or mistyped key Create a new key and reconnect.
    "Ahena couldn't open that Neon project" Wrong NEON_PROJECT_ID, or a project-scoped key for another project Check the id in the Neon Console (Settings → General).
    "This key can only view …" Viewer access Use a project-scoped key or ask an admin for Editor.
    "isn't allowed to do that" (403) at apply The key lacks Editor access As above.
    "The Neon project is locked" Another operation kept running past Ahena's retries Wait a minute and apply again.
    Branch shows init Neon is still creating it Run Doctor again in a minute.
    No DATABASE_URL after apply The role already existed, so Neon returned no password ahena env set <environment> DATABASE_URL with the string from the Neon Console.
  10. Disconnect and revoke. ahena disconnect neon -e production, then revoke the key in the Neon Console (organization Settings → API keys → Revoke).