Console
Sign in
Docs for Platform operator
On this page
  1. How configuration is read
  2. Process
  3. Database
  4. Deployment artifacts
  5. Access
  6. Budgets
  7. Worker
  8. Observability
  9. Production checks
  10. Operator commands
  11. Bindings file
  12. Collection manifest
  13. Policy bundle

Configuration

Every environment variable and deployment file Data reads: process settings, database roles, Access, budgets, the worker, operator commands, the gateway bindings file, the collection manifest and the policy bundle. It is the reference for operators.

Read byPlatform operatorSecurity engineerDatabase administratorApplication developer

How configuration is read#

src/runtime/config.ts reads the environment once at startup and validates it as a whole. It reports every problem in one message that starts with the environment is invalid:, and any value it cannot understand stops the process. The behavioral rules are in spec §14.

packages/data/.env.example lists the process variables with development values that are not secrets. Getting started copies it to .env, which Bun loads at startup.

The Required column uses these values: always means every process role; api means roles api and all; worker means roles worker and all.

Process#

VariableDefaultRequiredAllowed valuesMeaning
DATA_HOST127.0.0.1noany hostListener bind address. In production, bind an explicitly protected service interface.
DATA_PORT8082no1 to 65535Listener port.
DATA_PROCESS_ROLEallnoapi, worker, allWhat the process runs (Operations).
DATA_ENVIRONMENTdevelopmentnodevelopment, productionproduction turns on the transport and secret checks of Production checks.
DATA_LOG_LEVELinfonodebug, info, warn, errorLog level.

Database#

VariableDefaultRequiredAllowed valuesMeaning
DATA_DATABASE_URLnonealwayspostgres:// or postgresql:// URLThe scoped data role (data_runtime): rows in the transaction's scope, nothing else.
DATA_REGISTRY_DATABASE_URLnonealwayspostgres:// or postgresql:// URLThe narrow reader of the workspace registry (data_registry).
DATA_DELIVERY_DATABASE_URLthe value of DATA_DATABASE_URLworkerpostgres:// or postgresql:// URLDelivery metadata (data_delivery).
DATA_DB_POOL_MAX20no1 to 200Data-role connections per process.

The registry and delivery pools hold five connections each; their size is not configurable.

Deployment artifacts#

VariableDefaultRequiredAllowed valuesMeaning
DATA_GATEWAY_BINDINGS_FILEnoneapipathAccepted routes and how their identities are verified (Bindings file).
DATA_LEGACY_IDENTITYrefusenorefuse, warn, allowWhat to do with the legacy v4 identity format: refuse stops startup on a version 1 file or a v4 route, warn accepts them and logs a warning naming the routes, allow accepts them. The format is symmetric: Data would hold the gateway's derived secret and could sign an identity with it. 1.0.0 refuses it by default and 2.0.0 removes it.
DATA_SCHEMA_BUNDLE_FILEnonealwayspathCollection manifests (Collection manifest).
DATA_POLICY_BUNDLE_FILEnonealwayspathPolicy bundle (Policy bundle).
DATA_CURSOR_KEY_FILEnonealwaysfile of at least 32 bytesKey that signs page cursors. It is not a gateway key.

Manifests and policies are immutable for the life of a process, and their version is their content digest.

Access#

VariableDefaultRequiredAllowed valuesMeaning
DATA_ACCESS_URLnonealwaysabsolute http or https URLAccess base URL. It must be https in production, because the context credential travels with it.
DATA_ACCESS_CONTEXT_TOKEN_FILEnonealwaysfileService credential for the access context contract.
DATA_CONTEXT_CACHE_TTL_MS0no0 to 5000Lifetime of the read-only context cache. 0 means no cache. Mutations and worker commands always fetch a fresh context.
DATA_CONTEXT_CACHE_MAX_ENTRIES4096no1 to 1000000Context cache size. When it is full, the least recently used entry is evicted; expired entries are dropped on write.
DATA_ACCESS_TIMEOUT_MS1000no1 to 60000Bound on one call to Access.

Budgets#

