> ## Documentation Index
> Fetch the complete documentation index at: https://oxy.tech/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Users, roles, and security

> How Airhouse authenticates connections, what each role can do, and how to enable TLS.

Every connection to Airhouse authenticates as a user, and every user has one role. Airhouse checks the role on every statement before the statement reaches the query engine.

## Authentication

Airhouse uses **SCRAM-SHA-256** password authentication, the same mechanism as modern PostgreSQL. Standard drivers support it out of the box.

Credentials come from one of three places:

| Source | Lifetime | Use it for |
| - | - | - |
| Database users created with the [Admin API](../reference/admin-api) | Until deleted | People, BI tools, and trusted services |
| Short-lived credentials minted by a [service account](./service-accounts) | Minutes to hours (24 hours at most) | End users of your own application, agents |
| Static users in the server configuration | Until the configuration changes | Single-database, self-hosted setups without the Admin API |

Passwords for database users and minted credentials are stored as SCRAM verifiers, never in plain text.

## Roles

| Role | Can run |
| - | - |
| `reader` | Queries: `SELECT`, `WITH`, `SHOW`, `DESCRIBE`, `EXPLAIN` |
| `writer` | Everything a reader can, plus `INSERT`, `UPDATE`, `DELETE`, `MERGE`, and `COPY` |
| `admin` | Everything, including `CREATE`, `ALTER`, and `DROP` |

A statement your role doesn't allow is rejected with SQLSTATE `42501` before it runs:

```text theme={null}
ERROR:  Permission denied: Reader role cannot execute Insert statements
```

Writers can't create, alter, or drop tables. Have an admin create the tables your pipelines write to, or mint a [schema-scoped writer](./service-accounts#schema-scoped-writers), which may run DDL inside its own schemas.

<Tip>
  Follow least privilege: give dashboards and analysts `reader`, give ingestion pipelines `writer`, and keep `admin` for the people who manage table structure.
</Tip>

### When a role can't be determined

If Airhouse can't determine the role for a session (for example, because the credential expired or was revoked before the session's first query), it closes the connection rather than running with reduced privileges. Reconnect with a valid credential. Your driver's normal reconnect logic handles this.

## TLS

When the server has a certificate and key configured (see `[tls]` in the [configuration reference](../reference/configuration)), clients can connect over TLS:

```bash theme={null}
psql "host=airhouse.example.com port=5445 dbname=demo user=alice sslmode=require"
```

Use `sslmode=require` or stricter in every client that connects over a network you don't control.

## Static users for self-hosted setups

When you run Airhouse without the Admin API, you can define users in the configuration file:

```toml airhouse.toml theme={null}
[[users]]
name = "admin"
password = "change-me"
role = "admin"

[[users]]
name = "dashboard"
password = "change-me-too"
role = "reader"
```

Or with the `AIRHOUSE_USERS` environment variable, as comma-separated `name:password:role` entries:

```bash theme={null}
export AIRHOUSE_USERS="admin:change-me:admin,dashboard:change-me-too:reader"
```

If you manage users with the Admin API, don't also define static users. Static users are checked first and aren't tied to a single database.

<Warning>
  Name every user you intend to admit. With static users configured, a username that isn't in the list can't run queries. If no users are configured at all and the Admin API isn't enabled, any username that presents the server's default password (`[auth] password`, or `AIRHOUSE_PASSWORD`) connects with full `admin` rights. Only run that way on your own machine.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.