Configuration Reference

Every environment variable the control plane reads is parsed in one place — AppConfigService (src/config/app-config.service.ts). The only files allowed to read process.env directly are main.ts, db/migrate.ts, telemetry/telemetry.ts, the auth/{pinned-keys.service,pinned-keys-admin.controller,auth.module}.ts set, and domain-packs/domain-packs.module.ts. Start from .env.example.

.env is loaded automatically at process start via a dotenv/config preload — the first import in main.ts, before anything else runs — so it's populated before AppConfigService is ever constructed (including the manual instance main.ts uses to drive database migrations, ahead of Nest's own bootstrap).

Defaults below are the code defaults. Several variables are fail-fast in production (NODE_ENV !== 'development') — see Startup validation.

Several keys are env-var equivalents of the registry's TOML config (e.g. AUTH_REQUIRE_TENANT ↔ auth.require_tenant, TENANT_AGENTS ↔ [[auth.tenant_agents]], REVOCATION_FEEDS ↔ [[auth.revocation_feeds]], CONTROL_PLANE_PINNED_KEYS ↔ [[playground.pinned_keys]]). They exist so the CP enforces the same rules; the model behind them lives in the registry's CONFIGURATION.md, AUTHENTICATION.md, and MULTI-TENANCY.md.

Core server

VarTypeDefaultMeaning
NODE_ENVstringdevelopmentdevelopment relaxes fail-fast checks.
PORTnumber3001HTTP listen port.
HOSTstring0.0.0.0Bind address.
PUBLIC_HOSTstring''Externally-resolvable host (example.com / example.com:8443) a consumer's did:web resolver hits for /.well-known/did.json. Distinct from HOST. Used to assert the did:web witness↔host binding at boot.
CORS_ORIGINstringhttp://localhost:3000Allowed CORS origin.

Database

VarTypeDefaultMeaning
DATABASE_URLstringpostgres://postgres:postgres@localhost:5432/acdp_control_planePostgres connection string.
DB_POOL_MAXnumber20Max pool connections per replica. Must be ≥ 2.
DB_POOL_IDLE_TIMEOUTnumber (ms)30000Idle connection timeout.
DB_POOL_CONNECTION_TIMEOUTnumber (ms)5000Connection-acquisition timeout.

Authentication & issuance

See AUTH.md.

VarTypeDefaultMeaning
AUTH_API_KEYSCSV''Bearer API keys. Empty = auth bypassed (dev only).
AUTH_ADMIN_API_KEYSCSV''Subset allowed admin ops (revoke any jti, reload pinned keys, read revocation feed, enroll registry, routing stats).
AUTH_REQUIRE_TENANTboolfalseStrict-tenant default-deny. See TENANCY.md.
AUTH_PERSISTENCEmemory|postgresmemoryBackend for challenges/revocations/ledger. postgres required for multi-instance.
AUTH_SWEEP_INTERVAL_SECONDSnumber300Expired-state GC interval; ≤0 disables.
TOKEN_ISSUANCE_ENABLEDboolfalseEnable /auth/challenge + /auth/token + JWT verify path.
JWT_SECRETstring''HS256 signing secret. ≥32 bytes when issuance + HS256.
JWT_SIGNING_ALGHS256|EdDSAHS256Issuance algorithm.
JWT_PRIVATE_KEY_PEMstring (PEM)''Ed25519 PKCS8 private key. Required when issuance + EdDSA.
JWT_KIDstring''Override kid; else derived from key fingerprint.
JWT_AUTHORITYstringcontrol-plane.localiss claim + challenge signing input.
JWT_AUDIENCEstring= JWT_AUTHORITYaud claim bound + required on local verify.
JWT_TTL_SECONDSnumber3600Issued-token TTL. ≥60 when issuance.
CHALLENGE_TTL_SECONDSnumber300Challenge-nonce TTL. ≥30 when issuance.
CONTROL_PLANE_PINNED_KEYSCSV''did=base64[:alg][:from..until]. Verification + emergency revocation.
TRUSTED_ISSUERSCSV''Federated peers. iss|alg|material|audience[|scope]. audience required.
REVOCATION_FEEDSCSV''Peer feeds to poll. issuer|url|admin_token[|poll_seconds].