VariableDefaultAllowed valuesBounds
DATA_REQUEST_TIMEOUT_MS10000100 to 600000All work for one request.
DATA_DB_STATEMENT_TIMEOUT_MS3000100 to 600000One statement; the server cancels it.
DATA_DB_TRANSACTION_TIMEOUT_MS5000100 to 600000One transaction. When the budget is spent the connection is destroyed, so the server rolls back.
DATA_DB_ACQUIRE_TIMEOUT_MS100010 to 600000Waiting for a connection.
DATA_MAX_BODY_BYTES10485761024 to 16777216Request body. A record body is also limited to 256 KiB.
DATA_MAX_IN_FLIGHT_REQUESTS2001 to 10000Concurrent requests in the process. The excess is refused with 503 OVERLOADED.
DATA_MAX_IN_FLIGHT_REQUESTS_PER_TENANTthe value of DATA_MAX_IN_FLIGHT_REQUESTS1 to 10000Concurrent requests per tenant. The default gives one tenant the whole process, so a multi-tenant deployment sets each tenant's room. The excess is 429 TOO_MANY_REQUESTS.
DATA_DRAIN_MS300001000 to 300000Drain budget at shutdown.

None of these variables is required. A request's effective timeout is the smallest applicable budget. A local timer alone never cancels a statement: the statement and transaction timeouts do.

Worker#

VariableDefaultRequiredAllowed valuesMeaning
DATA_WORKER_PRINCIPAL_IDnoneworkerAccess principal UUIDPrincipal the worker acts as.
DATA_WORKER_ROUTE_IDnoneworkerroute idRoute the worker acts on, typically data-worker.
DATA_WORKER_CONCURRENCY4no1 to 64Concurrent deliveries.

Observability#

VariableDefaultRequiredAllowed valuesMeaning
DATA_METRICS_TOKEN_FILEabsentnofileBearer token guarding /metrics. Without it the endpoint answers 404.

The log level is DATA_LOG_LEVEL (Process).

Production checks#

With DATA_ENVIRONMENT=production, startup also refuses:

  1. a DATA_DATABASE_URL or DATA_REGISTRY_DATABASE_URL on a remote host without an sslmode= parameter (use sslmode=require); the hosts 127.0.0.1, localhost, ::1 and postgres count as local,
  2. a DATA_ACCESS_URL that does not start with https://,
  3. a credential that the repository publishes: the file of the access context or the file of the cursor key that holds a value of deploy/fixtures/secrets is refused, and so is a password of a role that the repository publishes. The repository is public, and a deployment that mounts the fixtures holds a credential that everybody knows.

Role privileges, schema drift and the access context version are checked by readiness in every environment (Operations).

Operator commands#

Operator credentials never appear in the API's environment; each command reads only what it needs.

VariableDefaultCommand and meaning
DATA_ADMIN_DATABASE_URLnoneprovision.ts, restore.sh: the server administrator.
DATA_DATABASE_NAMEdataprovision.ts: the database it creates.
DATA_OWNER_PASSWORD, DATA_RUNTIME_PASSWORD, DATA_REGISTRY_PASSWORD, DATA_DELIVERY_PASSWORD, DATA_MAINTENANCE_PASSWORDnoneprovision.ts: role passwords. They are never printed.
DATA_OWNER_DATABASE_URLnonemigrate.ts, workspace.ts, collection.ts, policy.ts: the schema owner. Given to restore.sh, it returns ownership to that role.
DATA_MIGRATIONS_DIR./migrationsmigrate.ts: migrations directory.
DATA_MAINTENANCE_DATABASE_URLnoneretention.ts: the maintenance role.
DATA_AUDIT_RETENTION_DAYS90retention.ts: audit entries.
DATA_VERSIONS_RETENTION_DAYS90retention.ts: record version images.
DATA_RECEIPT_RETENTION_HOURS24retention.ts: creation receipts.
DATA_EVENT_RETENTION_DAYS7retention.ts: completed events and deliveries.
DATA_RETENTION_ACTORoperatorretention.ts: the actor recorded for the run.
DATA_DELIVERY_DATABASE_URLnonedelivery.ts: delivery metadata.
DATA_BACKUP_DATABASE_URL, DATA_BACKUP_DIRnonebackup.sh. The backup role bypasses row-level security, so a backup holds every tenant's rows.
DATA_BACKUP_FILE, DATA_RESTORE_DATABASE, DATA_RUNTIME_DATABASE_URLnonerestore.sh: the dump to restore, the database to restore into, and the API role used to check isolation.

