You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All events carry `database_id` when applicable, enabling filtering of audit trails per database.
48
+
49
+
## DML Audit (Optional Per-Database)
50
+
51
+
Enable audit logging of all data modifications on a per-database basis:
52
+
53
+
```sql
54
+
ALTER DATABASE production SET AUDIT_DML = 'writes'; -- INSERT, UPDATE, DELETE only
55
+
ALTER DATABASE production SET AUDIT_DML = 'all'; -- All queries
56
+
ALTER DATABASE production SET AUDIT_DML = 'none'; -- Disabled (default)
57
+
```
58
+
59
+
When enabled, every write produces a `DmlAudit` entry carrying:
60
+
- User ID, database, collection, operation type, row ID, LSN
61
+
- Statement digest and execution timestamp
62
+
- Sourced from the Event Plane (non-blocking to writers)
63
+
64
+
## Per-Database Audit Filtering
65
+
66
+
Filter audit entries by database:
67
+
68
+
```sql
69
+
SHOW AUDIT IN DATABASE production;
70
+
SHOW AUDIT IN DATABASE production WHERE event_type = 'DmlAudit';
71
+
```
72
+
73
+
## Hash Chain Integrity
74
+
75
+
Every audit entry includes a SHA-256 hash of the previous entry. The chain extends each entry's hash with `database_id` when scoped, preserving compatibility with pre-database entries whose `database_id` was null.
76
+
77
+
If any record is modified, the chain breaks and tampering is detected.
32
78
33
79
## SIEM Export
34
80
@@ -37,3 +83,5 @@ CREATE CHANGE STREAM audit_export ON _system.audit
API keys can be scoped to specific databases, restricting the key's access:
37
+
38
+
```sql
39
+
CREATE API KEY 'web-app' FOR user alice WITH DATABASES (prod_orders, prod_users);
40
+
CREATE API KEY 'analytics' FOR user bob WITH DATABASES (analytics_db);
41
+
42
+
SHOW API KEYS; -- displays accessible_databases for each key
43
+
```
44
+
45
+
An API key with an empty database list inherits the owner's accessible databases at bind time. A non-empty list is an explicit restriction — the key can only access the listed databases.
46
+
47
+
### Service Accounts
48
+
49
+
Service accounts are privileged accounts designed for application-to-database connections:
50
+
51
+
```sql
52
+
CREATE SERVICE ACCOUNT etl_worker FOR DATABASE analytics_db;
53
+
ALTER SERVICE ACCOUNT etl_worker SET DATABASES (analytics_db, staging_db);
54
+
55
+
CREATE API KEY 'etl-key' FOR SERVICE ACCOUNT etl_worker;
56
+
```
57
+
58
+
Service accounts are scoped to a single tenant (inherited from the caller) and support per-database access control, inheriting or narrowing their scope when API keys are created on them.
59
+
60
+
## OIDC / SSO
61
+
62
+
NodeDB supports OpenID Connect (OIDC) for enterprise Single Sign-On integration. See (oidc-sso) for complete configuration, claim mapping, token refresh, and session lifetime management.
63
+
64
+
OIDC bearer tokens are supported on the **native protocol and HTTP** entry points only. pgwire connections use SCRAM-SHA-256 exclusively.
65
+
32
66
## JWKS (JWT)
33
67
34
68
Multi-provider support (Auth0, Clerk, Supabase, Firebase, Keycloak, Cognito):
@@ -46,6 +80,7 @@ JWT claims map to `$auth.*` session variables for RLS:
| Database | Per-database resource usage and performance |
56
+
| Tenant | Per-tenant query rates and memory usage |
57
+
58
+
## Per-Database & Per-Tenant Metrics
59
+
60
+
Database-scoped and tenant-scoped metrics enable precise monitoring of multi-tenant deployments.
61
+
62
+
### Per-Database Metrics
63
+
64
+
All metrics labeled with `database="<name>"`:
65
+
66
+
| Metric | Type | Description |
67
+
| --- | --- | --- |
68
+
| `nodedb_database_qps` | counter | Queries per second for the database |
69
+
| `nodedb_database_memory_bytes` | gauge | Memory currently used by the database |
70
+
| `nodedb_database_storage_bytes` | gauge | Storage footprint of all collections in the database |
71
+
| `nodedb_database_connections` | gauge | Active connections to the database |
72
+
| `nodedb_database_bridge_queue_depth` | gauge | Pending requests in SPSC bridge for the database |
73
+
| `nodedb_database_wal_commit_latency_p99` | histogram | 99th percentile WAL commit latency for writes to the database |
74
+
| `nodedb_database_maintenance_cpu_seconds` | counter | Cumulative CPU time spent on background maintenance tasks |
75
+
| `nodedb_database_mirror_lag_ms` | gauge | Replication lag for mirror databases (zero if not mirrored) |
76
+
77
+
### Per-Tenant Metrics
78
+
79
+
All metrics labeled with `database="<name>"` and `tenant="<name>"`:
80
+
81
+
| Metric | Type | Description |
82
+
| --- | --- | --- |
83
+
| `nodedb_tenant_qps` | counter | Queries per second for the tenant |
84
+
| `nodedb_tenant_memory_bytes` | gauge | Memory currently used by the tenant |
85
+
| `nodedb_tenant_storage_bytes` | gauge | Storage footprint of the tenant's data |
86
+
87
+
These metrics are emitted from the dispatch layer (qps), memory governor (memory), WAL (storage), connection listener (connections), and background scheduler (maintenance_cpu_seconds), ensuring consistent attribution across request processing.
Each tenant has fully isolated storage, indexes, and security policies. Cross-tenant data access is impossible by design.
9
9
10
+
## Scope: Database vs Tenant
11
+
12
+
**Database** is the deployment unit — a collection-namespace owner, the quota parent, and the unit of clone, mirror, and backup. One Origin instance hosts many databases.
13
+
14
+
**Tenant** is a customer within a deployment — a row-level scoping label inside collections within a database. One database hosts many tenants.
15
+
16
+
A tenant's data does not span databases. To move a tenant across databases, use `MOVE TENANT`. Cross-database queries are forbidden — applications that need to access multiple databases must open separate connections.
17
+
18
+
See (databases) for database creation, quota, and backup operations.
Copy file name to clipboardExpand all lines: docs/administration/rbac.rdx
+59Lines changed: 59 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -14,9 +14,66 @@ description: Role-based access control with GRANT, REVOKE, and built-in role hie
14
14
| `admin` | All operations + DDL |
15
15
| `tenant_admin` | Admin within a tenant |
16
16
| `superuser` | Unrestricted (cross-tenant) |
17
+
| `cluster_admin` | Cluster-wide admin operations without full superuser capabilities |
17
18
18
19
Higher roles inherit all permissions of lower roles.
19
20
21
+
## Database-Scoped Roles
22
+
23
+
NodeDB supports roles scoped to specific databases:
24
+
25
+
```sql
26
+
GRANT DATABASE_OWNER ON DATABASE analytics TO alice;
27
+
GRANT DATABASE_EDITOR ON DATABASE analytics TO bob;
28
+
GRANT DATABASE_READER ON DATABASE analytics TO charlie;
29
+
30
+
ALTER USER alice SET DEFAULT DATABASE analytics;
31
+
```
32
+
33
+
| Role | Permissions | Scope |
34
+
| --- | --- | --- |
35
+
| `DATABASE_OWNER(db)` | All operations within database, including schema and user management | Specific database |
36
+
| `DATABASE_EDITOR(db)` | SELECT, INSERT, UPDATE, DELETE on collections | Specific database |
37
+
| `DATABASE_READER(db)` | SELECT only | Specific database |
38
+
39
+
## ClusterAdmin Role
40
+
41
+
The `ClusterAdmin` role enables privileged cluster operations without granting full superuser access. ClusterAdmins can perform cluster-wide administration tasks but cannot bypass row-level security or read data from databases they don't own.
42
+
43
+
```sql
44
+
GRANT ROLE cluster_admin TO alice;
45
+
46
+
-- ClusterAdmin can:
47
+
ALTER DATABASE ... RENAME TO;
48
+
ALTER DATABASE ... SET QUOTA;
49
+
ALTER DATABASE ... SET IDLE_TIMEOUT;
50
+
CREATE OIDC PROVIDER;
51
+
ALTER OIDC PROVIDER;
52
+
```
53
+
54
+
## Admin DDL Gating Matrix
55
+
56
+
Certain administrative DDL operations are gated by required roles. Attempting an operation without the required role returns `INSUFFICIENT_PRIVILEGE` and emits a `PermissionDenied` audit entry.
@@ -55,3 +112,5 @@ AS BEGIN RETURN (SELECT COUNT(*) FROM audit_log); END;
55
112
SHOW GRANTS FOR analyst;
56
113
SHOW PERMISSIONS;
57
114
```
115
+
116
+
See (databases) for database creation, quota, and management. See (quotas) for resource limits. See (audit-logging) for permission denial auditing. See (oidc-sso) for OIDC provider setup and claim mapping.
Copy file name to clipboardExpand all lines: docs/administration/rls.rdx
+21Lines changed: 21 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -31,6 +31,27 @@ CREATE RLS POLICY org_filter ON docs FOR READ USING (org_id = $auth.org_id) REST
31
31
CREATE RLS POLICY not_deleted ON docs FOR READ USING (status != 'deleted') RESTRICTIVE;
32
32
```
33
33
34
+
## Session Context Variables
35
+
36
+
RLS policies can reference the authenticated session context via `$auth.*` variables:
37
+
38
+
| Variable | Type | Description |
39
+
| --- | --- | --- |
40
+
| `$auth.id` | string | User ID (from JWT `sub` or user record) |
41
+
| `$auth.username` | string | Username |
42
+
| `$auth.role` | string | Current role |
43
+
| `$auth.tenant_id` | u64 | Tenant ID of the authenticated user |
44
+
| `$auth.database_id` | u64 | Database ID of the session |
45
+
46
+
Example combining tenant and database scoping:
47
+
48
+
```sql
49
+
CREATE RLS POLICY multi_scope ON data FOR READ
50
+
USING (tenant_id = $auth.tenant_id AND database_shard = $auth.database_id);
51
+
```
52
+
53
+
Substitution is fail-closed: if the auth context lacks a required variable (e.g., `$auth.database_id` is null), the query rejects the row rather than allowing wide-open access.
54
+
34
55
## Cross-Engine Behavior
35
56
36
57
RLS filters are injected at plan time, before engine dispatch:
| `SESSION_CAP_EXCEEDED` | 7003 | Cluster session capacity exhausted; close idle sessions or raise `max_active_sessions` |
57
+
58
+
## Database Clone Errors (5000–5099)
59
+
60
+
| Error | Code | Resolution |
61
+
| --- | --- | --- |
62
+
| `CLONE_DEPTH_EXCEEDED` | 5002 | Clone lineage exceeds `MAX_CLONE_DEPTH = 8`; promote source clone to materialize and flatten lineage |
63
+
| `CLONE_DEPENDENCY` | 5003 | Source database has dependent clones; `DROP` clones first or use `DROP DATABASE source FORCE` to trigger materialization |
64
+
| `CANNOT_CLONE_MIRROR` | 5004 | Mirrors are read-only and create ambiguous bitemporal lineage; `PROMOTE` the mirror first, then clone |
65
+
66
+
## Database Mirror Errors (1100–1199, 6000–6099)
67
+
68
+
| Error | Code | Resolution |
69
+
| --- | --- | --- |
70
+
| `MIRROR_READ_ONLY` | 1103 | Mirror is read-only until promoted; use `ALTER DATABASE <mirror> PROMOTE` to switch to writable mode |
71
+
| `STALE_READ_NOT_LEADER` | 1104 | `Strong` consistency requested on a mirror (which is stale); use `BoundedStaleness` or `Eventual` consistency, or connect to the source database for strong reads |
72
+
| `MIRROR_DEGRADED` | 6001 | Mirror lag exceeds threshold (default: 5 s async, 100 ms sync); check source–mirror network and availability |
73
+
| `MIRROR_DISCONNECTED` | 6002 | Mirror disconnected from source; reconnecting with exponential backoff |
74
+
75
+
## Move Tenant Errors
76
+
77
+
| Error | Code | Resolution |
78
+
| --- | --- | --- |
79
+
| `MOVE_TENANT_ALREADY_AT_TARGET` | 5005 | Tenant already moved to target database; re-issuing is idempotent (safe to retry) |
80
+
81
+
## Session Lifecycle Errors (2000–2099)
82
+
83
+
| Error | Code | Resolution |
84
+
| --- | --- | --- |
85
+
| `SESSION_REVOKED` | 2001 | Session terminated due to identity change (DROP USER, role revocation, or soft-delete via `is_active=false`); reconnect to authenticate |
86
+
| `SESSION_IDLE_TIMEOUT` | 2002 | Session closed due to inactivity exceeding `idle_session_timeout_secs`; reconnect and keep activity |
87
+
| `SESSION_NOT_FOUND` | 1105 | `KILL SESSION` targeted a non-existent session ID; verify `SHOW SESSIONS` for active session IDs |
88
+
39
89
## Error Construction
40
90
41
91
Errors are created via constructors: `NodeDbError::storage("detail")`, `NodeDbError::collection_not_found("users")`, etc.
0 commit comments