Rokad

Configuration

Understand dhal.json, operating modes, runtime safety, and configuration precedence.

View repository
dhal documentation
Page 2 of 9

Dhal uses schema version 1. The default configuration file is dhal.json, although every adapter and createDhal() can receive a different configPath.

Configuration precedence

Dhal builds its effective configuration in this order:

  1. built-in defaults;
  2. values loaded from configPath;
  3. the inline config override passed to createDhal() or an adapter.

Objects are merged recursively. Arrays are replaced rather than concatenated.

ts
import { createDhal } from "@rokadhq/dhal";

const protection = createDhal({
  configPath: "dhal.json",
  config: {
    mode: "monitor",
    response: {
      message: "Request rejected by application security policy"
    }
  }
});

Minimal production-onboarding configuration

json
{
  "schemaVersion": "1",
  "mode": "monitor",
  "trustProxy": false,
  "runtime": {
    "onInternalError": "allow",
    "internalErrorStatusCode": 500,
    "maxInspectionMs": 25,
    "bypass": {
      "enabled": true,
      "paths": ["/health", "/healthz", "/ready", "/readyz"],
      "methods": ["OPTIONS"]
    }
  },
  "rateLimit": {
    "enabled": true,
    "store": "memory",
    "keyBy": ["ip", "route"],
    "default": {
      "windowSeconds": 60,
      "max": 120
    },
    "routes": {}
  }
}

Unspecified values inherit Dhal's defaults.

Core fields

FieldPurpose
schemaVersionMust be "1" for the v1 configuration contract.
modeGlobal off, monitor, block, or strict behaviour.
trustProxyAllows trusted forwarding headers to determine the client IP. Enable only behind a trusted proxy.
runtimeInternal-error, inspection-budget, and bypass behaviour.
identityHeader names used to resolve user, tenant, and API-key identities.
ipAllow/block lists and reputation settings.
rateLimitDefault and route-specific request limits.
rulesSignature, bot, payload, API, header, content-type, honeypot, and credential controls.
routesRoute-level overrides for modes, rules, limits, reputation, and responses.
policySeverity, suppressions, sampling, audit metadata, and CI requirements.
observabilityRedaction, logs, events, OpenTelemetry, correlation, and webhooks.
responseDefault blocking status code and public message.

Runtime safety

json
{
  "runtime": {
    "onInternalError": "allow",
    "internalErrorStatusCode": 500,
    "maxInspectionMs": 25,
    "bypass": {
      "enabled": true,
      "paths": ["/health", "/ready"],
      "methods": ["OPTIONS"]
    }
  }
}

Use onInternalError: "allow" during initial monitoring and when availability is the primary objective. strict mode or onInternalError: "block" is appropriate only after dependencies and failure behaviour have been validated.

Bypass paths and methods skip inspection. Keep this list narrow and limited to endpoints that cannot process untrusted application input.

Proxy trust

Do not enable trustProxy merely because the application is deployed behind a load balancer. First confirm that direct access is blocked and that the proxy overwrites, rather than appends untrusted, forwarding headers.

json
{
  "trustProxy": true
}

Incorrect proxy trust can allow an attacker to control the IP address used for allow lists, reputation, rate limits, and audit events.

Inline configuration

Inline configuration is useful for environment-specific values that are not secrets:

ts
const protection = createDhal({
  configPath: "dhal.json",
  config: {
    mode: process.env.DHAL_MODE === "block" ? "block" : "monitor"
  }
});

Store secrets in environment variables or a secrets manager. Configuration fields such as apiKeyEnv and secretEnv contain environment-variable names, not secret values.

Validate the file

bash
npx dhal test-config
npx dhal migrate --check

The package also exports the JSON Schema as @rokadhq/dhal/dhal.schema.json and programmatically through getDhalConfigJsonSchema().