Skip to main content
Version: 1.7.0

Configuration Reference

DVARA is configured through application.yml with environment variable overrides. Configuration is partitioned across three namespace families:

  • Cross-cutting (dvara.*) — license, audit, encryption, region, credentials, vault, actuator. Every DVARA app reads them.
  • LLM Gateway (dvara.llm-gateway.*) — providers, routes, resilience, rate-limit, cache, PII, guardrails, webhooks, IP access, FinOps, routing strategies.
  • Flightdeck / MCP-only (dvara.flightdeck.* and dvara.mcp-gateway.*) — admin UI, OIDC/SAML, compliance, agentic governance, MCP-proxy timeouts. Bound only by the apps that need them.

Spring relaxed binding maps every property to an env var by uppercasing and replacing dots/hyphens with underscores — dvara.llm-gateway.providers.openai.api-key is settable as DVARA_LLM_GATEWAY_PROVIDERS_OPENAI_API_KEY or, more commonly, via the explicit env-var aliases like OPENAI_API_KEY declared in application.yml (see below).

SaaS-mode property surface

The dvara.flightdeck.saas.* properties (Stripe price IDs, webhook signing secret, checkout URLs) plus the SaaS-funnel dvara.flightdeck.email.* configuration are for the dvarahq.com managed install only. They are intentionally not documented here. Self-hosted and managed-hosting customers do not need them — leave dvara.flightdeck.saas.enabled=false (the default) and the surface stays dark even if the JAR is on the classpath.

Configuration Hierarchy

Configuration is resolved in this order (later sources override earlier ones):

  1. Defaults — hardcoded in application.yml
  2. application.yml — file-based configuration
  3. Environment variables — override any property (e.g., OPENAI_API_KEY)
  4. System properties — JVM -D flags
  5. bootstrap.yaml — when DVARA_BOOTSTRAP_FILE is set, seeds workspaces, API keys, and routes into the database on first startup (idempotent; re-runs skip rows whose id already exists)
  6. Database (Flightdeck writes) — once seeded, ongoing changes to routes, policies, credentials, workspaces, budgets, etc. are made through the Flightdeck UI. These writes hot-reload across the fleet via version-table polling: each write bumps a per-table counter, and every pod runs a scheduled poll (dvara.config.poll-interval-ms, default 3000; first poll delayed by dvara.config.poll-initial-delay-ms, default 3000) that diffs the counters and refreshes only the areas that changed — rebuilding the routing engine (routes), rebuilding the policy index (policies), or evicting the relevant cache (credentials, plugins, semantic cache configs). Polling is gap-immune (a pod that was restarting during a change converges on its next poll) and pooler-agnostic (no session-pinned LISTEN connection, so it works behind transaction-mode connection poolers) — it replaced LISTEN/NOTIFY. No restart required for any persisted-config change.

Steps 1–4 govern boot-time configuration. Steps 5–6 govern persisted state and shadow the file-based config for the entities they own — once a route exists in the database, the YAML's dvara.llm-gateway.routes: entries no longer apply for that route id.

Full Application Configuration

dvara:
# ── Cross-cutting (every DVARA app reads these) ──
license:
key: ${DVARA_LICENSE_KEY:} # signed DVARA- envelope; required at startup
encryption:
master-password: ${DVARA_ENCRYPTION_MASTER_PASSWORD:}
audit:
hmac-secret: ${DVARA_AUDIT_HMAC_SECRET:default-dev-secret-change-in-production}
max-events: ${DVARA_AUDIT_MAX_EVENTS:100000}
store-prompts-by-default: ${DVARA_AUDIT_STORE_PROMPTS:false}
prompt-retention-days: ${DVARA_AUDIT_PROMPT_RETENTION_DAYS:90}
region:
id: ${DVARA_REGION_ID:} # region identity (blank = region-unaware)
name: ${DVARA_REGION_NAME:} # human-readable region name
credentials:
require-workspace-credential: ${DVARA_CREDENTIALS_REQUIRE_TENANT_CREDENTIAL:false}

# ── LLM Gateway ──
llm-gateway:
data-plane:
require-api-key: ${DVARA_LLM_GATEWAY_REQUIRE_API_KEY:false} # set true in production

# ── Provider Configuration ──
providers:
openai:
api-key: ${OPENAI_API_KEY:} # blank = provider disabled
# base-url: https://api.openai.com/v1 # override for Azure / proxy / long-tail OpenAI-compatible upstreams

anthropic:
api-key: ${ANTHROPIC_API_KEY:}

gemini:
api-key: ${GEMINI_API_KEY:}

azure-openai:
api-key: ${AZURE_OPENAI_API_KEY:}
base-url: ${AZURE_OPENAI_BASE_URL:} # required, e.g. https://{resource}.openai.azure.com/openai

mistral:
api-key: ${MISTRAL_API_KEY:}

cohere:
api-key: ${COHERE_API_KEY:}

groq:
api-key: ${GROQ_API_KEY:}

# ── First-class Chinese-region providers ──
qwen:
api-key: ${QWEN_API_KEY:} # Alibaba Dashscope
# base-url defaults to https://dashscope.aliyuncs.com/compatible-mode/v1

deepseek:
api-key: ${DEEPSEEK_API_KEY:}
# base-url defaults to https://api.deepseek.com/v1

moonshot:
api-key: ${MOONSHOT_API_KEY:} # Kimi
# base-url defaults to https://api.moonshot.cn/v1

chatglm:
api-key: ${ZHIPU_API_KEY:} # note: env var is ZHIPU_*, not CHATGLM_*
# base-url defaults to https://open.bigmodel.cn/api/paas/v4

grok:
api-key: ${XAI_API_KEY:} # note: env var is XAI_*, not GROK_*
# base-url defaults to https://api.x.ai/v1

ollama:
enabled: ${OLLAMA_ENABLED:false}
base-url: ${OLLAMA_BASE_URL:http://localhost:11434}

bedrock:
enabled: ${BEDROCK_ENABLED:false}
access-key: ${AWS_ACCESS_KEY_ID:}
secret-key: ${AWS_SECRET_ACCESS_KEY:}
region: ${AWS_REGION:us-east-1}

mock:
enabled: ${MOCK_PROVIDER_ENABLED:false} # dev / CI only — bundles Groovy for scripted matchers
response: "This is a mock response" # static text or `groovy:`-prefixed script
latency-ms: 100 # simulated delay
stream-token-delay-ms: 20 # inter-token SSE delay
error-rate: 0.0 # 0.0–1.0 failure fraction

# ── Route Configuration ──
routes:
- id: load-balance-gpt
model-pattern: "gpt*"
strategy: round-robin
providers:
- provider: openai
- provider: bedrock

- id: weighted-claude
model-pattern: "claude*"
strategy: weighted
providers:
- provider: anthropic
weight: 70
- provider: bedrock
weight: 30

# ── Resilience ──
resilience:
retry:
max-attempts: 3
initial-backoff-ms: 500
backoff-multiplier: 2.0
max-backoff-ms: 10000
circuit-breaker:
failure-rate-threshold: 50
sliding-window-size: 10
minimum-number-of-calls: 5
wait-duration-in-open-state-ms: 30000
permitted-calls-in-half-open: 3
timeout:
chat-timeout-ms: 30000
streaming-timeout-ms: 120000
fallback:
enabled: true

# ── Rate Limiting (always backed by an embedded distributed cache) ──
rate-limit:
enabled: false
per-key:
requests-per-minute: 100 # max requests per API key per 60-second sliding window
tokens-per-minute: 100000 # max tokens per API key per 60-second sliding window

# ── Response Caching (exact-match) ──
cache:
enabled: false
ttl-seconds: 3600
max-size: 10000

# ── Actuator Bearer auth (note: this family binds under `gateway.server.*`, not `dvara.*`) ──
gateway:
server:
api-key: ${DVARA_ACTUATOR_API_KEY:} # Bearer for /actuator/gateway-status + authenticated actuator paths
metrics-api-key: ${DVARA_ACTUATOR_METRICS_API_KEY:} # Distinct Bearer for /actuator/prometheus (must differ from api-key)

# ── Observability ──
management:
endpoints:
web:
exposure:
include: health,prometheus,gateway-status,info # exposed actuator endpoints
exclude: env,heapdump,threaddump,beans,mappings,configprops,loggers,scheduledtasks,caches,sessions,quartz # dangerous endpoints — return 404 regardless of auth
endpoint:
health:
show-details: when-authorized # `always` is rejected at boot; use `when-authorized` or `never`
prometheus:
metrics:
export:
enabled: true # enable Prometheus scrape endpoint
tracing:
sampling:
probability: ${TRACING_SAMPLING_PROBABILITY:1.0} # 0.0–1.0
otlp:
tracing:
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318/v1/traces}

# ── JVM and logging ──
spring:
threads:
virtual:
enabled: true # virtual threads
# profiles:
# active: log-plain # switch from JSON to plain-text logging

server:
port: 8080

Environment Variable Reference

