Skip to main content

06 — Permissions

Model

Permissions are grants that attach a group to a (server, database, action) with optional constraints.

group ⨯ (server, database) ⨯ action ⨯ constraints → allowed
PieceMeaning
GroupAn SSO group claim (e.g. backend-engineers, oncall, data-platform). Synced from IdP.
Server / databaseLogical names from the config — never raw hosts.
Actionschema_read, query_read, query_write, history_read.
ConstraintPer-grant overrides: require_reason, row_limit, statement_timeout_ms, allowed_schemas, denied_tables, time_window (e.g. business hours only).

A request is allowed iff some grant matches. Denies are not explicit — absence of a matching grant denies.

Actions in detail

ActionWhat it unlocks
schema_readlist_databases, describe_schema
query_readrun_query with read-only enforcement, explain, sample_table (size-capped)
query_writerun_query with write grants (requires the DB role to also have write privileges)
history_readRead your own query history via the get_query_history tool (#169). Scoping is on the SSO-verified identity, never a client-supplied user field. Admin-only history access remains via the state DB.

query_read implies schema_read. query_write implies query_read.

Worked examples

Backend engineer on staging: full query, schema-read on prod.

- group: backend-engineers
grants:
- server: staging
database: "*"
action: query_read
- server: prod
database: "*"
action: schema_read

Oncall with audited prod access: query on prod, but reason required and short timeouts.

- group: oncall
grants:
- server: prod
database: "*"
action: query_read
constraints:
require_reason: true
statement_timeout_ms: 5000
row_limit: 1000

Data platform with writes to a single sandbox DB:

- group: data-platform
grants:
- server: analytics
database: sandbox
action: query_write
constraints:
require_reason: true

New hires: no entry → no access. The default is nothing.

Worker-db rollout (issue #19). Rule: rollout stays contract-only and scan-friendly.

ItemValue
Deployable configconfig/permissions.yml
Zitadel groupsdevs, devops, cto
v1 action scopequery_read only
Shared constraintsstatement_timeout_ms: 30000, row_limit: 10000
Deferred scopePer-dev DB ownership (requires Zitadel group-per-app mapping)

Evaluation

For a request (user, groups, server, db, action):

  1. Resolve groups from the session.
  2. Collect every grant matching one of the user's groups and the target (server, db).
  3. If no grant has an action ≥ the requested action → forbidden.
  4. Merge constraints across matching grants: take the most restrictive value for each (lowest row_limit, lowest statement_timeout_ms, require_reason = true wins, schema/table allow/deny intersected).
  5. Apply constraints to the call.

Most-restrictive merging means you can't accidentally upgrade your access by being in two groups.

Hot reload

Permission changes land via PR → merge → operator runs kill -HUP on the gateway (or rolling restart). Live sessions keep their existing grants snapshot until next login (matches 04-auth-sso).

Admin operations

There is no in-band admin UI. Operator actions are CLI subcommands of the gateway binary, run inside the container, against the state DB:

  • gateway admin revoke-session <user_email>
  • gateway admin list-active-sessions
  • gateway admin replay-audit <query> (for compliance investigations)

Admin commands authenticate via a separate mechanism (Unix socket / shell access), not SSO — they're for whoever has prod shell access.