Skip to content

Commit 5d081c8

Browse files
committed
docs: expand existing pages with database-scoped features
Extend existing documentation to cover the database management layer: - ddl: add full Database Management DDL block (CREATE/DROP/ALTER DATABASE, CLONE, MIRROR, PROMOTE, MOVE TENANT, per-tenant quota, role grants, API keys, service accounts, OIDC providers) - rbac: add database-scoped roles table, ClusterAdmin role, and admin DDL gating matrix with required roles per operation - authentication: add database-scoped API key syntax, service account DDL, and OIDC bearer token scope note - rls: add $auth.database_id session variable and fail-closed substitution semantics - monitoring: add per-database and per-tenant metrics tables with label schema and emission sources - multi-tenancy: add Database vs Tenant scope definition and cross-links to databases and move-tenant pages - audit-logging: expand event taxonomy into four categories, add DML audit configuration, per-database audit filtering, and hash-chain integrity note - error-codes: add database boundary, quota, clone, mirror, move tenant, and session lifecycle error tables - limits-defaults: add database boundary constants, clone/mirror thresholds, role auth limits, and per-database quota defaults - sqlstate-mapping: add SQLSTATE codes for quota, mirror, clone, and session errors
1 parent e2b5a30 commit 5d081c8

10 files changed

Lines changed: 409 additions & 1 deletion

File tree

docs/administration/audit-logging.rdx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,53 @@ level = "standard"
2828

2929
## Key Events
3030

31-
AuthSuccess, AuthFailure, AuthzDenied, PrivilegeChange, SessionConnect/Disconnect, AdminAction, TenantCreated/Deleted, SnapshotBegin/End, RestoreBegin/End, CertRotation, KeyRotation, NodeJoined/Left, QueryExec, RlsDenied, RowChange.
31+
### Authentication & Session Events
32+
33+
AuthSuccess, AuthFailure, PermissionDenied, SessionConnect/Disconnect, SessionRevoked, LockoutTriggered, LoginRateLimited.
34+
35+
### Database Lifecycle Events
36+
37+
DatabaseCreated, DatabaseDropped, DatabaseRenamed, DatabaseQuotaChanged, DatabaseCloned, DatabaseMirrored, DatabasePromoted, DatabaseMaterialized, TenantMoved, DatabaseBackedUp, DatabaseRestored, DatabaseAuditDmlChanged, DatabaseIdleTimeoutChanged.
38+
39+
### Authorization & Audit Events
40+
41+
PrivilegeChange, RlsRejected, AdminAction, TenantCreated/Deleted.
42+
43+
### System Events
44+
45+
SnapshotBegin/End, RestoreBegin/End, CertRotation, KeyRotation, NodeJoined/Left, QueryExec, RowChange, OidcProviderChanged.
46+
47+
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.
3278

3379
## SIEM Export
3480

@@ -37,3 +83,5 @@ CREATE CHANGE STREAM audit_export ON _system.audit
3783
DELIVERY WEBHOOK 'https://siem.example.com/ingest'
3884
WITH (format = 'json', hmac_secret = 'your-secret');
3985
```
86+
87+
See (session-management) for session revocation audit events and (oidc-sso) for authentication provider changes.

docs/administration/authentication.rdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ psql -h localhost -p 6432 -U alice
2020

2121
## API Keys
2222

23+
### Basic API Key Usage
24+
2325
```sql
2426
CREATE API KEY 'my-service' ROLE readwrite;
2527
DROP API KEY 'my-service';
@@ -29,6 +31,38 @@ DROP API KEY 'my-service';
2931
curl -H "Authorization: Bearer <api-key>" http://localhost:6480/v1/query
3032
```
3133

34+
### Database Scoping
35+
36+
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+
3266
## JWKS (JWT)
3367

3468
Multi-provider support (Auth0, Clerk, Supabase, Firebase, Keycloak, Cognito):
@@ -46,6 +80,7 @@ JWT claims map to `$auth.*` session variables for RLS:
4680
| `role` | `$auth.role` | `WHERE $auth.role = 'admin'` |
4781
| `org_id` | `$auth.org_id` | `WHERE org_id = $auth.org_id` |
4882
| `scope` | `$auth.scopes` | Scope-based access control |
83+
| `database_id` | `$auth.database_id` | `WHERE db_shard = $auth.database_id` |
4984

5085
Supported algorithms: RS256, ES256.
5186

docs/administration/monitoring.rdx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,39 @@ curl http://localhost:6480/health/ready # WAL recovered, ready for queries
5252
| Connection | Active connections, auth failures |
5353
| Replication | Raft log lag, replication latency |
5454
| Storage | WAL fsync latency, segment count, compaction debt |
55+
| 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.
5588

5689
## Cross-shard transaction metrics
5790

docs/administration/multi-tenancy.rdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ description: Tenant isolation, quotas, backup, and GDPR purge.
77

88
Each tenant has fully isolated storage, indexes, and security policies. Cross-tenant data access is impossible by design.
99

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.
19+
1020
## Creating Tenants
1121

1222
```sql