The procedures that use them are in Operations.

Bindings file#

DATA_GATEWAY_BINDINGS_FILE is JSON of the form {"version": 2, "bindings": [...]}, with at most 100 bindings. The matching and verification rules are in spec §4.1.

json
{
  "version": 2,
  "bindings": [
    {
      "id": "data-api",
      "host": "api.gateway.test",
      "upstreamPathPattern": "/ws/{workspaceId}/v1/*",
      "routeId": "data-api",
      "identity": {
        "version": "v5",
        "algorithm": "ES256",
        "publicKeyFile": "./secrets/identity.public.pem"
      }
    }
  ]
}
FieldMeaning
idLocal route name, for diagnostics.
hostExact public authority the gateway signs: lowercase ASCII, with the port if any.
upstreamPathPatternPath Data receives, with exactly one {workspaceId} segment and a terminal /*.
routeIdAccess route scope matching the gateway route.
expectedTenantIdOptional. Restricts the route to one tenant.
identity.versionv5 (current) or v4 (legacy).
identity.algorithmES256 with a public key, HS256 with the derived key.
identity.publicKeyFileGateway public key file, P-256, PEM. Because it is a public key, Data can verify identities but cannot mint them.
identity.identityKeyFileDerived key file for the legacy format.

A legacy route names its format explicitly, and DATA_LEGACY_IDENTITY must permit it:

json
"identity": {
  "version": "v4",
  "algorithm": "HS256",
  "identityKeyFile": "./secrets/identity.key"
}

Moving a deployment off the legacy format is a procedure in Operations.

Collection manifest#

DATA_SCHEMA_BUNDLE_FILE is UTF-8 JSON, at most 1 MiB, validated against a strict schema that refuses unknown properties. How to design a collection is in Modeling.

json
{
  "version": 1,
  "schemaVersion": "articles-v1",
  "collections": [
    {
      "key": "articles",
      "table": "data.articles",
      "versions": { "table": "data.article_versions" },
      "label": "Articles",
      "fields": {
        "title": {
          "type": "string",
          "required": true,
          "nullable": false,
          "maxLength": 240
        }
      },
      "search": { "fields": ["title"], "config": "english" },
      "relations": {
        "category": {
          "target": "categories",
          "type": "many-to-one",
          "field": "category_id"
        }
      }
    }
  ]
}
PartHolds
versionManifest format, 1.
schemaVersionThe deployment's schema version.
keyCollection name used in paths. Matches ^[a-z][a-z0-9_]{0,62}$.
tablePhysical table as schema.table. The deployment controls it; a request never chooses it.
versions.tableOptional. The versions table of a collection that keeps a history (spec §11.6).
fieldsTypes, nullability, defaults, bounds. Types: string, text, boolean, integer, decimal, uuid, timestamp, json, enum. Optional: minLength, maxLength, minimum, maximum, enum, default, label, description.
searchfields of the collection's text search, plus config, the database text-search configuration.
relationsRegistered relations: target, type and field, with an optional label. Version 0.1 supports many-to-one references and derived one-to-many reads.
label, descriptionSemantic metadata. No studio presentation here.
NeverExecutable scripts, widget identifiers, page layouts.
Added by the serviceid, tenant_id, workspace_id, created_by, created_at, updated_by, updated_at, version. Clients never write them.

Policy bundle#

DATA_POLICY_BUNDLE_FILE is UTF-8 JSON, at most 1 MiB, with at most 1000 policies. The same format is published per workspace with bun run policy -- publish (Modeling).

json
{
  "version": 1,
  "policies": [
    {
      "id": "edit-own-articles",
      "permission": "content.editor",
      "collection": "articles",
      "action": "update",
      "where": { "created_by": { "_eq": "$principalId" } },
      "check": { "created_by": { "_eq": "$principalId" } },
      "fields": ["title", "body"]
    }
  ]
}
FieldMeaning
idStable policy name, recorded in the audit.
permissionAccess permission key the policy applies to.
collectionManifest collection.
actionread, create, update or delete.
wherePredicate selecting the rows the policy covers.
checkPredicate on the resulting row of a create or update.
fieldsFields the policy grants.

Predicates use the filter grammar and may use $principalId, $tenantId and $workspaceId. How policies combine is in spec §8.