Skip to main content
If you build an application on top of Airhouse, you usually don’t want to create and store a long-lived database password for each of your users. A service account solves this: your backend holds one service-account key and uses it to mint short-lived credentials for whichever user is signed in to your application. The user’s client then connects to Airhouse directly with those credentials. Your backend never proxies SQL.
Each credential carries its own role, so Airhouse enforces reader / writer / admin for every end user. Each credential also has a unique username that Airhouse stores with the subject you supplied, so queries can be traced back to one of your users.

When to use service accounts

Use a service account when:
  • Your users sign in to your application, not directly to Airhouse.
  • Each user’s queries should run with that user’s own role.
  • You don’t want to manage a long-lived Airhouse password per user.
For people, BI tools, and long-running services, create regular database users instead. Both kinds of credentials can exist in the same database.

Create a service account

Service accounts are created with the Admin API using the operator’s admin token. Each service account is bound to exactly one database and has two caps: the highest role it may mint (max_role) and the longest lifetime it may mint (max_ttl_secs, at most 86,400 seconds).
The response includes the service account’s bearer key:
The bearer is returned only once. Store it in your secret manager immediately. It’s never shown again, and listing service accounts doesn’t include it. To replace a lost bearer, create a new service account and revoke the old one.
A service account can’t connect to the database itself. It can only mint credentials.

Mint a credential

When a user signs in to your application, mint a credential for them with the service-account bearer:
The response is a ready-to-use Postgres credential:
Connect with dbname set to tenant, and user and password from the response:
After expires_at, the credential stops authenticating with SQLSTATE 28P01, the same error as a wrong password. Mint once per session, not once per query: a credential can run any number of queries until it expires.

Example: Python backend

Example: Node.js backend

Schema-scoped writers

If several applications share one database, you can mint a writer that may write only inside specific schemas, typically the application’s own schema. It can still read everything, like any writer:
The response echoes "write_schemas": ["app_store_ops"]. Treat a response without a matching echo as a failure: don’t hand out the credential, and revoke it. Rules for write_schemas:
  • Only allowed with "role": "writer".
  • One to 64 entries, each a lowercase identifier (letters, digits, and underscores, starting with a letter or underscore, at most 63 characters), with no duplicates.
  • memory, system, temp, and names starting with __ducklake_metadata_ aren’t allowed.
What a scoped writer can do:
  • Read any table in the database.
  • Write with INSERT, UPDATE, DELETE, MERGE, TRUNCATE, and COPY ... FROM STDIN, but only to tables named as schema.table (or lake.schema.table) in an allowed schema. Unqualified table names are refused.
  • Run DDL inside its schemas: CREATE SCHEMA for an allowed schema, and CREATE, ALTER, and DROP for tables and views in allowed schemas. Unlike an unscoped writer, a scoped writer can create its own tables.
  • Use BEGIN, COMMIT, and ROLLBACK, and the SET statements drivers send while connecting.
Everything else is refused, including ATTACH, USE, SET search_path, INSTALL, LOAD, DROP SCHEMA, CREATE TEMP ..., table functions other than range, generate_series, and unnest, multiple statements in one query, and SQL syntax the scope checker can’t parse (for example time travel with AT (VERSION => n)). A refusal is SQLSTATE 42501:
To check whether a server supports scoped writers before you rely on them, call GET /admin/v1/capabilities with your bearer. It returns {"mint.write_schemas": true} on servers that support the feature.

Revoke credentials

Revoke one minted credential early, for example when a user signs out:
The next login with that credential fails. Connections that are already open aren’t terminated. A service account can only revoke credentials it minted. The admin token can revoke any credential. Revoke a whole service account with the admin token:
Its bearer stops working immediately. Credentials it already minted keep working until they expire, unless you revoke them individually.

Rate limits

Each service account can mint 60 credentials per minute by default. Past the limit, the mint endpoint returns 429 Too Many Requests. Operators can change the limit with mint_rate_limit_per_min (see the configuration reference).

Errors

For the full list of endpoints, see the Admin API reference.