Authentication
XTDB provides authentication to control database access and secure connections. Authentication rules determine which users can connect and what credentials they must provide. They apply to pgwire and FlightSQL connections alike: see the ADBC reference for how a FlightSQL client presents its credentials.
Authentication providers
Section titled “Authentication providers”XTDB supports three authentication providers:
- Single Root User (
!SingleRootUser, v2.2+): a single user (xtdb) whose password is configured at startup. This is the default. - User List (
!UserList, v2.2+): a fixed set of users with pre-hashed passwords, configured at startup. - OpenID Connect (
!OpenIdConnect): integrates with external identity providers like Keycloak, Auth0, AWS Cognito or Azure Entra.
Single root user (v2.2+)
Section titled “Single root user (v2.2+)”The !SingleRootUser method authenticates a single user named xtdb against a password that’s configured at startup.
The password is resolved at config-construction time:
- The explicit
passwordfield on the YAML config (or KotlinSingleRootUservalue), if present. - Otherwise, the
XTDB_PASSWORDenvironment variable, if set. - Otherwise, no password is configured.
If no password is configured, connections run under TRUST — no credentials required.
If a password is configured, connections require PASSWORD authentication as the xtdb user.
Any other username is rejected.
Configuration
Section titled “Configuration”Resolve the password from XTDB_PASSWORD (the common case for containerised deployments):
authn: !SingleRootUserOr pass the password explicitly (discouraged for production — anyone with access to the config can read it):
authn: !SingleRootUser password: !Env XTDB_PASSWORDauthn: !SingleRootUser password: hunter2User list (v2.2+)
Section titled “User list (v2.2+)”The !UserList method authenticates against a fixed set of users configured at startup.
It suits simple deployments that want password authentication for more than one user without running an external identity provider.
The user set is static. It’s read from the configuration when the node starts, and the only way to change it is to edit the configuration and restart — there is no SQL or runtime API to add, alter, or remove users.
Configuration
Section titled “Configuration”Each user maps to a pre-hashed password, tagged with the algorithm that produced it:
authn: !UserList users: alice: !Argon2id "$argon2id$v=19$m=65536,t=3,p=1$c29tZXNhbHRzYWx0$RdescudvJCsgt3ub+b+dWRWJTmaaJObG" bob: !BCrypt "$2y$12$mc.G6e7uChPgZW2NfY0XQOQ0qN6Q0o3Yv0bFv6kKQXmnq7nqQk0K" rules: # local connections are trusted - remoteAddress: 127.0.0.1 method: TRUST # everyone else must supply a password - method: PASSWORDIf rules is omitted, every connection requires a password.
The configured users appear in the read-only pg_user view (with passwd redacted to NULL), so Postgres tooling that lists users sees them.
usesuper is true only for a user named xtdb — the same superuser convention as !SingleRootUser — and false for everyone else.
Hash algorithms
Section titled “Hash algorithms”Each password carries its own algorithm tag, so a node can hold a mix and you can move to a new algorithm without re-hashing the existing entries:
!Argon2id— argon2id, the recommended default.!BCrypt— bcrypt, in standard modular-crypt ($2…) form.
Hashing a password
Section titled “Hashing a password”Pre-hash passwords with the hash-password command and paste the output into the config:
xtdb hash-password [--argon2id | --bcrypt] 'my-password'--argon2id is the current default; pass --bcrypt to use bcrypt instead.
Omit the password argument to read it from stdin, which keeps the plaintext out of your shell history:
echo 'my-password' | xtdb hash-passwordOpenID Connect (OIDC)
Section titled “OpenID Connect (OIDC)”The !OpenIdConnect authentication method integrates with external identity providers like Keycloak, Auth0, AWS Cognito or Azure Entra.
Basic Configuration
Section titled “Basic Configuration”authn: !OpenIdConnect issuerUrl: https://your-keycloak.example.com/realms/master clientId: xtdb-client clientSecret: !Env OIDC_CLIENT_SECRET rules: - user: oidc-client method: CLIENT_CREDENTIALS - method: PASSWORDFor complete OIDC configuration, setup guides, and troubleshooting, see OpenID Connect Authentication.
Rule configuration
Section titled “Rule configuration”!UserList and !OpenIdConnect control database access through authentication rules that match users and IP addresses to determine the required authentication method.
!SingleRootUser doesn’t use rules — its method is determined by whether a password is configured.
Authentication Rules
Section titled “Authentication Rules”Authentication rules are evaluated in order until the first match.
If no rules match, the connection is rejected.
Where the caller’s address isn’t known, evaluation stops at the first rule that applies to that user and names a remoteAddress, and rejects the connection rather than moving on to a rule further down.
A FlightSQL client sending OAuth client credentials names no user, so its rules are matched against the user the identity provider reports for those credentials - for Keycloak, the client’s service-account user id - rather than a username it connects with. See the ADBC reference.
- Rule Parameters
-
-
user(optional): Match specific usernameremoteAddress(optional): Match IP address or CIDR block (IPv4 or IPv6)method(required): Authentication method to use
-
- Available Methods
-
-
TRUST: No authentication requiredPASSWORD: Require username/password validationCLIENT_CREDENTIALS: OAuth client credentials flow (OIDC only)DEVICE_AUTH: OAuth device authorization flow (OIDC only)
-
- Example Rule
- user: admin remoteAddress: 127.0.0.1 method: PASSWORDThis rule requires the admin user to provide a password when connecting from localhost.
In-process connections (v2.3+)
Section titled “In-process connections (v2.3+)”An application embedding XTDB opens connections with node.connect(), which applies no authentication rules: the caller already holds the node.
Those connections have no user, so their writes carry none in the audit trail and they can’t run superuser-only statements such as GRANT.
To run a session as one of your users, authenticate them through the node, which applies the same provider and rules as pgwire, then open connections as the principal it returns:
val alice = when (node.authMethodFor("alice", remoteAddress)) { TRUST -> node.authenticateTrusted("alice") PASSWORD -> node.authenticateWithPassword("alice", password) CLIENT_CREDENTIALS -> node.authenticateWithClientCredentials(clientId, clientSecret) DEVICE_AUTH -> node.startDeviceAuth("alice").let { auth -> println("Approve this connection at ${auth.url}") auth.await() } null -> error("alice may not connect from $remoteAddress")}
node.connect("xtdb", alice).use { conn -> // conn.user is the user the provider authenticated — for OIDC, the token's subject}A principal opens as many connections as you need, to any database.
None of the authenticate… calls apply your rules — that’s authMethodFor, so consult it first when the rules should decide.
authenticateWithPassword and authenticateWithClientCredentials verify the credentials they’re given, but authenticateTrusted checks nothing: it vouches for whichever user you name, superuser included.