Tenancy

See TENANCY.md.

VarTypeDefaultMeaning
TENANT_API_KEYSCSV''tenantId:key,…,bareKey. Bare keys → default.
TENANT_AGENTSCSV''tenantId:agent_did,…. Stamps JWT tenant claim.
TENANT_QUOTASstring''Per-tenant quotas. See POLICY.md.

Policy

See POLICY.md.

VarTypeDefaultMeaning
POLICY_BACKENDstatic|opastaticDecision backend.
OPA_URLstringhttp://localhost:8181OPA sidecar base URL.
OPA_PACKAGE_PATHstringacdp/policy/v1OPA package path.
OPA_TIMEOUT_MSnumber1500Per-query timeout.
OPA_FAIL_OPENboolfalseOn OPA error, allow instead of deny.

Ingest

See INGEST.md.

VarTypeDefaultMeaning
WEBHOOK_SECRETstring''Global HMAC secret for inbound webhooks. Empty = verification skipped (dev only — fails startup in production).
INGEST_REQUIRE_ENROLLMENTboolfalseAccept only enrolled authorities.
INGEST_STRICT_TENANTboolfalseUnenrolled authority may not assert non-default tenant.
INGEST_MAX_BODY_BYTESnumber1048576Raw body cap (1 MiB).
INGEST_MAX_JSON_DEPTHnumber64JSON nesting-depth cap.
DOMAIN_PACKSCSV''Active domain packs (e.g. finance); gates custom context_types.

Outbound webhooks

VarTypeDefaultMeaning
WEBHOOK_RETRY_INTERVAL_MSnumber300000Outbox retry-sweep interval; ≤0 disables.
WEBHOOK_SSRF_ALLOW_HTTPboolfalseAllow non-HTTPS subscriber URLs (dev only).
WEBHOOK_SSRF_ALLOW_LOOPBACKboolfalseAllow loopback/localhost subscriber URLs (dev only).

Streaming & infra

VarTypeDefaultMeaning
STREAM_HUB_STRATEGYmemory|redismemorySSE fan-out backend. redis for multi-instance.
REDIS_URLstring''Redis connection (SSE redis strategy + Redis quota store).
STREAM_SSE_HEARTBEAT_MSnumber15000SSE heartbeat interval.

Rate limiting (coarse throttle)