docs/administration/rbac.rdx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,66 @@ description: Role-based access control with GRANT, REVOKE, and built-in role hie
1414
| `admin` | All operations + DDL |
1515
| `tenant_admin` | Admin within a tenant |
1616
| `superuser` | Unrestricted (cross-tenant) |
17+
| `cluster_admin` | Cluster-wide admin operations without full superuser capabilities |
1718

1819
Higher roles inherit all permissions of lower roles.
1920

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.
57+
58+
| DDL Operation | Required Role | Notes |
59+
| --- | --- | --- |
60+
| `CREATE DATABASE` | Superuser or ClusterAdmin | Requires cluster-wide privilege |
61+
| `DROP DATABASE` (non-default) | Superuser | Permanent deletion; limited to superuser |
62+
| `DROP DATABASE ... FORCE` | Superuser | Force-drop with cascade safety override |
63+
| `ALTER DATABASE ... RENAME` | Superuser or ClusterAdmin | Cosmetic change; durable identity is `DatabaseId` |
64+
| `ALTER DATABASE ... SET QUOTA` | Superuser or ClusterAdmin | Changes resource limits |
65+
| `ALTER DATABASE ... SET IDLE_TIMEOUT` | Superuser or ClusterAdmin | Session timeout configuration |
66+
| `ALTER DATABASE ... SET AUDIT_DML` | Superuser or ClusterAdmin | Changes audit behavior |
67+
| `ALTER DATABASE ... MATERIALIZE` | Superuser, ClusterAdmin, or DatabaseOwner | Materializes a clone |
68+
| `ALTER DATABASE ... PROMOTE` | Superuser only | One-way mirror promotion; locked to superuser due to operational risk |
69+
| `CLONE DATABASE` | Superuser | Cross-database operation requiring cluster privilege |
70+
| `MIRROR DATABASE` | Superuser | Read-only replica setup |
71+
| `MOVE TENANT` | Superuser | Cross-database tenant relocation |
72+
| `BACKUP DATABASE` | Superuser or DatabaseOwner | Export entire database |
73+
| `RESTORE DATABASE` | Superuser | Disaster recovery operation |
74+
| `KILL SESSION` | Superuser, ClusterAdmin, or session-owner | Terminate a session |
75+
| `CREATE/ALTER/DROP OIDC PROVIDER` | Superuser or ClusterAdmin | SSO configuration |
76+
2077
## Custom Roles
2178

2279
```sql
@@ -55,3 +112,5 @@ AS BEGIN RETURN (SELECT COUNT(*) FROM audit_log); END;
55112
SHOW GRANTS FOR analyst;
56113
SHOW PERMISSIONS;
57114
```
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.

docs/administration/rls.rdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ CREATE RLS POLICY org_filter ON docs FOR READ USING (org_id = $auth.org_id) REST
3131
CREATE RLS POLICY not_deleted ON docs FOR READ USING (status != 'deleted') RESTRICTIVE;
3232
```
3333

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+
3455
## Cross-Engine Behavior
3556

3657
RLS filters are injected at plan time, before engine dispatch:

docs/reference/error-codes.rdx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,56 @@ NodeDB uses `NodeDbError` — a struct with numeric `ErrorCode`, human-readable
3636
| `AUTHORIZATION_DENIED` | 2000 | Check RBAC grants |
3737
| `SYNC_DELTA_REJECTED` | 3001 | CRDT sync constraint failed |
3838

39+
## Database Boundary Errors
40+
41+
| Error | Code | Resolution |
42+
| --- | --- | --- |
43+
| `DATABASE_NOT_FOUND` | 1101 | Verify database name; check `SHOW DATABASES` |
44+
| `CANNOT_DROP_DEFAULT_DATABASE` | 1102 | The `default` database is reserved; drop user databases instead |
45+
46+
## Database Quota Errors (7000–7099)
47+
48+
| Error | Code | Resolution |
49+
| --- | --- | --- |
50+
| `SERVER_OVERLOAD` | 7000 | Cluster under heavy pressure; retry with exponential backoff |
51+
| `TENANT_QUOTA_EXCEEDED` | 7001 | Tenant's rate/concurrency/memory limit exhausted; reduce load or raise quota via `ALTER TENANT … IN DATABASE … SET QUOTA` |
52+
| `DATABASE_QUOTA_EXCEEDED` | 7002 | Database's rate/concurrency/memory limit exhausted; reduce load or raise quota via `ALTER DATABASE … SET QUOTA` |
53+
| `QUOTA_OVERCOMMIT` | 5001 | Sum of tenant quotas exceeds database quota, or database quota exceeds global; rebalance quotas |
54+
| `TENANT_VECTOR_DIM_EXCEEDED` | 1023 | Vector embedding dimensionality exceeds tenant's `max_vector_dim`; reduce dimensionality or raise quota |
55+
| `TENANT_GRAPH_DEPTH_EXCEEDED` | 1024 | Graph traversal depth exceeds tenant's `max_graph_depth`; reduce traversal depth or raise quota |
56+
| `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+
3989
## Error Construction
4090

