# State storage

> Where Quack on Demand keeps control-plane state (tenants, pools, RBAC graph) in Postgres, separate from tenant data in DuckLake catalogs.

The manager keeps its control-plane state (tenants, databases, pools, nodes, the RBAC graph, and federation registry) in a dedicated Postgres database. This is distinct from where tenant *data* lives, which is each tenant-db's DuckLake catalog; see [DuckLake catalogs](/qod/concepts/catalogs).

## Postgres control-plane database

The control plane is a set of normalized tables with the `qodstate_` prefix, managed by Liquibase, living in a dedicated control-plane database (`qod` by default):

- **Registry:** `qodstate_tenant`, `qodstate_tenant_db`, `qodstate_pool`, `qodstate_node`.
- **RBAC graph:** `qodstate_user`, `qodstate_role`, `qodstate_role_permission`, `qodstate_group`, and the edge tables (`qodstate_user_role`, `qodstate_user_group`, `qodstate_group_role`), plus `qodstate_pool_permission`. `qodstate_user.kind` (`admin` or `user`) is the account's management-rights flag; roles and groups flagged `builtin` are the four protected [built-ins](/qod/operating/rbac-model#built-in-roles-and-groups) every tenant carries.
- **Federation:** `qodstate_federated_source`, `qodstate_federated_secret`.

Liquibase applies the changelog at boot (idempotent: already-applied changesets are skipped), so the schema is created and migrated automatically.

Connections are pooled via HikariCP (size 20 by default on the control-plane store, 10 on `UserStore`). The manager's shutdown hook drains both pools before the manager exits. The legacy file-backed store (single-JSON blob at `statePath`, gated by `stateStorage = file`) was dropped along with the `stateStorage` and `statePath` config keys; only the Postgres path is wired today.

## Table prefixes

The `qodstate_` prefix keeps control-plane tables from colliding with DuckLake's `ducklake_*` / `__ducklake_*` namespace. Those DuckLake tables live inside each managed tenant-db, never in the control-plane database, so the two never share a schema.

For the environment variables and how to point the control plane at a specific Postgres, see the [Configuration reference](/qod/reference/configuration) and [Tenants and databases](/qod/operating/tenants-databases).