VarTypeDefaultMeaning
THROTTLE_TTL_MSnumber60000Throttle window per (actorId|ip).
THROTTLE_LIMITnumber200Requests per window. /auth/* uses a tighter override.

Receipt audit (RFC-ACDP-0010)

An advisory-locked sweep that makes the CP an independent second observer of registry receipts: it picks unaudited context_published events, cross-checks each embedded registry_receipt, records a verdict in receipt_audits, and surfaces it as the trust member on GET /runs/:runId. The receipt format and the verification procedure it runs are normative in RFC-ACDP-0010 (registry-side runbook: acdp-registry-rs/docs/RECEIPTS.md) — see ARCHITECTURE.md for how the sweep fits the pipeline.

VarTypeDefaultMeaning
RECEIPT_AUDIT_ENABLEDboolfalseEnable the receipt-audit sweep.
RECEIPT_AUDIT_INTERVAL_SECONDSnumber300Sweep interval. ≥5 when enabled.
RECEIPT_AUDIT_BATCH_SIZEnumber50Events audited per sweep. ≥1 when enabled.
RECEIPT_AUDIT_LOOKBACK_HOURSnumber24Only events younger than this are picked up.

Transparency-log witnessing (RFC-ACDP-0012 / RFC-ACDP-0015)

The checkpoint witness polls GET /log/checkpoint on enrolled registries advertising acdp-registry-transparency-log, verifies each checkpoint's signature and its consistency against the last-witnessed head, and alerts on any dishonesty signal (root rewrite, split view, tree-size regression, log reset). The checkpoint/proof formats and checks are normative in RFC-ACDP-0012 and RFC-ACDP-0015; the knobs below are what this service exposes.

VarTypeDefaultMeaning
LOG_WITNESS_ENABLEDboolfalseEnable the checkpoint-witness sweep.
LOG_WITNESS_INTERVAL_SECONDSnumber300Sweep interval. ≥5 when enabled.
LOG_WITNESS_EXCLUDE_AUTHORITIESlist''Authorities never witnessed.

Witness cosigning (RFC-ACDP-0015). When enabled, a checkpoint that passes the §7 obligation (signature valid and consistency from the retained head) is cosigned: the witness mints a signed acdp-log-cosignature over the observed tuple with its own Ed25519 assertionMethod key and serves it at GET /log/witness. Riding the checkpoint witness, it requires LOG_WITNESS_ENABLED=true. A checkpoint that fails the obligation is never cosigned.

VarTypeDefaultMeaning
WITNESS_COSIGNING_ENABLEDboolfalseEnable minting + serving witness cosignatures.
WITNESS_IDstring''The witness's DID (did:web:<this-CP-host> or did:key). Required when enabled.
WITNESS_SIGNING_PRIVATE_KEY_PEMstring''PEM-encoded Ed25519 private key the witness cosigns with. Required when enabled. Dedicated — never the JWT IdP key (§5/§15).
WITNESS_KEY_IDstring''assertionMethod key id (DID URL under WITNESS_ID). Defaults to <WITNESS_ID>#witness-key-1.

Generate the witness key with openssl genpkey -algorithm ed25519. When WITNESS_ID is a did:web, its host must match PUBLIC_HOST so consumers can dereference /.well-known/did.json on this CP — the binding is asserted at boot (RFC-ACDP-0015 §9): a mismatch is fatal, and an unset PUBLIC_HOST only warns. did:key witnesses are exempt (self-describing).

Witness quorum consumption (RFC-ACDP-0015 §8). The mirror of cosigning: instead of minting, evaluate the N-witnessed quorum over the cosignatures a registry aggregates and serves on GET /log/checkpoint (the top-level witness_signatures sibling, §6.1). Each is verified against its witness's own resolved did:web document, and DISTINCT trusted witnesses over the checkpoint's exact (log_id, tree_size, root_hash) tuple are counted — never the CP's own local mint. The count + meets_quorum are recorded on the witnessed head (surfaced on GET /registries/:authority/log-witness per checkpoint and on the dashboard logWitness.headsMeetingQuorum tile). Rides the checkpoint witness, so it requires LOG_WITNESS_ENABLED=true.

VarTypeDefaultMeaning
WITNESS_QUORUM_ENABLEDboolfalseEnable quorum consumption over aggregated cosignatures.
WITNESS_QUORUM_TRUSTEDlist''Witness DIDs whose cosignatures count; others are verified-but-ignored.
WITNESS_QUORUM_MIN_WITNESSESnumber1The N in N-witnessed. ≥1 when enabled.

Log-inclusion audit (RFC-ACDP-0012). The sibling sweep to the checkpoint witness: for stored receipt-bearing publishes from log-advertising registries it proves each context is actually in the registry's log (fetching /log/proof?ctx_id=) and cross-binds against witnessed heads. Verdicts (included | invalid_proof | not_logged | no_log | error) seal once per event in log_inclusion_audits.

VarTypeDefaultMeaning
LOG_INCLUSION_AUDIT_ENABLEDboolfalseEnable the inclusion-audit sweep.
LOG_INCLUSION_AUDIT_INTERVAL_SECONDSnumber300Sweep interval. ≥5 when enabled.
LOG_INCLUSION_AUDIT_BATCH_SIZEnumber50Events audited per sweep. ≥1 when enabled.
LOG_INCLUSION_AUDIT_LOOKBACK_HOURSnumber24Only events younger than this are picked up.

Data retention

VarTypeDefaultMeaning
DATA_RETENTION_ENABLEDboolfalseEnable periodic purge of aged rows.
DATA_RETENTION_TTL_DAYSnumber30Age threshold. ≥1 when enabled.
DATA_RETENTION_INTERVAL_HOURSnumber24Purge-job interval.

Routing

VarTypeDefaultMeaning
BANDIT_EXPLORATION_FRACTIONnumber0.05Fraction of traffic using uniform exploration vs Thompson sampling.

Observability

VarTypeDefaultMeaning
LOG_LEVELstringinfodebug|info|warn|error.
OTEL_ENABLEDboolfalseEnable OpenTelemetry SDK.
OTEL_SERVICE_NAMEstringacdp-control-planeSpan/metric service name.
OTEL_EXPORTER_OTLP_ENDPOINTstring''OTLP endpoint; empty discards traces.
SWAGGER_ENABLEDbooldev: on / prod: offServe Swagger UI.
SWAGGER_PATHstringdocsSwagger UI path.

Misc

VarTypeDefaultMeaning
PLAYGROUND_URLstring''Playground backend for run-completion notifications; empty disables.

Startup validation

AppConfigService.validate() runs at boot. Throws (refuses to start) on:

  • Tenant bindings configured (TENANT_AGENTS or tenant-bound TENANT_API_KEYS) without AUTH_REQUIRE_TENANT=true.
  • Production with empty AUTH_API_KEYS.
  • Production with empty WEBHOOK_SECRET (inbound webhook HMAC verification would otherwise be silently disabled — see src/ingest/hmac.ts).
  • DB_POOL_MAX < 2.
  • DATA_RETENTION_ENABLED=true with DATA_RETENTION_TTL_DAYS < 1.
  • POLICY_BACKEND not in opa; JWT_SIGNING_ALG not in EdDSA.
  • Issuance + HS256 with JWT_SECRET < 32 bytes.
  • Issuance + EdDSA with empty JWT_PRIVATE_KEY_PEM.
  • Issuance with JWT_TTL_SECONDS < 60 or CHALLENGE_TTL_SECONDS < 30.
  • LOG_WITNESS_ENABLED=true with LOG_WITNESS_INTERVAL_SECONDS < 5.
  • RECEIPT_AUDIT_ENABLED=true with RECEIPT_AUDIT_INTERVAL_SECONDS < 5 or RECEIPT_AUDIT_BATCH_SIZE < 1.
  • LOG_INCLUSION_AUDIT_ENABLED=true with LOG_INCLUSION_AUDIT_INTERVAL_SECONDS < 5 or LOG_INCLUSION_AUDIT_BATCH_SIZE < 1.
  • WITNESS_COSIGNING_ENABLED=true without WITNESS_ID, without WITNESS_SIGNING_PRIVATE_KEY_PEM, or without LOG_WITNESS_ENABLED=true. (WitnessSigningService additionally rejects a non-Ed25519 key, a malformed witness DID, or a WITNESS_KEY_ID not under WITNESS_ID — in every environment.)

Warns (starts, but flags a risk) on, in production:

  • STREAM_HUB_STRATEGY=memory (SSE won't sync across replicas).
  • OTEL_ENABLED=true with empty OTEL_EXPORTER_OTLP_ENDPOINT (traces discarded).
  • REVOCATION_FEEDS set with TOKEN_ISSUANCE_ENABLED=false (poller won't run).
  • TOKEN_ISSUANCE_ENABLED=true with AUTH_PERSISTENCE=memory (state not shared).
  • WITNESS_QUORUM_ENABLED=true with empty WITNESS_QUORUM_TRUSTED (no cosignature can ever count toward quorum).