4191
Errors are created via constructors: `NodeDbError::storage("detail")`, `NodeDbError::collection_not_found("users")`, etc.

docs/reference/limits-defaults.rdx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,36 @@ SDKs surface these via `client.limits()`. See [Native Protocol — Handshake](..
6464
| ------------ | ------------ |
6565
| memory_limit | 1 GiB |
6666
| Docker | 75% of RAM |
67+
68+
## Database Boundary
69+
70+
| Setting | Value | Notes |
71+
| --- | --- | --- |
72+
| `DatabaseId` reserved range | 0–1023 | System databases; user IDs start at 1024 |
73+
| `default` database | `DatabaseId(0)` | Cannot be dropped; always exists |
74+
75+
## Clone & Mirror
76+
77+
| Setting | Value | Notes |
78+
| --- | --- | --- |
79+
| `MAX_CLONE_DEPTH` | 8 | Maximum clone lineage depth; `CLONE_DEPTH_EXCEEDED` if exceeded |
80+
| Mirror lag threshold (async) | 5 s (default) | Configurable per mirror; `MIRROR_DEGRADED` when exceeded |
81+
| Mirror lag threshold (sync) | 100 ms (default) | Configurable per mirror; `MIRROR_DEGRADED` when exceeded |
82+
83+
## Role & Auth
84+
85+
| Setting | Value | Notes |
86+
| --- | --- | --- |
87+
| `MAX_ROLE_INHERITANCE_DEPTH` | 8 | Maximum depth of role inheritance chain |
88+
| Login rate limit (`login_ip`) | 30 per minute | Per IP address; returns `INVALID_CREDENTIALS` after capacity exhausted |
89+
| Login rate limit (`login_user`) | 10 per minute | Per username; returns `INVALID_CREDENTIALS` after capacity exhausted |
90+
91+
## Per-Database Quota Defaults
92+
93+
| Setting | Default | Notes |
94+
| --- | --- | --- |
95+
| `cache_weight` | 1 | Relative weight for doc cache eviction |
96+
| `priority_class` | `standard` | `critical` (own fsync group, committed first), `standard` (batched normally), `bulk` (extended timeout) |
97+
| `maintenance_cpu_pct` | 25 | Percentage of core time available for background compaction |
98+
| `idle_session_timeout_secs` | 0 | Session idle timeout; 0 = disabled |
99+
| `audit_dml` | `none` | `none` (disabled), `writes` (DML inserts/updates/deletes), `all` (includes selects) |

docs/reference/sqlstate-mapping.rdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,15 @@ NodeDB maps internal error codes to PostgreSQL SQLSTATE codes for pgwire compati
2121
| Numeric overflow | 22003 | Numeric value out of range |
2222
| Division by zero | 22012 | Division by zero |
2323
| Rate limit exceeded | 54001 | Too many resources |
24+
| Tenant quota exceeded | 54001 | Too many resources |
25+
| Database quota exceeded | 54001 | Too many resources |
26+
| Server overload | 54001 | Too many resources |
27+
| Quota overcommit | 54000 | Program limit exceeded |
28+
| Database not found | 42P01 | Undefined table |
29+
| Mirror read only | 25006 | Read-only SQL transaction |
30+
| Stale read on mirror | 0A000 | Feature not supported |
31+
| Clone depth exceeded | 54000 | Program limit exceeded |
32+
| Session not found | 42704 | Undefined object |
33+
| Session idle timeout | 08006 | Connection failure |
34+
| Session revoked | 08006 | Connection failure |
2435
| Internal error | XX000 | Internal error |

0 commit comments

Comments
 (0)