Environment VariablePropertyDescription
OPENAI_API_KEYdvara.llm-gateway.providers.openai.api-keyOpenAI API key
ANTHROPIC_API_KEYdvara.llm-gateway.providers.anthropic.api-keyAnthropic API key
GEMINI_API_KEYdvara.llm-gateway.providers.gemini.api-keyGoogle Gemini API key
OLLAMA_ENABLEDdvara.llm-gateway.providers.ollama.enabledEnable Ollama provider
OLLAMA_BASE_URLdvara.llm-gateway.providers.ollama.base-urlOllama server URL
BEDROCK_ENABLEDdvara.llm-gateway.providers.bedrock.enabledEnable AWS Bedrock provider
AWS_ACCESS_KEY_IDdvara.llm-gateway.providers.bedrock.access-keyAWS access key for Bedrock
AWS_SECRET_ACCESS_KEYdvara.llm-gateway.providers.bedrock.secret-keyAWS secret key for Bedrock
AWS_REGIONdvara.llm-gateway.providers.bedrock.regionAWS region for Bedrock
AZURE_OPENAI_API_KEYdvara.llm-gateway.providers.azure-openai.api-keyAzure OpenAI API key
AZURE_OPENAI_BASE_URLdvara.llm-gateway.providers.azure-openai.base-urlAzure OpenAI deployment URL (required)
MISTRAL_API_KEYdvara.llm-gateway.providers.mistral.api-keyMistral API key
COHERE_API_KEYdvara.llm-gateway.providers.cohere.api-keyCohere API key
GROQ_API_KEYdvara.llm-gateway.providers.groq.api-keyGroq API key
QWEN_API_KEYdvara.llm-gateway.providers.qwen.api-keyAlibaba Qwen / Dashscope API key. Default base URL https://dashscope.aliyuncs.com/compatible-mode/v1.
DEEPSEEK_API_KEYdvara.llm-gateway.providers.deepseek.api-keyDeepSeek API key. Default base URL https://api.deepseek.com/v1.
MOONSHOT_API_KEYdvara.llm-gateway.providers.moonshot.api-keyMoonshot / Kimi API key. Default base URL https://api.moonshot.cn/v1.
ZHIPU_API_KEYdvara.llm-gateway.providers.chatglm.api-keyChatGLM / Zhipu API key — note the env-var name is ZHIPU_*, not CHATGLM_* (matches the brand's API auth header). Default base URL https://open.bigmodel.cn/api/paas/v4.
XAI_API_KEYdvara.llm-gateway.providers.grok.api-keyxAI Grok API key — note the env-var name is XAI_*, not GROK_* (matches xAI's auth convention). Default base URL https://api.x.ai/v1.
MOCK_PROVIDER_ENABLEDdvara.llm-gateway.providers.mock.enabledEnable mock provider for testing. Dev / CI only — the Mock provider bundles Groovy for scenario scripting, which means arbitrary code execution inside the gateway JVM. A startup WARN fires whenever Mock is enabled outside the dev / test / ci / local / default Spring profile allow-list.
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_RESPONSEdvara.llm-gateway.providers.mock.responseDefault mock response text (or groovy:-prefixed script)
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_LATENCY_MSdvara.llm-gateway.providers.mock.latency-msSimulated latency per call in ms (default: 100)
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_STREAM_TOKEN_DELAY_MSdvara.llm-gateway.providers.mock.stream-token-delay-msInter-token delay for streaming (default: 20)
DVARA_LLM_GATEWAY_PROVIDERS_MOCK_ERROR_RATEdvara.llm-gateway.providers.mock.error-rateProbability [0.0, 1.0] of injected PROVIDER_ERROR
dvara.llm-gateway.providers.mock.matchersWiremock-style conditional matchers (YAML list — see Provider Setup → Mock)
dvara.llm-gateway.providers.mock.scenarios-dirDirectory of *.groovy scenario files with hot reload
dvara.llm-gateway.providers.mock.console-authoringOpt in to the DVARA Flightdeck scenario-authoring UI at /mock-scenarios (default: false)
dvara.llm-gateway.providers.mock.audit-sample-rateFraction of MOCK_MATCHER_FIRED events to audit (default: 1.0)
DVARA_REGION_IDdvara.region.idRegion identity for this gateway instance
DVARA_REGION_NAMEdvara.region.nameHuman-readable region name
TRACING_SAMPLING_PROBABILITYmanagement.tracing.sampling.probabilityTrace sampling rate (0.0–1.0, default: 1.0)
OTEL_EXPORTER_OTLP_ENDPOINTmanagement.otlp.tracing.endpointOTLP HTTP endpoint for trace export
DVARA_FLIGHTDECK_COMPLIANCE_SOC2_SCHEDULEdvara.flightdeck.compliance.soc2-scheduleCron expression for scheduled SOC2 reports
DVARA_FLIGHTDECK_COMPLIANCE_HIPAA_SCHEDULEdvara.flightdeck.compliance.hipaa-scheduleCron expression for scheduled HIPAA reports
DVARA_FLIGHTDECK_COMPLIANCE_GDPR_SCHEDULEdvara.flightdeck.compliance.gdpr-scheduleCron expression for scheduled GDPR reports
DVARA_FLIGHTDECK_COMPLIANCE_RBI_SCHEDULEdvara.flightdeck.compliance.rbi-scheduleCron expression for scheduled India RBI reports (blank = disabled)
DVARA_FLIGHTDECK_COMPLIANCE_SEBI_SCHEDULEdvara.flightdeck.compliance.sebi-scheduleCron expression for scheduled India SEBI reports (blank = disabled)
DVARA_FLIGHTDECK_COMPLIANCE_DEFAULT_WORKSPACE_IDdvara.flightdeck.compliance.default-workspace-idWorkspace for scheduled reports (blank = all)
DVARA_FLIGHTDECK_COMPLIANCE_RETENTION_DAYSdvara.flightdeck.compliance.retention-daysReport retention period (default: 365)
DVARA_LLM_GATEWAY_PII_ENABLEDdvara.llm-gateway.pii.enabledEnable PII detection (default: true)
DVARA_LLM_GATEWAY_PII_DEFAULT_ACTIONdvara.llm-gateway.pii.default-actionDefault PII action: LOG, BLOCK, REDACT
DVARA_LLM_GATEWAY_PII_SCAN_RESPONSESdvara.llm-gateway.pii.scan-responsesScan LLM responses for PII (default: true)
DVARA_LLM_GATEWAY_PII_AUTO_DETOKENIZE_RESPONSEdvara.llm-gateway.pii.auto-detokenize-responseRestore {{PII_TYPE}} tokens in non-streaming responses after cache write; emits PII_RESPONSE_DETOKENIZED (default: false)
DVARA_LLM_GATEWAY_PII_STRIP_BEFORE_CACHEdvara.llm-gateway.pii.strip-before-cacheRedact PII before caching (default: true)
DVARA_PII_TOKEN_ENCRYPTION_MASTER_PASSWORDdvara.pii.token-encryption-master-passwordAES-256-GCM key for PII token encryption
DVARA_LLM_GATEWAY_PII_MAX_TOKENS_PER_TENANTdvara.llm-gateway.pii.max-tokens-per-workspaceMax PII tokens per workspace (default: 50000)
DVARA_LLM_GATEWAY_PII_TOKEN_RETENTION_DAYSdvara.llm-gateway.pii.token-retention-daysPII token retention period (default: 30)
DVARA_LICENSE_KEYdvara.license.keySigned DVARA license key for enterprise features
DVARA_ACTUATOR_API_KEYgateway.server.api-keyShared-secret Bearer required on /actuator/gateway-status and every authenticated actuator path except /actuator/prometheus. Generate with openssl rand -base64 32. Set the same value on the DVARA Flightdeck so it can fetch the rich status payload that powers the License page. The legacy property name gateway.control-plane.api-key (env: GATEWAY_CONTROL_PLANE_API_KEY) is accepted as a one-release fallback with a deprecation WARN; the legacy property name is removed in 1.1.0-GA. The current DVARA_ACTUATOR_API_KEY env var is the canonical name and not subject to removal.
DVARA_ACTUATOR_METRICS_API_KEYgateway.server.metrics-api-keyDistinct Bearer required only on /actuator/prometheus. Generate independently — a leaked metrics-scrape token must not unlock the license envelope. Configure your Prometheus scrape job's bearer_token_file to point at this value.
DVARA_LLM_GATEWAY_REQUIRE_API_KEYdvara.llm-gateway.data-plane.require-api-keyRequire a valid API key on every /v1/* request (default: false). When true, requests without a valid Authorization: Bearer header are rejected with 401.
DVARA_CREDENTIALS_REQUIRE_TENANT_CREDENTIALdvara.credentials.require-workspace-credentialStrict-BYOK enforcement (default: false). When true, workspaces without their own active provider credential are rejected with WORKSPACE_CREDENTIAL_REQUIRED (HTTP 403) instead of borrowing the platform-default credential. Per-workspace override via Workspace.metadata["credentials.require-workspace-credential"].
dvara.credentials.grace-expiry-initial-delay-msInitial delay before the first credential grace-expiry sweep runs after startup (default: 60000).
dvara.credentials.grace-expiry-interval-msInterval between credential grace-expiry sweeps (default: 60000). Each sweep transitions expired GRACE credentials to SUPERSEDED and emits a CREDENTIAL_GRACE_EXPIRED audit event.
DVARA_FLIGHTDECK_PLAYGROUND_ENABLEDdvara.flightdeck.playground.enabledEnable the Flightdeck Playground (Console /playground for owner + Portal /portal/playground for workspace admin / developer; default: true). Set false in production to prevent ad-hoc LLM calls from the Console + Portal.
DVARA_ENCRYPTION_MASTER_PASSWORDdvara.encryption.master-passwordAES-256-GCM master password for ENC: decryption and database credential storage. Required for credentials created in ENCRYPTED storage mode (the default). Optional for REFERENCE-only deployments — REFERENCE credentials store only a vault pointer and never hit the AES path, so a zero-trust deployment that creates all credentials with storage_mode=REFERENCE can omit this property entirely.
DVARA_FLIGHTDECK_SECURITY_ENABLEDdvara.flightdeck.security.enabledEnable authentication for admin endpoints (default: true). When true with no OIDC issuer-uri, built-in email/password auth activates.
DVARA_FLIGHTDECK_SECURITY_OIDC_ISSUER_URIdvara.flightdeck.security.oidc.issuer-uriOIDC issuer for JWT validation
DVARA_FLIGHTDECK_SECURITY_OIDC_AUDIENCEdvara.flightdeck.security.oidc.audienceExpected JWT audience
DVARA_FLIGHTDECK_SECURITY_OIDC_ROLE_CLAIMdvara.flightdeck.security.oidc.role-claimJWT claim for roles (default: roles)
DVARA_FLIGHTDECK_SECURITY_RBAC_ENABLEDdvara.flightdeck.security.rbac.enabledEnable URL-pattern RBAC (default: true)
DVARA_AUDIT_HMAC_SECRETdvara.audit.hmac-secretHMAC-SHA256 key for signing audit events
DVARA_AUDIT_MAX_EVENTSdvara.audit.max-eventsAudit store capacity (default: 100000)
DVARA_VAULT_BACKENDdvara.vault.backendVault backend: hashicorp, aws-secrets-manager, azure-key-vault
DVARA_LLM_GATEWAY_IP_ACCESS_ENABLEDdvara.llm-gateway.ip-access.enabledEnable IP allowlist/denylist (default: false)
DVARA_LLM_GATEWAY_WEBHOOKS_ENABLEDdvara.llm-gateway.webhooks.enabledEnable webhook delivery (default: true)
DVARA_DB_POOL_SIZEspring.datasource.hikari.maximum-pool-sizeJDBC connection pool size (default: 2 for data plane)
DVARA_CONFIG_FILEPath to gateway.yaml (default: ./gateway.yaml)
DVARA_API_KEYStatic API key for standalone mode
DVARA_BOOTSTRAP_FILEPath to bootstrap.yaml for first-startup seeding

Provider Credential Management

Provider API keys (OpenAI, Anthropic, Gemini, etc.) can be configured through three methods. The gateway checks them in priority order — the first match wins:

1. Database credential (DVARA Flightdeck / API) → highest priority
2. Vault (HashiCorp, AWS SM, Azure KV) → middle priority
3. Environment variable / property → lowest priority (fallback)

Method 1: Environment Variables (simplest)

Set the API key as an environment variable. This is the quickest way to get started:

export OPENAI_API_KEY=sk-your-key
export ANTHROPIC_API_KEY=sk-ant-your-key

The gateway reads these at startup. To change a key, you must restart the gateway.

Store credentials in the database through the DVARA Flightdeck or REST API. Keys are encrypted with AES-256-GCM before storage and decrypted at request time. No restart needed for rotation.

Prerequisites: Set DVARA_ENCRYPTION_MASTER_PASSWORD for AES encryption.

Via the DVARA Flightdeck:

  1. Navigate to CredentialsAdd Credential
  2. Select the provider and enter the API key
  3. The key is encrypted and stored — it takes effect immediately

Via the API: Manage these from the Console's Credentials page (/credentials), or the workspace Portal at /portal/credentials:

  • Create — pick the provider and secret key, then either paste the secret (ENCRYPTED, the default) or give a vault path (REFERENCE, which stores only the pointer).
  • Rotate — supply the new secret and a grace period. The old credential keeps resolving until it expires, so in-flight requests are not cut off.
  • Revoke — resolution falls back down the chain to the vault or the environment variable.

Delegate credential storage to an external secrets manager. See Vault Configuration below.

How Priority Works — Examples

Example 1: Database overrides env var

# Env var set at startup
OPENAI_API_KEY=sk-old-key

# Later, admin adds a credential via UI for provider.openai.api-key
# → Gateway immediately uses the database credential, not the env var

Example 2: Revoke falls back to env var

# Database credential for OpenAI: ACTIVE
# Env var: OPENAI_API_KEY=sk-fallback-key

# Admin revokes the database credential
# → Gateway falls back to sk-fallback-key from the env var

Example 3: Database + vault coexistence

# Vault has provider.openai.api-key = sk-from-vault
# Database has ACTIVE credential for provider.openai.api-key = sk-from-db

# → Gateway uses sk-from-db (database wins over vault)
# If database credential is revoked → sk-from-vault is used
# If vault entry is also removed → falls back to OPENAI_API_KEY env var

:::tip When to use which method

  • Environment variables — local development, CI/CD, single-operator setups
  • DVARA Flightdeck / API — team environments where multiple people manage credentials, or when you need rotation without restarts
  • Vault — production environments with strict secret management policies :::

Authentication Configuration

DVARA uses a two-domain authentication model: the data plane and the admin API are secured independently.

  • Data plane (/v1/chat/completions, /v1/embeddings, etc.) is always protected by API keys. Every request must include a valid Authorization: Bearer <api-key> header. This is active regardless of the dvara.flightdeck.security.enabled setting.

  • Console and Portal authentication is enabled by default (dvara.flightdeck.security.enabled=true). Three authentication modes are available:

    1. Built-in email/password (default) — activates when security.enabled=true and no OIDC issuer-uri or SAML metadata-url is set. On first visit, /setup creates the initial admin account. No external IdP required.
    2. OIDC/JWT — activates when dvara.flightdeck.security.oidc.issuer-uri is set. Requires an external IdP (Keycloak, Okta, Azure AD, Auth0).
    3. SAML 2.0 SSO — activates when dvara.flightdeck.security.saml.metadata-url is set (mutually exclusive with OIDC).
dvara:
flightdeck:
security:
enabled: true # Default: admin API requires authentication
# Built-in auth activates automatically when no OIDC/SAML is configured.
# To use OIDC instead, set the issuer-uri:
oidc:
issuer-uri: ${DVARA_FLIGHTDECK_SECURITY_OIDC_ISSUER_URI:}
audience: ${DVARA_FLIGHTDECK_SECURITY_OIDC_AUDIENCE:}
role-claim: ${DVARA_FLIGHTDECK_SECURITY_OIDC_ROLE_CLAIM:roles}
name-claim: ${DVARA_FLIGHTDECK_SECURITY_OIDC_NAME_CLAIM:name}
workspace-claim: ${DVARA_FLIGHTDECK_SECURITY_OIDC_WORKSPACE_CLAIM:workspace_id}
rbac:
enabled: true # URL-pattern RBAC when security is enabled
session:
timeout-seconds: 3600
This disables all admin authentication

Setting dvara.flightdeck.security.enabled=false disables all admin authentication. Anyone with network access to port 8090 can modify gateway configuration. Only use this for local development.

When dvara.flightdeck.security.enabled=true:

  • Without OIDC or SAML configured, built-in email/password authentication activates automatically. The first user is created via the /setup page. Additional users are invited by existing admins.
  • With issuer-uri configured, JWT tokens are validated against the configured issuer via OIDC discovery.
  • Roles are extracted from the JWT claim specified by role-claim and mapped to DVARA's six built-in roles: owner, policy-admin, billing-admin (platform), plus admin, developer, viewer (workspace).
  • When rbac.enabled=true (the default when security is on), URL-pattern RBAC rules enforce fine-grained access to each admin endpoint.
  • When rbac.enabled=false, any authenticated user can access all admin endpoints.

Workspace Configuration

Per-workspace configuration is not set via YAML files. Instead, workspace settings are managed from the Console's workspace form as metadata key-value pairs on each workspace object.

This means workspace-level customization (PII policy, guardrail behavior, cost controls, IP access, approval gates, etc.) is dynamic and does not require a gateway restart.

# Per-workspace config is set via Workspace.metadata (not YAML)
# Managed from the Console's workspace form, or via GitOps import
# Example metadata keys:
# priority-tier: premium|standard|bulk
# pii.enabled: true
# pii.action: BLOCK|REDACT|LOG
# pii.custom-patterns: {"label": "regex"}
# pii.scan-responses: true
# guardrail.enabled: true
# guardrail.action: BLOCK|FLAG|LOG
# guardrail.risk-score-threshold: 0.7
# cost.downgrade-threshold-pct: 80
# cost.downgrade-rules: "gpt-4o:gpt-4o-mini,claude-3-opus:claude-3-sonnet"
# cost.anomaly-threshold-pct: 200
# ip-access.allowlist: "10.0.0.0/8,172.16.0.0/12"
# ip-access.denylist: "192.168.1.100/32"
# approval.required-tools: "database_*,filesystem_*"
# approval.required-servers: "server-a,server-b"
# approval.timeout-seconds: 300
# audit.store-prompts: true
# agentic.loop-detection.enabled: true
# agentic.loop-detection.repetition-threshold: 5
# agentic.loop-detection.auto-kill: false

Example API call to set workspace metadata:

Set these on the Console's workspace form, or as a metadata object on the workspace in the GitOps export document.

Global defaults for PII, guardrails, budgets, and other enterprise features are configured in application.yml under their respective namespaces (e.g., dvara.llm-gateway.pii.*, dvara.llm-gateway.guardrail.*, dvara.llm-gateway.finops.*). Workspace metadata overrides these global defaults on a per-workspace basis.

Per-Workspace Metadata Key Reference

Every per-workspace override key the gateway recognises. Values are strings unless noted; booleans accept true/false/yes/no/on/off/1/0 (case-insensitive) per Spring's relaxed binding.

KeyTypeSubsystemBehaviour
priority-tierpremium / standard / bulkRoutingWorkspace's priority class for concurrency-based admission control. See Priority routing.
rate-limit.requests-per-minuteintRate limitPer-workspace override of the global dvara.llm-gateway.rate-limit.per-key.requests-per-minute cap for every API key belonging to this workspace.
rate-limit.tokens-per-minuteintRate limitPer-workspace override of the global dvara.llm-gateway.rate-limit.per-key.tokens-per-minute cap for every API key belonging to this workspace.
credentials.require-workspace-credentialbooleanBYOKNarrows or relaxes the global strict-BYOK policy (dvara.credentials.require-workspace-credential) for this workspace. Useful for tiered plans where free-tier workspaces share the platform-default credential and paid tiers must BYOK.
pat.max-ttl-daysint (≤ 365)AuthNarrows the platform PAT TTL ceiling for this workspace. Hard cap of 365 always applies.
pii.enabledbooleanPIIOverride PII detection enable/disable for this workspace.
pii.actionBLOCK / REDACT / LOGPIIOverride default PII action.
pii.scan-responsesbooleanPIIOverride response scanning.
pii.auto-detokenize-responsebooleanPIINarrows or enables the global auto-detokenize opt-in for this workspace.
pii.scan-streaming-responsesbooleanPIIOverride streaming-response scanning.
pii.custom-patternsJSON mapPIICustom regex patterns: {"label": "regex", ...}
embedded.enabled-filtersCSV of FilterType namesPIIRestrict the embedded scanner to a subset of its 22 default filter types for this workspace.
guardrail.enabledbooleanGuardrailsOverride guardrail detection.
guardrail.actionBLOCK / FLAG / LOGGuardrailsOverride default action.
guardrail.risk-score-thresholddouble (0.0–1.0)GuardrailsOverride risk-score threshold.
guardrail.semantic.enabledbooleanGuardrailsNarrows or enables the semantic prompt guard for this workspace.
guardrail.semantic.similarity-thresholddouble (0.0–1.0)GuardrailsOverride the semantic-guard cosine match threshold.
guardrail.semantic.deny-intentsJSON mapGuardrailsPer-workspace deny-intent exemplars: {"intent-name": ["phrase", ...]}.
guardrail.scan-streaming-responsesbooleanGuardrailsOverride streaming-response scanning.
guardrail.max-input-tokensintGuardrailsOverride OWASP LLM10 input-token cap.
guardrail.max-messages-per-requestintGuardrailsOverride message count cap.
guardrail.max-message-lengthintGuardrailsOverride per-message char cap.
guardrail.default-max-response-tokensintGuardrailsOverride applied when client omits max_tokens.
guardrail.injection.custom-patternsJSON mapGuardrailsCustom injection patterns.
guardrail.content.profanity.actionBLOCK / FLAG / LOGGuardrailsPer-category action override.
guardrail.content.violence.actionBLOCK / FLAG / LOGGuardrailsPer-category action override.
guardrail.content.sexual.actionBLOCK / FLAG / LOGGuardrailsPer-category action override.
guardrail.content.competitor.keywordsCSVGuardrailsCompetitor brand keywords.
guardrail.content.topic-restrictionsCSVGuardrailsRestricted topic keywords.
guardrail.content.custom-denylistJSON mapGuardrailsCustom deny-list patterns.
guardrail.mcp-injection.enabledbooleanGuardrailsEnable MCP injection scanning.
guardrail.mcp-injection.actionBLOCK / FLAG / SANITIZEGuardrailsMCP injection action.
guardrail.context.warning-threshold-pctint (0–100)GuardrailsContext-window warning threshold (default 70).
guardrail.context.hard-threshold-pctint (0–100)GuardrailsContext-window hard threshold (default 90).
guardrail.context.pruning-strategyNONE / TRUNCATE_OLDEST / TRUNCATE_MIDDLEGuardrailsPruning strategy on context overflow.
guardrail.pluginsJSON mapGuardrailsPer-plugin config: {"pluginName": {"enabled": false, ...}}
grounding.enabledbooleanGroundingOverride embedding-based hallucination detection.
grounding.actionBLOCK / FLAG / LOGGroundingOverride action on ungrounded claims.
grounding.max-sourcesintGroundingOverride source-document cap.
grounding.max-source-lengthintGroundingOverride per-source char cap.
cost.downgrade-threshold-pctint (0–100)FinOpsBudget utilization % at which model downgrade triggers (default: 80).
cost.downgrade-rulesCSV from:to pairsFinOpsModel downgrade rules, e.g. gpt-4o:gpt-4o-mini,claude-3-opus:claude-3-sonnet.
cost.anomaly-threshold-pctintFinOpsPer-workspace override of dvara.llm-gateway.finops.anomaly-threshold-pct (default: 200 = 2× baseline).
cost.per-call-max-usddoubleFinOpsPer-workspace override of dvara.llm-gateway.finops.per-call-max-cost-usd. A positive value narrows or raises this workspace's per-call ceiling — and enables the gate for the workspace even when the global ceiling is 0 (disabled). Only ever adds a restriction for that workspace.
ip-access.allowlistCSV CIDRsNetworkPer-workspace IP allowlist.
ip-access.denylistCSV CIDRsNetworkPer-workspace IP denylist.
approval.required-toolsCSV glob patternsAgenticTools that require human-in-the-loop approval (e.g. database_*,filesystem_*).
approval.required-serversCSV server IDsAgenticMCP servers whose every tool call requires approval.
approval.timeout-secondsintAgenticPer-workspace approval timeout override.
approval.default-actionapprove / denyAgenticAction on timeout.
agentic.loop-detection.enabledbooleanAgenticOverride loop detection.
agentic.loop-detection.repetition-thresholdintAgenticOverride consecutive same-tool threshold.
agentic.loop-detection.max-calls-per-minuteintAgenticOverride rate-limit threshold.
agentic.loop-detection.auto-killbooleanAgenticAuto-kill session on loop detection.
audit.store-promptsbooleanAuditOpt-in prompt storage for compliance.
audit.archive.retention-daysintAuditPer-workspace override for the archive job's PG retention window.

Set any of these keys from the Console's workspace form, or in a GitOps config/import document; unsetting is null (the global default takes over).

Bootstrap File Seeding

Set DVARA_BOOTSTRAP_FILE to a YAML file path to seed workspaces, API keys, and routes on first startup. This is designed for first-time workspace seeding before the Flightdeck UI is in use; entries are idempotently upserted on every boot, so re-running with the same file is safe.

docker run -d --name dvara-gateway \
-p 8080:8080 \
-v $(pwd)/bootstrap.yaml:/etc/dvara/bootstrap.yaml \
-e DVARA_BOOTSTRAP_FILE=/etc/dvara/bootstrap.yaml \
ghcr.io/dvarahq/dvara-gateway:1.7.0

The bootstrap loader is idempotent: it checks the config version and skips seeding if any configuration has already been applied (from a previous startup or from gateway.yaml).

workspaces:
- id: acme-corp
name: Acme Corp
status: active

api_keys:
- workspace: acme-corp
name: production-key
key: ${ACME_API_KEY} # env var reference
- workspace: acme-corp
name: dev-key
generate: true # auto-generated, printed at startup

routes:
- id: gpt-route
model: "gpt*"
provider: openai

Notes on the bootstrap flow:

  • The file performs idempotent first-startup seeding of workspaces, API keys, and routes. Re-running with the same file is safe — entries are upserted, never duplicated. BootstrapLoader runs after the gateway has booted (DVARA_LICENSE_KEY is optional — it activates the MCP and A2A planes; everything else runs without one — see Container images & license posture).
  • It supports ${ENV_VAR} and ${ENV_VAR:-default} substitution; resolution is performed by the gateway's YAML loader before parsing, so any Spring Environment source — env vars, JVM -D props, Kubernetes ConfigMaps — feeds back in.
  • The legacy GATEWAY_BOOTSTRAP_FILE env var is accepted for one release with a deprecation WARN (removed in 1.1.0-GA). Switch to DVARA_BOOTSTRAP_FILE for new deployments.
  • After first startup, ongoing config changes are made through the Flightdeck UI; subsequent boots skip rows whose id already exists in the database.

Provider Activation

A provider bean is only registered when its activation condition is met:

ProviderActivation Condition
OpenAIOPENAI_API_KEY is set and non-blank
AnthropicANTHROPIC_API_KEY is set and non-blank
GeminiGEMINI_API_KEY is set and non-blank
OllamaOLLAMA_ENABLED=true
BedrockBEDROCK_ENABLED=true
Azure OpenAIAZURE_OPENAI_API_KEY + AZURE_OPENAI_BASE_URL both set
MistralMISTRAL_API_KEY is set and non-blank
CohereCOHERE_API_KEY is set and non-blank
GroqGROQ_API_KEY is set and non-blank
QwenQWEN_API_KEY is set and non-blank
DeepSeekDEEPSEEK_API_KEY is set and non-blank
MoonshotMOONSHOT_API_KEY is set and non-blank
ChatGLMZHIPU_API_KEY is set and non-blank
GrokXAI_API_KEY is set and non-blank
MockMOCK_PROVIDER_ENABLED=true

If no provider is configured for a requested model, the gateway returns HTTP 400 with error code no_provider.

Property Reference

dvara.llm-gateway.providers.*

See Provider Setup for per-provider configuration details.

dvara.llm-gateway.routes[]

PropertyTypeRequiredDefaultDescription
idstringyesUnique route identifier
model-patternstringyesGlob pattern to match model names
strategystringnomodel-prefixmodel-prefix, round-robin, weighted, latency-aware, cost-aware, canary, geo-aware, intelligent
cost-tolerance-pctintno0Cost tolerance band (0–100) for cost-aware routing — providers within this % above the cheapest may be preferred by the latency tiebreak. Not read by other strategies.
latency-sla-mslongno0Latency SLA in ms for cost-aware routing (0 = no SLA)
model-tiersmapnoComplexity→model mapping for intelligent routing (keys: SIMPLE, MODERATE, COMPLEX)
providers[].providerstringyesProvider name
providers[].weightintno1Weight for weighted strategy
providers[].regionstringnoRegion affinity for this provider entry
pinned-model-versionstringnoOverride model name sent to provider

dvara.llm-gateway.resilience.*

PropertyTypeDefaultDescription
resilience.enabledbooleantrueMaster switch for retries, timeouts and the circuit breaker. Off, every row below is inert and a provider failure surfaces to the caller unretried.
resilience.retry.max-attemptsint3Max retry attempts on failure
resilience.retry.wait-duration-msint500Wait between retries (ms)
resilience.circuit-breaker.failure-rate-thresholdint50Failure % to open circuit
resilience.circuit-breaker.sliding-window-sizeint20Calls in sliding window
resilience.circuit-breaker.wait-duration-in-open-state-msint30000Time before half-open (ms)
resilience.timeout.chat-timeout-msint30000Non-streaming request timeout
resilience.timeout.streaming-timeout-msint120000Streaming request timeout

dvara.llm-gateway.rate-limit.*

PropertyTypeDefaultDescription
rate-limit.enabledbooleanfalseEnable rate limiting
rate-limit.per-key.requests-per-minuteint100Max requests per API key per 60-second window. Per-workspace override via Workspace.metadata["rate-limit.requests-per-minute"].
rate-limit.per-key.tokens-per-minuteint100000Max tokens per API key per 60-second window. Per-workspace override via Workspace.metadata["rate-limit.tokens-per-minute"].
rate-limit.override-cache-ttl-secondsint5TTL for the per-workspace rate-limit override cache read on the hot /v1/* path. The override propagates within this window with no restart; a missing / non-numeric / non-positive metadata value logs a WARN and falls back to the global default.

dvara.llm-gateway.provider-rate-limit.* (Enterprise — Upstream Provider Rate-Limit Tracking)

Distinct from the per-API-key client limiter above. This reacts to each upstream credential's published rate-limit headers (x-ratelimit-remaining-*, anthropic-ratelimit-*, Retry-After on any 429) and proactively sheds — or fails over to the next provider — before forwarding, so the circuit breaker never opens with cross-workspace collateral damage. State is keyed per credential; a shared platform key is one window across workspaces, BYOK keys are isolated. Fail-open — cold start / disabled / any tracker error → allow. On a terminal shed the client gets HTTP 429 + Retry-After (PROVIDER_RATE_LIMITED). The per-workspace tier comes from the existing Workspace.metadata["priority-tier"] (no separate key).

PropertyTypeDefaultDescription
provider-rate-limit.enabledbooleanfalseMaster switch. When false, the tracker short-circuits to allow — behaviour unchanged.
provider-rate-limit.token-headroom-pctint15Slack reserved over the estimated input tokens when checking the remaining token budget (the token estimate is approximate).
provider-rate-limit.tier.bulk-threshold-pctint50A bulk-tier workspace is shed once the credential's remaining budget fraction drops to/below this.
provider-rate-limit.tier.standard-threshold-pctint20Shed threshold for standard-tier workspaces.
provider-rate-limit.tier.premium-threshold-pctint10Shed threshold for premium-tier workspaces — so premium retains the last ~10% of a shared credential's budget after bulk/standard are already shed.

dvara.llm-gateway.cache.*

PropertyTypeDefaultDescription
cache.enabledbooleanfalseEnable response caching
cache.ttl-secondsint3600Cache entry time-to-live
cache.max-sizeint10000Max entries (in-memory only)

dvara.llm-gateway.cache.semantic.* (Enterprise — Semantic Cache)

Embedding-based cache that serves near-duplicate prompts (cosine similarity ≥ threshold) from a prior response instead of re-calling the provider. Per-workspace, oldest-first eviction. A semantic-cache HIT records the served tokens (cache_status=HIT) but books $0 cost — no upstream call happened.

PropertyTypeDefaultDescription
cache.semantic.enabledbooleantrueEnable semantic caching
cache.semantic.default-thresholddouble0.92Cosine similarity at/above which a prompt is a cache hit
cache.semantic.max-entriesint10000Per-workspace cap on cached entries (oldest-first eviction)
cache.semantic.ttl-secondsint3600Cache entry time-to-live
cache.semantic.embedding.providerstringsimple-hashEmbedding backend. simple-hash = dependency-free char-trigram embedder; onnx = in-process neural embeddings (all-MiniLM-L6-v2, 384-dim). If the ONNX model/deps are unavailable, logs a WARN and falls back to simple-hash. Switching provider clears the cache — the two embedding spaces are incompatible.
cache.semantic.embedding.model-pathstringOptional ONNX model directory holding model.onnx + tokenizer.json; blank = the bundled model
cache.semantic.indexstringlinearlinear = brute-force cosine over the per-workspace store; hnsw = in-process approximate-nearest-neighbour index for sub-linear lookup over the same source of truth
cache.semantic.hnsw.mint16Graph fan-out (only used when index=hnsw)
cache.semantic.hnsw.ef-constructionint200Build-time search width (only used when index=hnsw)
cache.semantic.hnsw.ef-searchint50Query-time search width — higher = better recall, slower query (only used when index=hnsw)
cache.semantic.hnsw.rebuild-thresholddouble0.3Rebuild the index once it exceeds live capacity by this fraction, reclaiming evicted entries (only used when index=hnsw)

dvara.llm-gateway.pii.* (Enterprise — PII Detection)

PropertyTypeDefaultDescription
pii.token-modestringvaultWhat a PII token is. vault mints an opaque token and stores the mapping; self-contained mints a token carrying its own ciphertext and stores nothing, so any pod holding the workspace key resolves it without a lookup. Erasure is identical either way — destroying the workspace key makes every token under it unreadable. Self-contained tokens grow from ~30 to ~115 characters and cross the provider boundary, and nothing is stored, so the Console cannot count or list them. An unrecognised value falls back to vault.
pii.dek-forensic-retention-daysint30How long a retired key is kept after rotation so rows it encrypted stay readable. Clamped down to pii.token-retention-days — a retired key outliving the tokens it can read is a longer PII-recoverability window, not a longer forensic one.
pii.key-sweep-cronstring0 45 3 * * *Removes token rows whose key has been destroyed, emitting PII_ERASURE_SWEPT. There is deliberately no retention knob: the delay is scheduling latency, not policy.
pii.key-retire-cronstring0 50 3 * * *Destroys retired keys past the clamped forensic window.
pii.enabledbooleantrueEnable PII detection and enforcement
pii.providerstringregexDetection provider: regex or presidio. The endpoint is the real toggle: setting a non-blank pii.presidio.endpoint activates the layered regex + Presidio-NER detector (with overlap deduplication) regardless of this flag; provider=presidio is kept as an explicit alias, and provider=presidio without an endpoint logs a WARN and falls back to regex-only. Startup logs the active posture.
pii.default-actionstringLOGDefault action: LOG, BLOCK, REDACT
pii.scan-responsesbooleantrueScan LLM responses for PII output leaks
pii.auto-detokenize-responsebooleanfalseOpt-in "transparent round-trip". When true, {{PII_TYPE_hex}} tokens in a non-streaming /v1/chat/completions response are restored to their original values before returning to the client, emitting a PII_RESPONSE_DETOKENIZED audit event. Applied only after the response is written to cache, so the cache never holds raw PII. Streaming is unaffected. Default-off because it re-introduces raw PII into responses. Per-workspace override via pii.auto-detokenize-response metadata.
pii.embedded.enabledbooleanfalsePromote the in-process embedded scanner into the PII pipeline so its hits are tokenized/redacted (and participate in the detokenize round-trip), compositted and span-deduplicated with regex[+Presidio]. Only redactable structured/name filter types map; content-signal types stay flag-only. Enabling this together with guardrail.embedded.enabled double-detects the same entity (redact + flag) and logs a startup WARN — pick one pipeline.
pii.embedded.filtersCSVComma-separated override of the enabled embedded filter types. Blank = the detector's complementary structured-PII default set (the types the regex detector does not already cover). Global only — per-workspace embedded filters remain a guardrail-pipeline capability.
pii.strip-before-cachebooleantrueAlways redact PII before writing to cache
pii.token-encryption-passwordstringAES-256-GCM password for PII token encryption
pii.max-tokens-per-workspaceint50000Maximum PII tokens stored per workspace
pii.token-retention-daysint30Days to retain PII tokens before expiry
pii.presidio.endpointstringPresidio analyzer endpoint URL
pii.presidio.languagestringenPresidio analysis language
pii.presidio.score-thresholddouble0.5Minimum Presidio confidence score
pii.presidio.timeout-secondsint5Presidio HTTP timeout
pii.presidio.cache-max-sizeint1000LRU cache max entries (0 = disabled)
pii.presidio.cache-ttl-secondsint300Cache entry TTL
pii.scan-streaming-responsesbooleantrueScan SSE streaming responses for PII. Buffers a rolling window across chunk boundaries so spans split across two SSE events are still caught.
pii.streaming-scan-window-sizeint256Characters buffered before a PII scan triggers during streaming.
pii.streaming-overlap-marginint64Characters retained between scan windows so PII spanning chunk boundaries is not missed.

Per-workspace PII configuration is set via workspace metadata keys:

Metadata KeyValuesDescription
pii.enabledtrue / falseOverride PII detection for this workspace
pii.actionBLOCK / REDACT / LOGOverride default PII action for this workspace
pii.scan-responsestrue / falseOverride response scanning for this workspace
pii.scan-streaming-responsestrue / falseOverride streaming-response scanning for this workspace
pii.custom-patternsJSON stringCustom regex patterns ({"label": "regex", ...})
cost.downgrade-threshold-pctint (0–100)Budget utilization % at which model downgrade triggers (default: 80)
cost.downgrade-rulesstringComma-separated model downgrade rules (format: "from:to,from:to", e.g. "gpt-4o:gpt-4o-mini,claude-3-opus:claude-3-sonnet")
cost.anomaly-threshold-pctintPer-workspace anomaly detection threshold (default: 200 = 2x baseline)

See PII Detection for full configuration details.

dvara.llm-gateway.guardrail.* (Enterprise — Guardrails & Safety)

PropertyTypeDefaultDescription
guardrail.enabledbooleantrueEnable guardrail detection and enforcement
guardrail.default-actionstringLOGDefault action: LOG, BLOCK, FLAG
guardrail.scan-responsesbooleantrueScan LLM responses for violations
guardrail.risk-score-thresholddouble0.7Detections below this score are ignored
guardrail.max-input-tokensint32000Max estimated input tokens (OWASP LLM10)
guardrail.max-messages-per-requestint100Max messages per request (OWASP LLM10)
guardrail.max-message-lengthint50000Max character length per message (OWASP LLM10)
guardrail.default-max-response-tokensint4096Applied when client doesn't specify max_tokens
guardrail.scan-streaming-responsesbooleantrueEnable guardrail scanning on streaming responses
guardrail.streaming-scan-window-sizeint256Chars buffered before scan trigger during streaming
guardrail.streaming-overlap-marginint64Chars retained between windows for boundary detection
guardrail.ml-classifier.enabledbooleanfalseEnable ML classifier for injection detection
guardrail.ml-classifier.providerstringgenericProvider: generic, lakera, shield-gemini, bedrock-guardrails, aporia, onnx-injection. onnx-injection is the only in-process provider (a DeBERTa prompt-injection classifier run locally — no HTTP, no SaaS, no egress); a missing model reports unavailable and ML classification is skipped.
guardrail.ml-classifier.model-pathstringonnx-injection only — model directory holding model.onnx + tokenizer.json. Blank = the bundled model. Air-gapped operators point this at a local dir.
guardrail.ml-classifier.endpointstringURL. Auto-defaulted for lakera/shield-gemini; ignored for bedrock-guardrails (derived from region + guardrail-id); required for aporia (per-project Guardrails URL, no universal default); unused for generic and onnx-injection.
guardrail.ml-classifier.api-keystringAPI key for the ML provider (lakera/shield-gemini/aporia)
guardrail.ml-classifier.project-idstringGoogle Cloud project ID (shield-gemini only)
guardrail.ml-classifier.locationstringus-central1Google Cloud region (shield-gemini only)
guardrail.ml-classifier.regionstringus-east-1AWS region for the Bedrock ApplyGuardrail endpoint (bedrock-guardrails only)
guardrail.ml-classifier.aws-access-keystringAWS access key for SigV4 signing (bedrock-guardrails only)
guardrail.ml-classifier.aws-secret-keystringAWS secret key for SigV4 signing (bedrock-guardrails only)
guardrail.ml-classifier.guardrail-idstringBedrock guardrailIdentifier to apply (bedrock-guardrails only)
guardrail.ml-classifier.guardrail-versionstringDRAFTBedrock guardrailVersion to apply (bedrock-guardrails only)
guardrail.ml-classifier.confidence-thresholddouble0.8Detections below this are ignored
guardrail.ml-classifier.timeout-secondsint5HTTP call timeout
guardrail.ml-classifier.cache-max-sizeint1000LRU cache max entries. 0 = disabled
guardrail.ml-classifier.cache-ttl-secondsint300Cache entry TTL
guardrail.plugins.enabledbooleanfalseEnable external guardrail plugin system
guardrail.plugins.definitions[].namestringUnique plugin identifier
guardrail.plugins.definitions[].urlstringHTTP endpoint URL
guardrail.plugins.definitions[].secretstringHMAC-SHA256 signing secret
guardrail.plugins.definitions[].timeout-msint5000HTTP call timeout
guardrail.plugins.definitions[].fail-modestringOPENOPEN or CLOSED
guardrail.grounding.enabledbooleanfalseEnable hallucination/grounding detection
guardrail.grounding.similarity-thresholddouble0.7Cosine similarity threshold for claims
guardrail.grounding.actionstringLOGAction: LOG, FLAG, BLOCK
guardrail.semantic-guard.enabledbooleanfalseEnable the embedding-based semantic prompt guard — flags a prompt when its embedding is cosine-similar to an operator-defined "deny-intent" exemplar set, catching keyword-free paraphrases the regex/content filters miss. A match is a TOPIC_RESTRICTION detection through the unchanged BLOCK/FLAG/LOG pipeline. Request-side only; requires a real embedding backend (otherwise no-ops). Exemplar sets are per-workspace via guardrail.semantic.deny-intents metadata.
guardrail.semantic-guard.similarity-thresholddouble0.8Cosine at/above which a prompt matches a deny-intent set
guardrail.semantic-guard.cache-max-sizeint1000LRU embedding cache max entries (prompt + exemplar embeddings; 0 = disabled)
guardrail.semantic-guard.cache-ttl-secondsint300Embedding cache entry TTL

Per-workspace guardrail configuration is set via workspace metadata keys:

Metadata KeyValuesDescription
guardrail.enabledtrue / falseOverride guardrail detection for this workspace
guardrail.actionBLOCK / FLAG / LOGOverride default action for this workspace
guardrail.risk-score-thresholddouble (0.0–1.0)Override risk score threshold
guardrail.semantic.enabledtrue / falseNarrows or enables the semantic prompt guard for this workspace
guardrail.semantic.similarity-thresholddouble (0.0–1.0)Override the semantic-guard cosine match threshold
guardrail.semantic.deny-intentsJSON mapPer-workspace deny-intent exemplars: {"intent-name": ["phrase", ...]} (a list or a newline/comma-separated string). There is no global exemplar config.
guardrail.max-input-tokensintOverride max estimated input tokens
guardrail.max-messages-per-requestintOverride max messages per request
guardrail.max-message-lengthintOverride max message character length
guardrail.default-max-response-tokensintOverride default response token cap
guardrail.content.profanity.actionBLOCK / FLAG / LOGPer-category action override
guardrail.content.violence.actionBLOCK / FLAG / LOGPer-category action override
guardrail.content.sexual.actionBLOCK / FLAG / LOGPer-category action override
guardrail.content.competitor.keywordscomma-separatedCompetitor brand keywords
guardrail.content.topic-restrictionscomma-separatedRestricted topic keywords
guardrail.content.custom-denylistJSON stringCustom deny-list patterns
guardrail.injection.custom-patternsJSON stringCustom injection patterns
guardrail.mcp-injection.enabledtrue / falseEnable MCP injection scanning
guardrail.mcp-injection.actionBLOCK / FLAG / SANITIZEMCP injection action
guardrail.context.warning-threshold-pctint (0–100)Context window warning threshold (default: 70)
guardrail.context.hard-threshold-pctint (0–100)Context window hard threshold (default: 90)
guardrail.context.pruning-strategyNONE / TRUNCATE_OLDEST / TRUNCATE_MIDDLEContext pruning strategy
guardrail.scan-streaming-responsestrue / falseOverride streaming-response scanning for this workspace
guardrail.max-input-tokensintPer-workspace override (OWASP LLM10)
guardrail.max-messages-per-requestintPer-workspace override (OWASP LLM10)
guardrail.max-message-lengthintPer-workspace override (OWASP LLM10)
guardrail.default-max-response-tokensintPer-workspace override applied when client omits max_tokens
guardrail.pluginsJSON mapPer-plugin config: {"pluginName": {"enabled": false}}

See Guardrails & Safety for full configuration details.

dvara.llm-gateway.guardrail.embedded.* (Enterprise — Embedded PII Scanner)

In-process PII detection via the embedded scanner — runs entirely in-process, no external service required, no network calls, no vendor API keys, sub-millisecond per-call latency. Pairs with the always-on regex PII detector; for production credit-card detection, rely on the regex path's Luhn validation since the embedded scanner is best-effort.

PropertyTypeDefaultDescription
guardrail.embedded.enabledbooleanfalseEnable the embedded PII scanner. The default policy enables 22 deterministic filter types: SSN, passport, drivers license, credit card, IBAN, bank routing, bitcoin, currency, phone, phone extension, email, IP, MAC, URL, street address, ZIP, state abbreviation, date, age, VIN, tracking, physician name. 8 additional dictionary-based filters (first name, surname, city, etc.) are opt-in per workspace via embedded.enabled-filters.

Per-workspace restriction via Workspace.metadata["embedded.enabled-filters"] (comma-separated FilterType names) — editable from the DVARA Console workspace form (Built-in PII Filters tab).

dvara.llm-gateway.guardrail.grounding.* (Enterprise — Hallucination / Grounding Detection)

Embedding-based grounding detection. When source documents are provided on the request via request.metadata["grounding.sources"] (List<String>), the detector splits the response into sentence claims, embeds each claim and source, and flags claims with max cosine similarity below the threshold.

PropertyTypeDefaultDescription
guardrail.grounding.enabledbooleanfalseEnable embedding-based grounding detection
guardrail.grounding.similarity-thresholddouble0.7Cosine similarity threshold — claims below this are flagged ungrounded
guardrail.grounding.actionstringLOGLOG, FLAG, or BLOCK when a hallucination is detected
guardrail.grounding.max-sourcesint50Maximum source documents per request; excess silently dropped
guardrail.grounding.max-source-lengthint10000Maximum chars per source document; oversized sources silently dropped

Per-workspace overrides via Workspace.metadata: grounding.enabled, grounding.action, grounding.max-sources, grounding.max-source-length. Streaming requests run the same detector at stream-end against the accumulated response text.

dvara.llm-gateway.finops.* (Enterprise — Cost Calculation)

PropertyTypeDefaultDescription
finops.enabledbooleantrueEnable enterprise cost calculation
finops.pricing-cache-ttl-secondsint60TTL for pricing lookup cache (0 = disabled)
finops.spend-cache-ttl-secondsint30TTL for budget spend cache (avoids per-request DB queries)
finops.soft-alert-cooldown-minutesint60Per-budget-cap dedup window for soft-limit alerts. The first soft breach on a cap writes BUDGET_CAP_SOFT (which fans out to webhook subscribers and the audit log); subsequent soft breaches on the same cap within this window are skipped — no audit event, no webhook delivery. The Prometheus counter gateway_budget_soft_alert_total is incremented on every breach regardless.

Operational caveat: a workspace whose spend straddles the soft threshold over many requests will not generate a per-call alert — operators wiring on-call paging that expects per-call notifications should lower this to seconds (e.g. 1) or zero. The cooldown is per budget id, not per workspace, so a workspace with three caps (global / workspace / API-key) can still see up to three soft alerts within the window. Model downgrade is not subject to the cooldown — it runs on every request whose soft limit is breached, so downgrade continues to apply silently between alert fires.
finops.downgrade-cache-ttl-secondsint5TTL for per-workspace downgrade policy cache (0 = disabled)
finops.chargeback-schedulestring""Cron expression for monthly chargeback report auto-generation (empty = disabled)
finops.anomaly-threshold-pctint200Default anomaly threshold: current daily rate vs 30d baseline (200 = 2x)
finops.budget-warning-threshold-pctint75Default utilization % threshold at which WARN_AGENT policy rules fire. When a workspace's spend on a budget cap crosses this fraction of the cap's limit, any policy with a WARN_AGENT action sees the warning state and can inject a soft-cap notice into the response without blocking the call. Per-budget override via the budget cap's soft_limit_pct field; per-workspace override via the policy YAML's threshold expression.
finops.per-call-max-cost-usddouble0Per-call max-cost ceiling in USD (0 = disabled). A single request whose estimated cost (max_tokens × the model's output price) exceeds this is rejected pre-dispatch with 402 PER_CALL_COST_EXCEEDED — a hard stop on one call, complementing (not replacing) the accumulated-spend budget caps. Applies only when the request sets max_tokens and the model is priced; otherwise the call can't be priced and passes. Per-workspace override via cost.per-call-max-usd metadata.
finops.spend-settle-margin-minutesint180Minutes after a UTC day ends before its spend is treated as settled and frozen into an immutable daily partial. Until then the day stays in the live hot tail, so an ordinary late write (clock skew, commit lag, a long stream persisting its cost after midnight) is still counted. Larger margin = a slightly larger, still ~1-day-bounded, live hot-tail scan.
finops.spend-retention-daysint90How long to keep the immutable daily spend partials. These are a purely-derived read cache (the durable spend history is the cost records), so a deleted partial an active period still needs simply re-materializes on the next read — this is a cache-size knob, not a correctness one. Clamped up to a fixed correctness floor so it can never drop below what an open budget period reads.
finops.spend-counter.enabledbooleanfalseEnforce budgets against an approximate fleet-wide counter in the embedded cache instead of the ledger. Off by default on purpose: while a pod still has a datasource the ledger read is exact, and swapping an exact number for an approximate one before the exact one becomes unavailable only loses accuracy. cost_records remains the billing truth in every posture. Every path fails open.
finops.spend-counter.checkpoint-interval-mslong10000How often the cluster-view counter is written to the pod's local store and read back on cold start. The interval is the entire exposure window: after a cluster-wide cache loss a pod re-seeds from its last checkpoint and under-counts by whatever the fleet spent since, so overspend is bounded by checkpoint age x burn rate rather than by outage duration.
dvara.llm-gateway.quota-counter.checkpoint-interval-mslong10000How often the fleet-wide quota counter is snapshotted to the pod's local store and read back on cold start. Without a checkpoint a total cache-cluster loss seeds zero and the month's usage is forgotten until rollover — fail-open, which is the correct direction, but unbounded in time. With one, the exposure becomes checkpoint age x call rate. The interval is therefore the entire exposure window and the only knob. Fixed delay, so a snapshot slower than the interval on a large cluster cannot queue behind itself; an empty snapshot is skipped rather than written, since a freshly-restarted pod would otherwise overwrite the very checkpoint about to seed it.
finops.spend-retention-cronstring0 15 3 * * *Cron for the daily sweep (03:15 UTC) that deletes spend partials older than the (clamped) spend-retention-days. Set to - to disable.

Cost calculation uses pricing entries managed from the Console's Pricing page. Pricing supports glob patterns (e.g. gpt-4o* matches gpt-4o, gpt-4o-mini). Costs are calculated per-request with high-precision decimal arithmetic and persisted as cost records, viewable on the Console's Costs page.

Budget caps enforce soft and hard spending limits at three scopes — global, workspace, and API-key. The data plane evaluates all applicable caps for each request in most-specific-first order (API-key → workspace → global) and short-circuits on the first hard or soft breach; for non-breach cases the tightest-remaining-percent cap is surfaced and rendered into response headers. Configure budgets on the Console's Budgets page. Budget periods (DAILY, WEEKLY, MONTHLY) reset automatically at UTC boundaries.

Hard breach → the budget enforcer rejects the request with HTTP 402 Payment Required and writes a BUDGET_CAP_HARD audit event. No retries, no downgrade — the request never reaches the provider.

Soft breach → the request is allowed through with the breach state stamped onto the request context. Two side-effects fire from the soft state:

  1. A BUDGET_CAP_SOFT audit event is written (subject to the soft-alert-cooldown-minutes dedup window above). Webhook subscribers with the BUDGET_CAP_SOFT event type get the payload via the audit→webhook bridge.
  2. The model-downgrade step reads the soft-breach signal and, if Workspace.metadata["cost.downgrade-rules"] defines a rule for the requested model, swaps the model in-flight (e.g. gpt-4ogpt-4o-mini) before dispatch. Downgrade fires on every soft-breach request — it is not subject to the soft-alert cooldown, so spend continues to bend toward the cheaper variant even when alerts are paused.

The downgrade threshold (default 80% of the cap) and downgrade rules are per-workspace via cost.downgrade-threshold-pct and cost.downgrade-rules metadata keys.

Chargeback reports aggregate costs by workspace, API key, model, provider, and time period. Reports are exportable as CSV and PDF. Monthly auto-generation is available via chargeback-schedule cron. Cost forecasting uses trailing 7-day and 30-day spend trends with linear projection. Cost anomaly detection compares current daily spend rate against the 30-day baseline; alerts fire when the deviation exceeds the configured threshold (per-workspace override via cost.anomaly-threshold-pct metadata key).

Enterprise License

A valid DVARA license key is required for all apps to start. Set DVARA_LICENSE_KEY as an environment variable.

PropertyEnvironment VariableDefaultDescription
dvara.license.keyDVARA_LICENSE_KEYSigned DVARA license key (trial or production)
dvara.license-monitor.interval-msDVARA_LICENSE_MONITOR_INTERVAL_MS3600000 (1 hour)Interval for runtime license re-validation. Each sweep re-checks the envelope's signature and expiry, updates the runtime license status, and writes a LICENSE_* audit event on status transitions.

Runtime license lifecycle:

  • Within 30 days of expiryEXPIRING_SOON: X-License-Warning response header added, LICENSE_EXPIRY_WARNING audit event
  • Expired (within 14-day grace)GRACE_PERIOD: X-License-Warning header, LICENSE_EXPIRED audit event, service fully operational
  • Expired (beyond the 14-day grace)DEGRADED: data-plane /v1/* returns 402 Payment Required, admin/internal endpoints remain accessible, LICENSE_DEGRADED audit event

dvara.flightdeck.license-alert.* (License-Expiry Email Alerts)

Operator-friendly email notifications on license status transitions. The alerting is driven by the runtime license monitor — no schedule config needed. Off by default so existing deploys don't start sending alerts on a license-key rotation just because the rc23+ images landed.

PropertyDefaultDescription
dvara.flightdeck.license-alert.enabledfalseMaster switch. Opt-in.
dvara.flightdeck.license-alert.recipients[]Email addresses to notify on LICENSE_EXPIRY_WARNING / LICENSE_EXPIRED / LICENSE_DEGRADED transitions. Comma-separated in env vars, list in YAML. At least one address is required when enabled=true — misconfigured state logs a WARN at boot but doesn't refuse to start (alerting is non-critical to data-plane uptime).
dvara.flightdeck.license-alert.subject-prefix[DVARA license alert]Override for the email subject line prefix. Useful when a single ops mailbox receives alerts from multiple DVARA installs (e.g. [DVARA license alert — prod], [DVARA license alert — staging]).

Enterprise Latency-Aware Routing Configuration

Tunes the latency-aware routing strategy (see Routing → Latency-aware) — EWMA per-(provider, model) latency tracking with a configurable freshness window and decay penalty for stale samples.

PropertyEnvironment VariableDefaultDescription
dvara.llm-gateway.routing.latency.alpha0.2EWMA smoothing factor (0.0–1.0). Higher values give more weight to recent samples
dvara.llm-gateway.routing.latency.decay-threshold-ms60000Staleness threshold in milliseconds. Entries older than this receive a decay penalty
dvara.llm-gateway.routing.latency.decay-multiplier0.5Stale EWMA is divided by this value (lower = harsher penalty)
dvara.llm-gateway.routing.latency.min-samples5Minimum latency samples before EWMA is used for routing decisions
dvara.llm-gateway.routing.latency.snapshot-interval100Persist latency snapshot to repository every N samples

Enterprise Priority Routing Configuration

PropertyEnvironment VariableDefaultDescription
dvara.llm-gateway.routing.priority.enabledfalseEnable concurrency-based priority admission control
dvara.llm-gateway.routing.priority.max-concurrent-requests1000Maximum concurrent requests across all tiers
dvara.llm-gateway.routing.priority.tiers.premium.throttle-threshold-pct100Load % at which premium requests are throttled
dvara.llm-gateway.routing.priority.tiers.standard.throttle-threshold-pct80Load % at which standard requests are throttled
dvara.llm-gateway.routing.priority.tiers.bulk.throttle-threshold-pct50Load % at which bulk requests are throttled
dvara.llm-gateway.routing.priority.resolver-cache-ttl-seconds5TTL for workspace → priority tier cache

Enterprise Webhook Configuration

PropertyEnvironment VariableDefaultDescription
dvara.llm-gateway.webhooks.enabledDVARA_LLM_GATEWAY_WEBHOOKS_ENABLEDtrueEnable webhook delivery of governance events
dvara.llm-gateway.webhooks.max-retriesDVARA_LLM_GATEWAY_WEBHOOKS_MAX_RETRIES3Maximum retry attempts for failed deliveries
dvara.llm-gateway.webhooks.base-retry-delay-msDVARA_LLM_GATEWAY_WEBHOOKS_BASE_RETRY_DELAY_MS1000Base delay in milliseconds for exponential backoff
dvara.llm-gateway.webhooks.retry-multiplierDVARA_LLM_GATEWAY_WEBHOOKS_RETRY_MULTIPLIER4Multiplier for exponential backoff (delay = base x multiplier^attempt)
dvara.llm-gateway.webhooks.delivery-timeout-msDVARA_LLM_GATEWAY_WEBHOOKS_DELIVERY_TIMEOUT_MS5000HTTP connect+read timeout for webhook delivery
dvara.llm-gateway.webhooks.approval-base-urlDVARA_LLM_GATEWAY_WEBHOOKS_APPROVAL_BASE_URL"" (empty)Base URL for approve/deny links in MCP approval webhooks
dvara.llm-gateway.webhooks.approval-ttl-minutesDVARA_LLM_GATEWAY_WEBHOOKS_APPROVAL_TTL_MINUTES15TTL in minutes for approval token validity
dvara.llm-gateway.webhooks.max-delivery-log-entriesDVARA_LLM_GATEWAY_WEBHOOKS_MAX_DELIVERY_LOG_ENTRIES50000Capacity of the delivery log (rolling). Older entries are dropped when capacity is reached.

dvara.persistence.* (Enterprise — Database Persistence)

PropertyEnvironment VariableDefaultDescription
spring.datasource.urlSPRING_DATASOURCE_URLJDBC connection URL (e.g. jdbc:postgresql://localhost:5432/dvara). When set with a valid license, PostgreSQL persistence activates automatically.
dvara.persistence.batch-sizeDVARA_PERSISTENCE_BATCH_SIZE100Batch size for bulk write operations
dvara.config.poll-interval-ms3000Interval at which every pod reads a config version counter table to detect config changes (routes, policies, credentials, guardrail plugins, MCP servers). This is the propagation-latency floor. Version-table polling is gap-immune and pooler-agnostic (no session-pinned LISTEN connection); tune higher to reduce DB load on large fleets, lower for faster propagation.
dvara.config.poll-initial-delay-ms3000Delay before the first poll after startup — startup config is already loaded fresh, so the first poll only establishes the version baseline.
spring.datasource.usernameSPRING_DATASOURCE_USERNAMEDatabase username
spring.datasource.passwordSPRING_DATASOURCE_PASSWORDDatabase password
spring.flyway.enabledSPRING_FLYWAY_ENABLEDtrueAuto-run database migrations on startup
spring.datasource.hikari.maximum-pool-sizeDVARA_DB_POOL_SIZE2JDBC connection pool size. Data plane (gateway-server, mcp-proxy-server) defaults to 2 (one connection for config reads via the version-table poller, one for audit writes) — the data plane is read-mostly and persists asynchronously, so a small pool is correct. Flightdeck (admin server, flightdeck) should be sized to its real concurrency — set DVARA_DB_POOL_SIZE=10 or higher in production for the UI's read-heavy dashboard pages. Do not raise the data-plane pool unless you have evidence of contention; raising it just to "be safe" wastes connections that are scarce behind PgBouncer / DigitalOcean Managed Connection Pool.
# Enterprise persistence (PostgreSQL-backed state)
dvara:
persistence:
batch-size: 100 # default: 100

spring:
datasource:
url: jdbc:postgresql://localhost:5432/dvara
username: dvara
password: ${DB_PASSWORD}
flyway:
enabled: true # auto-runs migrations on startup

dvara.data-plane.* / dvara.llm-gateway.local-store.* (Data-Plane Decoupling)

How a gateway pod runs without a database of its own — configuration arrives as a signed bundle and writes go to a local spool that ships to Flightdeck. The narrative, including the order to enable things in and the two SSL properties that are easy to confuse, is on Data-plane decoupling. This is the property reference.

A pod that has no datasource needs all three of serve-from-bundle, local-store.path and control-plane.base-url. Anything less is refused at startup, with the missing property named — there is deliberately no single "run without a database" switch, because each missing piece fails silently in production and a dropped audit trail is invisible until somebody asks for it.

PropertyDefaultDescription
dvara.data-plane.control-plane.base-urlFlightdeck's URL. This property alone gates the whole bundle client — leave it unset and none of this wires. Set without serve-from-bundle it puts the pod in shadow mode: it fetches, verifies, snapshots and compares bundles while still serving from PostgreSQL.
dvara.data-plane.serve-from-bundlefalseServe configuration from the held bundle instead of PostgreSQL. Config writes on that pod then fail rather than appearing to succeed against a read-only bundle.
dvara.data-plane.control-plane.public-keyBase64 X.509 Ed25519 key the pod pins bundles against. Required whenever base-url is set: a pod that cannot verify must not fetch.
dvara.data-plane.control-plane.additional-public-keys(empty)Extra keys accepted alongside public-key. Empty in steady state; it exists so a signing-key rotation can be a rolling change with no moment where a valid bundle is rejected.
dvara.data-plane.control-plane.ssl-bundleOnly checks the client certificate's CN against the requested scope. It does not put a certificate on the HTTP client — that is Spring's own spring.http.clients.ssl.bundle (note the plural). Setting this one alone gives a pod that passes its startup check and then fails every fetch with PKIX path building failed.
dvara.data-plane.control-plane.snapshot-pathWhere the pod persists the last bundle it verified, reloaded at boot before the first fetch. Blank disables it. Without it a pod that restarts while the control plane is down has nothing to serve. Written atomically and re-verified on load, so a tampered snapshot is rejected.
dvara.data-plane.control-plane.poll-interval-ms5000 (bundle) · 10000 (deny-list)How often the pod re-fetches. The same property is read by both consumers with different defaults; for the deny-list it is the release-latency floor for a revocation.
dvara.data-plane.control-plane.deny-list-urlfalls back to base-urlThe break-glass revocation channel. The default is the convenient posture, not the strongest one — pointing it at the same host as the bundles is a shared failure mode, which is the thing a second channel exists to avoid.
dvara.data-plane.control-plane.ingest-urlfalls back to base-urlWhere the drained spool is shipped.
dvara.data-plane.control-plane.shadow-compare-interval-ms30000How often shadow mode diffs the bundle against PostgreSQL.
PropertyDefaultDescription
dvara.llm-gateway.local-store.pathThe switch: blank disables the whole spool, and with it every row below. An absolute path to the SQLite file. The volume behind it must survive a restart, or the spool loses exactly what it exists to protect.
dvara.llm-gateway.local-store.max-sheddable-records500000Bound across the sheddable classes. Audit is exempt and cannot be shed — usage, cost and observability rows are reconstructable or expendable; an audit event is neither.
dvara.llm-gateway.local-store.max-per-workspace-per-class50000Per-workspace ceiling inside the global bound, so one workspace spooling hard cannot starve the rest of a shared pod.
dvara.llm-gateway.local-store.pii-high-water-per-workspace40000Depth past which the pod stops minting PII tokens instead of discarding them, applying the workspace's degraded action. Deliberately below the per-class ceiling so this limit is reached first and is the one that speaks.
dvara.llm-gateway.local-store.drain-interval-ms30000Fixed delay between drain passes — a delay rather than a rate, so a slow control plane cannot queue drains behind each other.
dvara.llm-gateway.local-store.drain-batch-size500Envelopes attempted per pass.
dvara.config.bundle.signing-keygeneratedBase64 PKCS#8 Ed25519 private key. Flightdeck signs bundles with it; pods only verify. Optional — left unset, Flightdeck generates a keypair once and persists it, so it survives a restart and is shared across replicas. Set it only to hold the key material yourself.
dvara.config.bundle.signing-public-keyBase64 X.509 Ed25519 public key. Required whenever the private key is set, because Java cannot derive one from the other. A startup self-check signs and verifies a fixed vector and refuses to boot on a mismatched pair, rather than letting every pod in the fleet reject every bundle while Flightdeck reports signing success.
dvara.config.bundle.republish-interval-ms5000How often Flightdeck checks whether any config has changed and republishes. It watches change counters rather than republishing on a timer, because materializing a bundle is tens of megabytes at scale. Publishing is idempotent — the version only moves when the content hash does.
dvara.internal.mtls.enabledfalseOpens /internal/** to client-certificate-authenticated callers. When false the surface is deny-all. Enabling this is not sufficient on its own: the connector must also request certificates via server.ssl.client-auth against a truststore of the CA signing pod certificates, or every request is rejected. That is deliberate — the alternative, where a missing truststore quietly degrades to "allow", is how a channel carrying signed config and per-workspace keys ends up open with nothing in the logs.
dvara.internal.mtls.log-rejectionstrueLogs every client certificate the TLS handshake refuses, at WARN, with subject, issuer, validity window, reason and peer address. Without it a refused certificate leaves no trace at all — note the asymmetry, that a request with no certificate reaches the application and gets a clean 403 in the access log, while one with a bad certificate is refused below the servlet container and vanishes. Follows the connector, not the property above.
dvara.data-plane.control-plane.poll-initial-delay-ms5000Delay before a pod's first deny-list poll after startup.
dvara.internal.deny-list.republish-initial-delay-ms5000Delay before Flightdeck's first deny-list republish after startup.
dvara.internal.deny-list.republish-interval-ms5000How often Flightdeck rebuilds and re-signs the revocation deny-list. A timer, not a change trigger — the opposite choice from the config bundle, and deliberate: the list is bytes, so rebuilding unconditionally costs nothing, while a revocation that failed to fire a trigger is one that silently never reaches the fleet.
dvara.internal.deny-list.session-retention-margin-minutes15Added to the agentic session TTL to decide when a killed-session entry retires. Caveat worth knowing rather than finding: session ids are client-supplied, so an honest agent could resume under a previously-killed id after the window. Killing contains a session; containing an actor is an API-key or workspace revocation, which are separate entries on the same list.
dvara.llm-gateway.local-store.shutdown-drain-seconds20How long a stopping pod flushes in-process. A fraction of the orchestrator's grace period, not all of it: overrunning it turns an orderly stop into a kill mid-flush. 0 disables it, which makes every routine rolling update depend on the volume instead of a clean handover.

Distributed Caching and Rate Limiting

DVARA ships with an embedded distributed cache for API key lookups and rate-limit counters. No external cache infrastructure is required — the cache runs in-process and auto-clusters across pods.

  • Local / Docker Compose: pods discover each other via multicast (works out of the box).
  • Kubernetes: pods discover each other via DNS lookup against a headless Service (not a regular ClusterIP Service — the cluster discovery mechanism needs EndpointSlice records returned by the Service). Create the headless Service with clusterIP: None and a label selector matching your data-plane pods, then set the CACHE_SERVICE_NAME env var on every pod to the Service name. The Service must live in the same namespace as the pods; cross-namespace discovery requires CACHE_SERVICE_NAMESPACE as well. Without CACHE_SERVICE_NAME, pods boot in single-node mode and rate-limit / cache state is per-pod, not fleet-wide — silently breaking horizontal correctness. The gateway logs a clear WARN at startup when this happens.

The distributed cache currently wraps the API key repository with a 30-second TTL for sub-millisecond auth lookups across the fleet. Write-through eviction ensures revoked keys are never served from cache.

Rate limiting uses the same in-process distributed maps for per-key request and token counts shared across all pods. See Rate Limiting for configuration.

MCP Plane Configuration

Configuration for the MCP plane, which runs inside the gateway and serves /mcp on port 8080. Requires a license.

PropertyEnvironment VariableDefaultDescription
dvara.mcp-gateway.timeout-default30Default timeout in seconds for upstream MCP server requests
dvara.mcp-gateway.timeout-max120Maximum allowed timeout in seconds (caps any per-server override)
dvara.mcp-gateway.registry-cache-ttl-seconds30TTL for the MCP server registry cache

A2A Plane Configuration

Configuration for the A2A plane, which runs inside the gateway and serves /a2a on port 8080. Requires a license and a database. It shares the standard actuator/observability and license configuration; the A2A-specific settings govern Agent Card discovery, streaming hops, and the human-in-the-loop approval gate.

Discovery (GET /a2a/{agentId}/.well-known/agent-card.json):

PropertyDefaultDescription
a2a.discovery.card-ttl-seconds3600How long a cached Agent Card is served before it re-fetches from the peer. ?refresh=true forces an immediate re-fetch.
a2a.discovery.fetch-timeout-seconds10Connect/read timeout for the peer Agent Card fetch.

Streaming (POST /a2a/{agentId}/message:stream):

PropertyDefaultDescription
a2a.streaming.enabledtrueMaster switch for the streaming endpoint.
a2a.streaming.scan-window-size256Chars used with the overlap tail to detect PII spanning two SSE events.
a2a.streaming.overlap-margin64Chars retained between events to catch boundary-spanning PII.
a2a.streaming.connect-timeout-seconds10Connect timeout for the upstream message/stream POST.
a2a.streaming.read-timeout-seconds300Total timeout for the upstream SSE stream.

Approval gate (the human-in-the-loop gate on A2A hops; requires a datasource):

PropertyDefaultDescription
a2a.approval.enabledtrueMaster switch. When false, hops never require approval.
a2a.approval.timeout-seconds300How long a paused hop waits before the default action applies. Per-workspace override via Workspace.metadata["approval.timeout-seconds"].
a2a.approval.default-actiondenyAction on timeout — deny (safe) or approve. Per-workspace override via Workspace.metadata["approval.default-action"].
a2a.approval.max-pending-approvals1000Backpressure cap — past this, new hops are auto-denied rather than parked.
a2a.approval.poll-interval-ms1000How often a paused waiter polls the durable approval record; a decision written on any pod releases the hop within one interval. Also the release-latency floor.
a2a.approval.sweep-interval-ms60000Fixed-delay orphan-sweep interval (transitions PENDING past expires_at to TIMEOUT; only mops up hops whose waiter pod died).
a2a.approval.sweep-initial-delay-ms60000Delay before the first orphan sweep after startup.

Which hops require approval is per-workspace via Workspace.metadata: approval.required-skills (comma-separated glob patterns matched against the requested skill) and approval.required-agents (comma-separated exact target-agent ids) — reusing the same metadata namespace as the MCP approval.required-tools / approval.required-servers keys.

The per-workspace skills allow-list that filters advertised skills is not a property — it lives on each agent registry entry (skills_allow_list, editable on the Console's A2A → Agents page). Per-hop policy lives in the A2A policy store, managed through the Console's GitOps export/import (a2a_policies) — there is no Console page for them. See A2A Governance Plane.

dvara.mcp-gateway.pii.* (MCP Gateway PII Detection)

The MCP Gateway runs its own PII scanner independent of the LLM gateway's dvara.llm-gateway.pii.* settings — MCP tool inputs and outputs are not LLM completions and need their own enable/action knobs.

PropertyDefaultDescription
dvara.mcp-gateway.pii.enabledtrueEnable PII scanning on MCP tool arguments and responses.
dvara.mcp-gateway.pii.default-actionLOGDefault PiiAction on detection: LOG, BLOCK, or REDACT.
dvara.mcp-gateway.pii.scan-responsestrueScan MCP tool responses (in addition to request arguments) for PII output leaks.

Per-workspace overrides are managed identically to the LLM-gateway side via workspace metadata.

dvara.mcp-gateway.catalog.* (MCP Discovery Catalog)

Off by default. When enabled, a scheduled sync materializes a platform-global read model of MCP servers from the configured sources; the Portal browses it and one-click-registers an entry into the workspace's governed MCP servers. Consume-only — DVARA does not publish a registry. No catalog beans, sync, nav item, or admin/Portal surface register unless enabled=true.

PropertyDefaultDescription
dvara.mcp-gateway.catalog.enabledfalseMaster switch.
dvara.mcp-gateway.catalog.sync-cron0 0 6 * * *Cron for the scheduled refresh. The sync is fail-soft per source — one source erroring never wipes another's rows. Also triggerable from the Console (emits MCP_CATALOG_SYNCED).
dvara.mcp-gateway.catalog.curated.enabledtrueEnable the curated source.
dvara.mcp-gateway.catalog.curated.path""Path to a curated catalog JSON of vetted, register-ready servers; blank = the bundled empty default.
dvara.mcp-gateway.catalog.official.enabledfalseEnable the official MCP Registry source.
dvara.mcp-gateway.catalog.official.base-urlhttps://registry.modelcontextprotocol.ioOfficial MCP Registry base URL.
dvara.mcp-gateway.catalog.official.timeout-seconds10HTTP timeout for the official-registry crawl.
dvara.mcp-gateway.catalog.official.page-size100Page size for the official-registry crawl.
dvara.mcp-gateway.catalog.docker.enabledfalseEnable the Docker MCP catalog source.
dvara.mcp-gateway.catalog.docker.urlThe Docker MCP catalog YAML URL.

management.* (Observability)

PropertyTypeDefaultDescription
management.endpoints.web.exposure.includestringhealth,prometheus,gateway-status,infoActuator endpoints to expose. The probe paths (/actuator/health, /actuator/health/liveness, /actuator/health/readiness, /actuator/info) stay anonymous. /actuator/gateway-status requires DVARA_ACTUATOR_API_KEY; /actuator/prometheus requires DVARA_ACTUATOR_METRICS_API_KEY.
management.endpoints.web.exposure.excludestringenv,heapdump,threaddump,beans,mappings,configprops,loggers,scheduledtasks,caches,sessions,quartzDangerous endpoints — return 404 regardless of authentication. Do not remove from this list without a security review.
management.endpoint.health.show-detailsstringwhen-authorizedHealth detail visibility. Setting this to always is rejected at boot — the readiness probe stays DOWN and a rolling deploy halts on the first replica. Use when-authorized (default) or never.
management.prometheus.metrics.export.enabledbooleantrueEnable Prometheus scrape endpoint
management.tracing.genai-semconv.enabledbooleantrueEmit OpenTelemetry GenAI semantic-convention (gen_ai.*) attributes on provider spans, in addition to the legacy attributes. Set false to suppress the gen_ai.* attributes (e.g. for cardinality concerns) without losing the legacy attributes.

Logging

ConfigurationValueDescription
Default formatStructured JSONEvery log line is valid JSON
Plain-text modespring.profiles.active=log-plainHuman-readable output for local dev
Log config filelogback-spring.xmlLogging configuration, supports Spring profile overrides

dvara.flightdeck.security.* (Enterprise — OIDC/JWT Authentication)

PropertyDefaultDescription
dvara.flightdeck.security.enabledtrueEnable authentication for admin endpoints. When true with no OIDC issuer-uri or SAML metadata-url, built-in email/password auth activates. Set to false for local development only.
dvara.flightdeck.security.oidc.issuer-uriOIDC issuer URL for JWT validation (e.g. Keycloak, Auth0)
dvara.flightdeck.security.oidc.audience""Expected JWT audience claim (blank = skip audience validation)
dvara.flightdeck.security.oidc.role-claimrolesJWT claim containing roles (supports dot-notation, e.g. realm_access.roles)
dvara.flightdeck.security.oidc.name-claimnameJWT claim for user display name
dvara.flightdeck.security.oidc.workspace-claimworkspace_idJWT claim for workspace association
dvara.flightdeck.security.rbac.enabledtrueEnable URL-pattern RBAC enforcement (false = authenticated() only)
dvara.flightdeck.security.session.timeout-seconds3600Session timeout in seconds

dvara.vault.* (Enterprise — Secrets Management)

PropertyDefaultDescription
dvara.vault.backend"" (disabled)hashicorp, aws-secrets-manager, or azure-key-vault
dvara.vault.cache-ttl-seconds300Secret cache TTL
dvara.vault.hashicorp.addressHashiCorp Vault address
dvara.vault.hashicorp.tokenStatic Vault token
dvara.vault.hashicorp.auth-methodtokentoken or approle
dvara.vault.hashicorp.secret-pathsecret/data/dvaraKV v2 secret path
dvara.vault.aws.regionus-east-1AWS region
dvara.vault.aws.secret-namedvara/provider-credentialsAWS Secrets Manager secret name
dvara.vault.azure.vault-urlAzure Key Vault URL

dvara.llm-gateway.tls.* (Enterprise — mTLS Per Provider)

PropertyDefaultDescription
dvara.llm-gateway.tls.enforce-tls13trueEnforce TLS 1.3 on all outbound provider connections
dvara.llm-gateway.tls.providers.<name>.mtls-enabledfalseEnable mTLS client cert for this provider
dvara.llm-gateway.tls.providers.<name>.client-cert-pathPath to client certificate (PEM or PKCS12)
dvara.llm-gateway.tls.providers.<name>.client-key-pathPath to client private key (PEM)
dvara.llm-gateway.tls.providers.<name>.trust-store-pathPath to trust store

dvara.llm-gateway.ip-access.* (Enterprise — IP Access Control)

PropertyDefaultDescription
dvara.llm-gateway.ip-access.enabledfalseEnable IP allowlist/denylist
dvara.llm-gateway.ip-access.scopeallall or data-plane (only /v1/*)
dvara.llm-gateway.ip-access.global-allowlist[]CIDR ranges to allow (e.g. 10.0.0.0/8)
dvara.llm-gateway.ip-access.global-denylist[]CIDR ranges to deny

Per-workspace: ip-access.allowlist and ip-access.denylist in workspace metadata (comma-separated CIDRs).

dvara.audit.* (Enterprise — Audit Trail)

PropertyDefaultDescription
dvara.audit.hmac-secretgenerated per installHMAC-SHA256 key signing the audit chain. Unset is the recommended posture where a database is configured: the install mints a secret on first boot and persists it, so every process signing that chain shares one value and there is no unsigned-by-default state. The literal default-dev-secret-change-in-production shipped in application.yml is refused on a production-class profile — it is public, so anyone could forge the chain. Set this explicitly to hold the key material yourself, and always set it, the same value on both halves, when a data-plane pod ships audit to Flightdeck: the pod signs and Flightdeck verifies without re-signing, so a mismatch is a 400 tamper on every batch. A pod with no datasource cannot persist one and generates an ephemeral secret per process — lost on restart and shared with nothing, which is why that case warns loudly.
dvara.audit.max-events100000Capacity of append-only audit store
dvara.audit.store-prompts-by-defaultfalseStore prompt/response text in audit events
dvara.audit.prompt-retention-days90Retention period for stored prompts
dvara.audit.chain-verify.enabledtrueScheduled read-only verification that the HMAC hash chain still links. Registers only where a durable envelope sink exists — with nothing to round-trip there is nothing to verify.
dvara.audit.chain-verify.interval-ms3600000Fixed delay between verification passes (hourly).
dvara.audit.chain-verify.max-envelopes1000How many of the most recent envelopes each pass reads. Bounded deliberately: an unbounded verify would pull an entire audit history into memory. Because it is a tail, the oldest envelope it reads is its trust anchor — a pass proves the chain is intact across the window it covered, not across all history.

dvara.llm-gateway.audit.tier-retention-* (Hosted Tiers Only)

Prunes each account's audit history to the retention window of its tier.

On a self-managed or managed-hosting install this job does nothing, and that is by design. It keys on an account's tier; an install with no account, or an account carrying no tier, has no window and is skipped. Your retention is your own storage decision, and a sweep that guessed one for you would delete your compliance history on our schedule.

PropertyDefaultDescription
dvara.llm-gateway.audit.tier-retention-cron0 40 3 * * *When the sweep runs. Control-plane only — it issues DELETE against audit_events, the one operation that would make the trail not append-only, so it registers on Flightdeck and on no data-plane pod.
dvara.llm-gateway.audit.tier-retention-batch-size5000Rows per batch. Pruning runs in bounded batches so a first run on a large install cannot hold a long transaction against the table every request writes to.
Pruning breaks the hash chain across the pruned boundary

This is by construction and cannot be engineered away — a hash chain is a chain because removing a link is detectable. If you verify a chain on a hosted install and find a break at the retention horizon, that is the retention sweep, not tampering. The signed envelope is deleted together with its event, because keeping an envelope whose event is gone would be a dangling proof of nothing.

dvara.flightdeck.audit-archive.* (Audit Archive Job)

Off-by-default scheduled job that archives older audit events out of PostgreSQL into object storage. Per-workspace overrides live in Workspace.metadata.

PropertyDefaultDescription
dvara.flightdeck.audit-archive.enabledfalseMaster switch. When false, the scheduled job is registered but no-ops.
dvara.flightdeck.audit-archive.schedule0 0 2 * * ?Spring cron — daily 02:00 UTC by default.
dvara.flightdeck.audit-archive.retention-days180Events older than this in PostgreSQL are eligible for archive. Per-workspace override via Workspace.metadata["audit.archive.retention-days"].
dvara.flightdeck.audit-archive.grace-period-days7Extra days after a partition is VERIFIED in object storage before the corresponding PG rows are deleted. Safety window for restoring from PG if an object-storage issue surfaces post-upload.
dvara.flightdeck.audit-archive.bucket""Object-storage bucket name. Read from SPACES_BUCKET env var by the install scripts.
dvara.flightdeck.audit-archive.endpoint""S3-compatible endpoint (DigitalOcean Spaces, MinIO, AWS S3). Env: SPACES_ENDPOINT.
dvara.flightdeck.audit-archive.region""Object-storage region. Env: SPACES_REGION.
dvara.flightdeck.audit-archive.access-key-id""Object-storage access key. Env: SPACES_ACCESS_KEY_ID.
dvara.flightdeck.audit-archive.secret-access-key""Object-storage secret. Env: SPACES_SECRET_ACCESS_KEY.
dvara.flightdeck.audit-archive.max-rows-per-archive1000000Safety cap on rows per single (workspace, date) partition. If a partition exceeds this, the job logs a warning and skips it — operator must split it manually before the next run. Raise if a workspace generates more than 1M audit events/day.

dvara.llm-gateway.siem.* (Enterprise — SIEM Export)

PropertyDefaultDescription
dvara.llm-gateway.siem.splunk.enabledfalseEnable Splunk HEC SIEM export
dvara.llm-gateway.siem.splunk.hec-urlSplunk HEC endpoint URL
dvara.llm-gateway.siem.splunk.tokenHEC authentication token
dvara.llm-gateway.siem.splunk.index""Splunk index (blank = default)
dvara.llm-gateway.siem.splunk.sourcedvara-gatewaySplunk event source
dvara.llm-gateway.siem.splunk.source-type_jsonSplunk source type
dvara.llm-gateway.siem.splunk.timeout-seconds10HTTP timeout for the HEC POST. Failed posts are retried via the Kafka DLQ if Kafka is also configured; otherwise the audit event still lands in PostgreSQL (HEC export is best-effort).
dvara.llm-gateway.siem.cloudwatch.enabledfalseEnable CloudWatch Logs SIEM export
dvara.llm-gateway.siem.cloudwatch.log-groupCloudWatch log group name
dvara.llm-gateway.siem.cloudwatch.log-streamdvara-auditCloudWatch log stream name
dvara.llm-gateway.siem.cloudwatch.regionus-east-1AWS region
dvara.llm-gateway.siem.cloudwatch.endpointCustom endpoint (blank = regional default https://logs.{region}.amazonaws.com)
dvara.llm-gateway.siem.cloudwatch.batch-size25Flush events to CloudWatch in batches of this size — bounded by the CloudWatch PutLogEvents API limit of 10,000 events per call.
dvara.llm-gateway.siem.cloudwatch.timeout-seconds10HTTP timeout for PutLogEvents calls.
dvara.llm-gateway.siem.kafka.enabledfalseEnable Kafka SIEM export
dvara.llm-gateway.siem.kafka.bootstrap-serversKafka broker addresses (e.g. localhost:9092)
dvara.llm-gateway.siem.kafka.topicdvara-auditKafka topic for audit events
dvara.llm-gateway.siem.kafka.dead-letter-topic""Dead-letter topic for failed publishes (blank = disabled)
dvara.llm-gateway.siem.kafka.acksallProducer acknowledgment (all = survives broker failures)
dvara.llm-gateway.siem.kafka.security-protocolSecurity protocol (e.g. SASL_SSL)
dvara.llm-gateway.siem.kafka.sasl-mechanismSASL mechanism (e.g. PLAIN, SCRAM-SHA-256)
dvara.llm-gateway.siem.kafka.sasl-jaas-configJAAS config string for SASL auth

dvara.mcp-gateway.agentic.* (Enterprise — Agentic Governance)

PropertyDefaultDescription
dvara.mcp-gateway.agentic.enabledtrueEnable agentic governance features
dvara.mcp-gateway.agentic.session-ttl-minutes60Active session TTL
dvara.mcp-gateway.agentic.session-max-capacity10000Max tracked sessions
dvara.mcp-gateway.agentic.loop-detection.enabledtrueEnable loop detection
dvara.mcp-gateway.agentic.loop-detection.repetition-threshold5Consecutive same-tool calls to trigger
dvara.mcp-gateway.agentic.loop-detection.max-calls-per-minute60Rate limit threshold
dvara.mcp-gateway.agentic.loop-detection.auto-killfalseAuto-kill session on loop detection
dvara.mcp-gateway.agentic.approval.enabledtrueEnable human-in-the-loop approval gates
dvara.mcp-gateway.agentic.approval.timeout-seconds300Approval wait timeout
dvara.mcp-gateway.agentic.approval.default-actiondenyAction on approval timeout: deny or approve
dvara.mcp-gateway.agentic.approval.max-pending-approvals1000Max concurrent pending approvals
dvara.mcp-gateway.agentic.approval.poll-interval-ms1000How often a paused approval wait polls the durable approval record for a terminal decision. The blocking wait never holds a connection (each poll is a short borrow-and-return), so a decision written on any pod releases the paused call within one poll interval. Lower = snappier release at a higher DB read rate; also the release-latency floor.
dvara.mcp-gateway.agentic.approval.sweep-interval-ms60000Fixed-delay interval for the orphan sweep that transitions PENDING approvals past their expires_at to TIMEOUT — covers the case where the pod holding a blocked wait died. A live waiter times itself out first, so this only mops up orphans.
dvara.mcp-gateway.agentic.approval.sweep-initial-delay-ms60000Delay before the first orphan sweep after startup.
dvara.mcp-gateway.agentic.loop-detection.cycle-max-length4Max cycle pattern length
dvara.mcp-gateway.agentic.loop-detection.cycle-repetitions3Required cycle repetitions to trigger
dvara.mcp-gateway.agentic.loop-detection.history-size100Per-session tool-call history buffer size

dvara.flightdeck.security.builtin.* (Built-in Email/Password Auth)

Active when dvara.flightdeck.security.enabled=true and no OIDC issuer-uri or SAML metadata-url is set. Provides form login, invite-only user creation, password reset, and personal access tokens.

PropertyDefaultDescription
dvara.flightdeck.security.builtin.session-timeout-minutes60Form-login session timeout
dvara.flightdeck.security.builtin.max-failed-attempts5Consecutive failed logins before lockout
dvara.flightdeck.security.builtin.lockout-duration-minutes15Lockout duration after max failed attempts
dvara.flightdeck.security.builtin.base-urlhttp://localhost:8090Base URL for invitation and password-reset email links
dvara.flightdeck.security.builtin.pat.default-max-ttl-days365Platform-wide ceiling on PAT (personal access token) TTL. A per-workspace override lives in Workspace.metadata["pat.max-ttl-days"] and narrows below this ceiling. The absolute hard cap is 365 regardless of configuration — higher values are clamped at PAT creation time. PAT expiry is required (non-expiring PATs are not allowed).

Email delivery configuration has been consolidated under the new dvara.flightdeck.email.* namespace — see Email Configuration below.

dvara.flightdeck.email.* (Consolidated Email Configuration)

The Flightdeck pod is the only one that ships actual sends. Producers in any module hand off an email request; Flightdeck renders the template and dispatches via the configured transport. Both the URL fields and the transport + durability fields bind to the same dvara.flightdeck.email.* prefix.

URL + transport fields (consumed by producers and the transport tier):

PropertyEnv VarDefaultDescription
dvara.flightdeck.email.fromDVARA_FLIGHTDECK_EMAIL_FROMnoreply@dvarahq.comSender address — From: header on every outbound email
dvara.flightdeck.email.transportDVARA_FLIGHTDECK_EMAIL_TRANSPORTloglog prints to console (dev / CI); smtp sends over SMTP — also requires spring.mail.host, and refuses to start without it; resend POSTs to the Resend transactional API
dvara.flightdeck.email.public-endpoint-urlDVARA_FLIGHTDECK_EMAIL_PUBLIC_ENDPOINT_URLhttps://api.dvarahq.com/v1Data-plane URL shown in the welcome email and /signup/check-email
dvara.flightdeck.email.flightdeck-urlDVARA_FLIGHTDECK_EMAIL_FLIGHTDECK_URLhttps://platform.dvarahq.comConsole base URL for welcome / password-reset CTAs
dvara.flightdeck.email.docs-urlDVARA_FLIGHTDECK_EMAIL_DOCS_URLhttps://dvarahq.com/docsDocs link in the welcome email
dvara.flightdeck.email.resend-api-keyDVARA_FLIGHTDECK_EMAIL_RESEND_API_KEYResend API key (re_…); only used when transport=resend. The sender domain in email.from must be verified in Resend (or use the sandbox sender onboarding@resend.dev).
dvara.flightdeck.email.resend.verify-domain-at-startuptrueWhen transport=resend, the boot probe calls GET /domains and refuses to start on a production-class profile if the sender domain isn't verified. Skipped automatically for the sandbox sender. Disable on air-gapped / no-egress environments.

Durability + retry + DLQ + idempotency knobs (dvara.flightdeck.email.delivery.*):

PropertyDefaultDescription
delivery.enabledtrueMaster switch for the durability layer. false → listener falls back to fire-and-forget — no email_delivery_log row, no idempotency dedup, no retry, no DLQ. Useful for tests that don't want a Postgres dep.
delivery.max-attempts5Sync attempt 1 + 4 async retries before the row is marked DEAD_LETTERED
delivery.initial-backoff-ms30000Backoff before attempt 2
delivery.max-backoff-ms120000Ceiling on any single retry's backoff
delivery.backoff-multiplier2.0Exponential factor — delay(n) = min(initial × multiplier^(n-2), max) for n ≥ 2. With defaults: 0 / +30s / +60s / +120s / +120s → cumulative ~5m 30s before DLQ.
delivery.retry-sweep-interval-ms30000How often the retry sweeper polls the DB for due retries
delivery.retry-sweep-batch-size100Max rows the sweeper processes per tick
delivery.idempotency-ttl-minutes60Dedup window. A second publish with the same idempotency key within this window is a no-op. Producers reusing deterministic UUIDs across dlq-retention-days should regenerate to avoid losing the audit row on PK collision.
delivery.dlq-retention-days30How long SENT + DEAD_LETTERED rows are kept for operator review
delivery.cleanup-cron0 0 3 * * *Daily 03:00 UTC sweep that drops terminal rows past dlq-retention-days. PENDING_RETRY rows are never touched.

The legacy dvara.flightdeck.email.resend.retry-max-attempts / .retry-initial-backoff-ms / .retry-max-backoff-ms properties on the Resend transport are now defunct (retry lives at the listener level) and kept for one release with @Deprecated. Operators should migrate to the dvara.flightdeck.email.delivery.* namespace.

dvara.flightdeck.security.saml.* (SAML 2.0 SSO)

Active when dvara.flightdeck.security.saml.metadata-url is set. Mutually exclusive with OIDC — configuring both fails at startup.

PropertyEnv VarDefaultDescription
dvara.flightdeck.security.saml.metadata-urlDVARA_FLIGHTDECK_SECURITY_SAML_METADATA_URLIdP metadata XML URL (activates SAML mode)
dvara.flightdeck.security.saml.entity-idDVARA_FLIGHTDECK_SECURITY_SAML_ENTITY_IDdvaraSP entity ID
dvara.flightdeck.security.saml.registration-idDVARA_FLIGHTDECK_SECURITY_SAML_REGISTRATION_IDdvaraSAML registration ID used in the ACS and metadata URLs (/login/saml2/sso/{registrationId})
dvara.flightdeck.security.saml.role-attributeDVARA_FLIGHTDECK_SECURITY_SAML_ROLE_ATTRIBUTErolesSAML attribute containing roles
dvara.flightdeck.security.saml.email-attributeDVARA_FLIGHTDECK_SECURITY_SAML_EMAIL_ATTRIBUTENameIDSAML attribute for email (NameID uses the SAML NameID element)
dvara.flightdeck.security.saml.workspace-attributeDVARA_FLIGHTDECK_SECURITY_SAML_WORKSPACE_ATTRIBUTEworkspace_idSAML attribute for workspace ID
dvara.flightdeck.security.saml.name-attributeDVARA_FLIGHTDECK_SECURITY_SAML_NAME_ATTRIBUTEdisplayNameSAML attribute for display name
dvara.flightdeck.security.saml.auto-provisionDVARA_FLIGHTDECK_SECURITY_SAML_AUTO_PROVISIONfalseAuto-create users on first SAML login
dvara.flightdeck.security.saml.default-rolesDVARA_FLIGHTDECK_SECURITY_SAML_DEFAULT_ROLESviewerComma-separated roles for auto-provisioned users
dvara.flightdeck.security.saml.default-workspace-idDVARA_FLIGHTDECK_SECURITY_SAML_DEFAULT_WORKSPACE_IDWorkspace ID for auto-provisioned users (required when auto-provisioning is on)

dvara.vault.hashicorp.* / .aws.* / .azure.* (Full Vault Config)

The basic dvara.vault.backend selector and cache TTL are in the earlier Vault section. Backend-specific sub-fields:

PropertyEnv VarDefaultDescription
dvara.vault.hashicorp.namespaceVAULT_NAMESPACEVault Enterprise namespace (optional)
dvara.vault.hashicorp.role-idVAULT_ROLE_IDAppRole role ID
dvara.vault.hashicorp.secret-idVAULT_SECRET_IDAppRole secret ID
dvara.vault.aws.access-keyAWS_VAULT_ACCESS_KEYOptional. When blank, the AWS SDK's default credential provider chain resolves credentials in order: env vars (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY), Java system props, the EC2 instance profile, the ECS task role, the EKS IRSA web-identity token (AWS_WEB_IDENTITY_TOKEN_FILE), and the ~/.aws/credentials file. For Kubernetes deployments, prefer IRSA over static keys — leave both fields blank and attach the IAM role to the pod's ServiceAccount.
dvara.vault.aws.secret-keyAWS_VAULT_SECRET_KEYOptional. Same fallback chain as access-key — leave blank to use instance / IRSA / SDK-default credentials.
dvara.vault.azure.client-idAZURE_CLIENT_IDAzure AD application client ID
dvara.vault.azure.client-secretAZURE_CLIENT_SECRETAzure AD application client secret
dvara.vault.azure.tenant-idAZURE_TENANT_IDAzure AD workspace ID

dvara.llm-gateway.tls.providers.<name>.* (Full mTLS Config)

PropertyDefaultDescription
client-key-passwordPassword for client key or PKCS12 bundle
trust-store-passwordTrust store password (if encrypted)
store-typeautoAuto-detected from extension: .p12→PKCS12, .jks→JKS, .pem→PEM

dvara.flightdeck.compliance.* (Scheduled Compliance Reports)

PropertyEnv VarDefaultDescription
dvara.flightdeck.compliance.soc2-scheduleDVARA_FLIGHTDECK_COMPLIANCE_SOC2_SCHEDULE""Cron expression for SOC2 auto-generation (blank = disabled)
dvara.flightdeck.compliance.hipaa-scheduleDVARA_FLIGHTDECK_COMPLIANCE_HIPAA_SCHEDULE""Cron expression for HIPAA auto-generation
dvara.flightdeck.compliance.gdpr-scheduleDVARA_FLIGHTDECK_COMPLIANCE_GDPR_SCHEDULE""Cron expression for GDPR auto-generation
dvara.flightdeck.compliance.rbi-scheduleDVARA_FLIGHTDECK_COMPLIANCE_RBI_SCHEDULE""Cron expression for India RBI auto-generation (blank = disabled)
dvara.flightdeck.compliance.sebi-scheduleDVARA_FLIGHTDECK_COMPLIANCE_SEBI_SCHEDULE""Cron expression for India SEBI auto-generation (blank = disabled)
dvara.flightdeck.compliance.default-workspace-idDVARA_FLIGHTDECK_COMPLIANCE_DEFAULT_WORKSPACE_IDWorkspace ID for scheduled reports (blank = all workspaces)
dvara.flightdeck.compliance.retention-daysDVARA_FLIGHTDECK_COMPLIANCE_RETENTION_DAYS365Retention for generated reports

Other Flightdeck Properties

PropertyDefaultDescription
dvara.flightdeck.docs.base-urlhttps://dvarahq.com/docsWhere the Console's in-context Docs link points. Set it to a mirror on an air-gapped install, otherwise every help link leaves the network.
dvara.flightdeck.security.pat.expiry-sweep-interval-ms60000Fixed delay between sweeps that retire expired personal access tokens. Control-plane only — it runs on Flightdeck and on no data-plane pod.
dvara.flightdeck.security.pat.expiry-sweep-initial-delay-ms60000Delay before the first sweep after startup.
dvara.flightdeck.catalog.tiers-fileAbsolute path to a JSON file holding the tier catalogue. Tier bands are a pricing decision that changes on a different clock from releases, so this makes adjusting one an edit rather than a restart of every pod. A malformed file changes nothing, and that direction is the entire safety property: an empty catalogue would make every tier unresolvable, and an unresolvable tier fails open, standing every quota control down for every workspace at once while looking like the reload worked. A candidate is validated and swapped only on success. A missing file is not an error — the path may be a mount that has not appeared yet.
dvara.flightdeck.catalog.tiers-file-poll-ms10000How often the file's modification time is checked. Polling rather than a filesystem watch, deliberately: a watch silently misses events on some mounts, and a catalogue that quietly stopped reloading would be invisible until someone noticed a stale band.

dvara.flightdeck.portal.* (Workspace Self-Service Portal)

PropertyDefaultDescription
dvara.flightdeck.portal.enabledtrueEnable /portal/* routes for workspace users
dvara.flightdeck.portal.self-servicetrueAllow workspaces to create API keys via the portal

Flightdeck GitOps Config Import — Upload Cap

The Flightdeck Config Import surface (POST /config/import/preview) is protected by two layered byte caps. Sized for fleet snapshots — a 10K-workspace export is well under 5 MB — and small enough that a single tampered upload can't OOM the JVM.

PropertyDefaultDescription
spring.servlet.multipart.max-file-size25MBTomcat-level cap on the uploaded .json snapshot. A MaxUploadSizeExceededException handler turns Tomcat's bare 413 into a friendly redirect-with-flash on /config/import.
spring.servlet.multipart.max-request-size25MBMirrors max-file-size so a multipart envelope with extra form fields can't sneak past.

The import endpoint re-checks the same byte cap in-process for both file uploads and pasted JSON, so the cap applies uniformly regardless of input method.

dvara.region.* (Multi-Region Identity)

PropertyEnv VarDescription
dvara.region.idDVARA_REGION_IDRegion identifier used in data-residency routing
dvara.region.nameDVARA_REGION_NAMEHuman-readable region name

Setting dvara.region.id is only half of data residency. It states which region this gateway is in. What a given workspace is allowed to reach is configured per workspace, and it is not a property:

wherevalue
Workspace.metadata["data-residency.allowed-regions"]comma-separated region ids. Empty or absent means no restriction.

Both halves are required. A workspace with an allow-list, routed to a provider whose declared region is not on it, is refused with 403 DATA_RESIDENCY_VIOLATION; if no provider on the route is in an allowed region, 400 NO_REGIONAL_PROVIDER.

Residency was not enforced before 1.7.0

Enforcement compared the workspace's side of the constraint against a request-metadata key nothing in the product ever wrote, so DATA_RESIDENCY_VIOLATION could not fire on any request, under any configuration. An install that set both halves and saw no violations was not being protected — it was reading a control that could not trigger. Fixed in dvarahq/dvara#1335. If you relied on residency before 1.7.0, re-test it rather than assuming the absence of violations meant compliance.

Enforcement also fails open where there is genuinely nothing to compare — a provider with no declared region, or a call with no workspace (admin tooling, health probes). That is the exceptional path, not the normal one.

dvara.pii.* (PII Key Custody)

Which key seals the per-workspace data-encryption keys that PII tokens are built on. The mechanism is identical either way; what differs is who holds the key relative to who controls the data. Self-hosted, an application-held key sits inside your own trust boundary, so local is the default.

PropertyEnvDefaultDescription
dvara.pii.key-sourcelocallocal or kms. On kms, key custody moves to AWS KMS and no key material is held by the application — which is a materially different statement to put in a DPA from "our process holds the key".
dvara.pii.kms.key-idKMS key ARN or alias. Required when key-source=kms, and refused at startup rather than on the first PII request — discovering it mid-request means discovering it as a failed customer call.
dvara.pii.kms.regionus-east-1KMS region.
dvara.pii.kms.access-keyOptional. Omit both this and the secret key to use the default AWS credential chain, which is what makes "no application-held key material" true rather than nearly true on an instance profile or IRSA.
dvara.pii.kms.secret-keyOptional, and paired with the access key above.

A kms install needs no dvara.pii.token-encryption-master-password at all. AWS only — the seam that would allow another provider exists, but nothing else is implemented today.

Erasure is mode-independent. Destruction blanks the wrapped key in the DVARA database and never deletes the KMS key, which is both shared across workspaces and subject to a mandatory AWS deletion delay that would make immediate crypto-shredding impossible.

dvara.llm-gateway.stale-reads.* (Serving Through a Database Outage)

What an authentication lookup does when its origin is unreachable. Requires distributed caching to be enabled — the policy is implemented by the cache-aside layer, so setting it alone registers nothing.

PropertyDefaultDescription
dvara.llm-gateway.stale-reads.enabledfalseWhen true, an auth lookup whose origin is unreachable is answered from the last known good value rather than failing closed. Without it the config cache TTL is the data plane's entire outage tolerance: past it every entry expires, every lookup falls through to a dead database, and authentication fails closed. Stale is only ever a fallback, never a shortcut — the origin is still tried on every miss and a remembered value is served only after it has actually failed, so steady-state behaviour is unchanged.
dvara.llm-gateway.stale-reads.ceiling-seconds900How long a remembered value may be served. Past it, reads fail closed again. This is the whole trade, and it is a dial rather than a constant: enabling it means a revoked API key keeps working for up to this long while the database is down — right for most installs, and wrong for one whose compliance posture says revocation is immediate.

Writes are deliberately never covered. An admin mutation against a dead database fails, loudly — the caller has to know their change did not land. Serving a stale read states what was true; accepting a write would be a lie about what is. Revocation also forgets the remembered copy, not just the cache entry, so an origin blip cannot re-serve a revoked key.

A pod serving on remembered credentials is otherwise indistinguishable from a healthy one, so it is surfaced on /actuator/gateway-status and as the gateway_config_stale_seconds gauge.

dvara.llm-gateway.ce.telemetry.* (Opt-In Usage Telemetry)

Unlicensed installs only, and off by default. A licensed or air-gapped install never wires the emitter at all — the send path is impossible by bean-absence rather than by a runtime check.

PropertyDefaultDescription
dvara.llm-gateway.ce.telemetry.enabledfalseThe opt-in. Off means no emitter, no scheduler and no network. On means a scheduled best-effort HTTPS POST of a five-field, non-sensitive payload: an anonymous random install id, the posture, the version, a coarse request-volume bucket (never an exact count), and the emit time. Nothing else leaves the box — no prompt or response content, no PII, no workspace data, no keys, no provider or model names. Either posture logs a startup line stating which applies.
dvara.llm-gateway.ce.telemetry.endpointhttps://telemetry.dvarahq.com/ce/v1Collection URL. Override it to point at a proxy, or at a local sink if you want to inspect exactly what would be sent.
dvara.llm-gateway.ce.telemetry.interval-hours24Emit cadence.
dvara.llm-gateway.ce.telemetry.initial-delay-hours1Delay before the first emit after startup.

dvara.encryption.* (AES-256-GCM Master Password)

PropertyEnv VarDescription
dvara.encryption.master-passwordDVARA_ENCRYPTION_MASTER_PASSWORDMaster password used to derive AES-256-GCM keys for provider credentials and the PII token store. Required when storing credentials in ENCRYPTED mode.

See Credentials & BYOK for rotation guidance.

MCP Plane Observability Config

For the MCP plane, in addition to the existing MCP timeout properties:

PropertyDefaultDescription
management.endpoints.web.exposure.includehealth,prometheusActuator endpoints to expose
management.prometheus.metrics.export.enabledtrueEnable Prometheus scrape endpoint
management.tracing.sampling.probability1.0OTel sampling probability (env: TRACING_SAMPLING_PROBABILITY)
management.otlp.tracing.endpointhttp://localhost:4318/v1/tracesOTLP trace exporter endpoint (env: OTEL_EXPORTER_OTLP_ENDPOINT)
dvara.mcp-gateway.agentic.approval.default-actiondenyAction on timeout: deny or approve