Security and access

Security

Authentication, roles, TLS, limits and the safeguards built into the process.

Clients and TLS#

  • Client authentication is SCRAM-SHA-256 by default; md5 is available and trust is for development only.
  • Enable client TLS with listen.tls.cert and listen.tls.key, and require it with listen.require_tls = true. StreamQL accepts SSLRequest and direct TLS with ALPN postgresql.
  • Server connections use upstream.sslmode. Use verify-full with upstream.sslrootcert for a private CA; an unreadable CA fails closed.
  • Clients that ask for protocol 3.2 are negotiated down to 3.0.

Console sign-in#

  • The console supports local passwords (argon2id, minimum access.password.min_length 12 characters, common passwords and the user's email rejected), OIDC single sign-on with PKCE, TOTP and WebAuthn multi-factor, and break-glass accounts whose every use is audited.
  • access.mfa is off, optional (default), required_admins or required.
  • Sessions end after access.session_idle_s (1800) of inactivity or access.session_absolute_s (43200). An account locks for access.lockout_s (900) after access.lockout_attempts (5) failures, and sign-in attempts are rate limited (access.attempts_per_minute, 20).
  • The first start prints a one-time setup code. The first person to claim the node becomes its admin; access.loopback_claim lets a loopback client claim a loopback-bound node.
  • Set access.trusted_proxies to the CIDRs of your reverse proxies before relying on X-Forwarded-For, and access.public_url when the console is served under a public host name.

For OIDC, access.oidc takes issuer, client_id, client_secret, scopes, role_claim, a roles map from claim values to console roles, default_role and auto_provision. ID tokens signed with RS256 and ES256 are accepted, and issuer, audience, expiry and nonce are checked.

Roles#

PermissionAdminOperatorViewerAuditor
View dashboardsyesyesyesyes
View statement text and client addressesyesnonoyes
Add and remove databasesyesnonono
Edit policy and configyesscopednono
Switch optimizations on or offyesscopednono
Drain gateways and manage workersyesyesnono
Manage alert rules and channelsyesscopednono
Manage users, roles and API tokensyesnonono
Read the audit logyesnonoyes

Scoped is a full grant for a user assigned every database. A user assigned only some databases gets it on routes that check the target database, and every changed key must sit under an assigned database. Without the sensitive-read permission, client addresses show as <redacted>.

API credentials#

CredentialPrincipal
sq_session cookieA console user, with the user's role and database scope
Authorization: Bearer sqa_...An API token with its own role and scope
Authorization: Bearer <admin.token>Automation, as admin

A wrong bearer token is always 401 and never falls back to anonymous. Cookie-authenticated writes need a CSRF token. access.anonymous is auto (default), none or viewer.

Limits and hardening#

  • limits.max_message_bytes (256 MiB) bounds a message held whole in memory; larger row and COPY data is streamed when limits.stream_large_messages is on.
  • limits.max_unauthenticated (1024), limits.auth_timeout_s (30) and limits.failed_login (20 failures per 60 s block an address for 60 s) harden the pre-authentication phase.
  • Framing and authentication faults close the connection; missing evidence means a statement is forwarded unmodified.
  • The process sets RLIMIT_CORE to 0, disables dumping, refuses to run as root unless secrets.allow_root is set, and keeps secrets in an encrypted in-memory vault with zeroed memory.
  • Every optimization has an independent kill switch; the dataplane.* options and dataplane.engine = "tokio" restore the defaults.

Secrets#

Use secret references (file:, env:, vault: and the others in Configuration) for upstream passwords, the admin token, OIDC client secrets and MCP passwords. They are never written to logs or shown again by the console.