Data governance and auditing
Data governance is the set of controls that answer, for any piece of data, who can access it, who did access it, where it came from, how long it is kept, and whether it can be recovered. QuestDB covers these questions with features spread across several parts of the documentation. This page groups them by the question each one answers, so you can map QuestDB to your own policy or to an auditor's checklist, and states where the coverage ends.
Most of these features are part of QuestDB Enterprise. Views, query tracing,
SHOW CREATE, metrics, read-only interfaces, and checkpoint backups are
available in all editions.
At a glance
| Question | Feature | Edition |
|---|---|---|
| Who is connecting? | OIDC single sign-on, users and service accounts, TLS | QuestDB Enterprise |
| What may they read or change? | Permissions, down to column and row level | QuestDB Enterprise |
| What may they even see? | Object visibility | QuestDB Enterprise 4.1.0+ |
| How much may they consume? | Resource groups and per-principal memory limits | QuestDB Enterprise 4.1.0+ |
| Who can do what, right now? | Access control functions, SHOW CREATE DATABASE | QuestDB Enterprise |
| Who read this data? | Audited views | QuestDB Enterprise |
| What queries ran recently? | Query tracing, query_activity() | All |
| Where does this data come from? | SHOW CREATE, views(), materialized_views() | All |
| How long is it kept? | Storage policies | QuestDB Enterprise |
| How long is it kept? | TTL | QuestDB open source |
| Can it still change? | Immutability after DROP LOCAL | QuestDB Enterprise |
| Can we recover it? | Backup, point-in-time recovery, replication | QuestDB Enterprise |
| Can we recover it? | Checkpoints | All |
Control access
Identity
Every connection in QuestDB Enterprise runs as a principal: a user, or a service account for applications. Principals authenticate with passwords, JWK tokens for ILP, or REST tokens with an expiry, and OIDC delegates authentication to an identity provider such as Microsoft Entra ID or PingFederate. With OIDC, group claims from the provider map to QuestDB groups, so access follows the directory instead of being maintained in two places.
TLS encrypts traffic on the HTTP, PGWire, and ILP endpoints, and certificates can be rotated without a restart.
Permissions
Role-based access control grants permissions to users, groups, and service accounts at database, table, and column level, and per endpoint, so a service account can be limited to ILP ingestion only. A grant carries the grant option only when you say so, which keeps delegation explicit.
QuestDB stores no owner for an object. Its creator, or the user or group named
in OWNED BY, receives all
permissions on it with the grant option, and from then on these are ordinary
grants. To find who controls an object, look for the principals that hold
permissions on it with the grant option, as shown in
Review entitlements.
Column-level grants hide sensitive columns from a principal:
GRANT SELECT ON fx_trades(* EXCLUDE (counterparty)) TO analyst;
Row-level restrictions use views: a view captures
its creator's permissions, so granting SELECT on a filtered view, and not on its
table, exposes only the matching rows. See
row-level security for a
worked multi-tenant example.
Since QuestDB Enterprise 4.1.0,
object visibility hides tables and
views from principals that hold no permission on them. A hidden object is left
out of SHOW TABLES, tables(), information_schema, and pg_catalog, and
statements that name it fail as if it did not exist. A few paths still reveal a
hidden object, listed in the RBAC
limitations.
Read-only interfaces
http.security.readonly
and
pg.security.readonly
make an interface refuse statements that modify data or structure. This is
available in all editions, and in QuestDB open source it is the main way to protect data from a
reporting tool. It is a data boundary, not an administrative lockdown.
Resource limits
Resource groups cap how many queries a set of principals may run at once, their share of CPU, and their query memory, so one tenant cannot starve another. Per-principal memory limits override the server-wide limit for a single user, group, or service account. Both are available since QuestDB Enterprise 4.1.0.
Review entitlements
The access control functions return
permissions as rows, so an entitlement review is a query rather than one
SHOW PERMISSIONS per principal. active_permissions() resolves group
membership and reports what each principal can effectively do:
SELECT entity_name, entity_type, column_name, grant_option
FROM active_permissions()
WHERE permission = 'SELECT'
AND (table_name = 'fx_trades' OR table_name IS NULL)
ORDER BY entity_name;
A row with an empty table_name is a database-level grant, which covers every
table. active_grants() lists only direct grants, and
active_permissions() EXCEPT active_grants() shows what was inherited. The
functions have
limitations: the built-in
admin and OIDC users are not listed, for example.
For a point-in-time snapshot of the whole access control layer,
SHOW CREATE DATABASE
dumps every user, group, service account, membership, and grant as replayable
SQL, without credentials:
SHOW CREATE DATABASE INCLUDE (ACL);
Storing that output on a schedule gives you a history of entitlements that you can diff between two reviews.
Audit data access
Audited views
An audited view, created WITH AUDIT, records
a row in sys.view_audit for each read: the principal, when the read finished,
how long it took, whether it succeeded, and the values of the view parameters
marked AUDITED. Put sensitive data behind an audited view, grant SELECT on
the view and not on its tables, and the trail answers who looked at which
symbols, accounts, or time ranges:
SELECT ts, principal, params, status
FROM sys.view_audit
WHERE view_name = 'positions_by_book'
AND ts IN '$now-7d..$now'
ORDER BY ts DESC;
No principal, the built-in admin included, can insert into, update, rename, or drop the audit table, see tamper protection. Read the delivery guarantees and limitations before relying on the trail as evidence: recording is best-effort, and some paths read the same data without an audit row.
Query tracing
Query tracing records the text,
duration, and principal of every query in the _query_trace table. It is off
by default and keeps 24 hours of history, so treat it as a short-term
investigation tool rather than an audit log.
Running queries
query_activity() lists the
queries running now, with the user, the query text, and the memory used, and
CANCEL QUERY stops one.
Trace lineage
Views, materialized views, and live views are defined in SQL, and that
definition is the lineage of the data they return.
views() and
materialized_views() list
each view with its SQL, and materialized_views() also names the base table
and the last refresh times:
SELECT view_name, base_table_name, last_refresh_finish_timestamp
FROM materialized_views();
SHOW CREATE returns the full DDL of a single table or
view, and SHOW CREATE DATABASE returns every object in dependency order, so
each view appears after the objects it reads from. Running it on a schedule and
keeping the output records how the schema changed over time.
Retain and protect data
A storage policy moves each partition through its lifecycle on a schedule: conversion to Parquet, upload to cold storage in an object store, removal of the local copy, and final deletion. This makes retention a property of the table, declared once, instead of a script that has to keep running:
ALTER TABLE fx_trades SET STORAGE POLICY(
TO PARQUET 7d,
TO REMOTE 14d,
DROP LOCAL 90d,
DROP REMOTE 3Y
);
Once DROP LOCAL runs, a partition is
sealed: later
inserts and updates that target it are not applied, and removing the policy does
not unseal it. Partitions in cold storage remain queryable with normal SQL.
To check that retention is applied,
storage_policies lists the
policy on each table, including system tables such as sys.view_audit, and
table_cold_partitions()
shows which partitions of a table are uploaded and sealed.
In QuestDB open source, TTL drops partitions older than a set age. QuestDB Enterprise rejects TTL on user tables in favor of storage policies. See data retention for the manual options.
Recover and verify
QuestDB Enterprise backups upload only changed data
to object storage and run on a schedule, and an optional BLAKE3 hash of every partition file is
verified on restore.
Point-in-time recovery replays
replicated WAL on top of a backup to rebuild the database as it stood at any
instant, for example just before an accidental DROP TABLE.
Replication keeps hot copies on other
nodes. In all editions, you can also use
checkpoints
with your own snapshot tooling.
To find when a destructive change was committed, use the server log, or
wal_transactions() while the table still exists, as described in
choosing a recovery timestamp.
Monitor
QuestDB exposes metrics to
Prometheus and records them every second in
sys.metrics, including
resource group metrics
that show admission waits and rejections per group. These are operational
signals and do not include authentication events.
Coverage limits
Check these gaps against your requirements before mapping QuestDB to a compliance control:
- Read auditing is best-effort. Audited views can lose rows under overload, a failed write, or a crash, and there is no lossless mode. Each node keeps its own trail, which is not replicated. Old rows can be removed by retention, and the removal itself is not recorded.
- Only reads through an audited view are audited. A principal with
SELECTon the underlying tables reads the same rows with no audit row, and denied reads are usually not recorded. - There is no durable query log. Query tracing keeps 24 hours.
- There is no audit log of DDL, grants, or logins. Schema changes, grants
and revokes, and authentication attempts are not recorded in a queryable
table. Snapshots from
SHOW CREATE DATABASEand the server log are the closest substitutes. - There is no session listing.
sys.metricscounts open connections per protocol, idle ones included, but not who holds them, andquery_activity()shows only running queries. - There is no encryption at rest or data masking. Use storage-level encryption for data at rest, and column grants or views to keep sensitive values from a principal.
- There are no table or column comments, so data classification has to live outside the database.