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:
- built-in defaults;
- values loaded from
configPath; - the inline
configoverride passed tocreateDhal()or an adapter.
Objects are merged recursively. Arrays are replaced rather than concatenated.
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
{
"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
| Field | Purpose |
|---|---|
schemaVersion | Must be "1" for the v1 configuration contract. |
mode | Global off, monitor, block, or strict behaviour. |
trustProxy | Allows trusted forwarding headers to determine the client IP. Enable only behind a trusted proxy. |
runtime | Internal-error, inspection-budget, and bypass behaviour. |
identity | Header names used to resolve user, tenant, and API-key identities. |
ip | Allow/block lists and reputation settings. |
rateLimit | Default and route-specific request limits. |
rules | Signature, bot, payload, API, header, content-type, honeypot, and credential controls. |
routes | Route-level overrides for modes, rules, limits, reputation, and responses. |
policy | Severity, suppressions, sampling, audit metadata, and CI requirements. |
observability | Redaction, logs, events, OpenTelemetry, correlation, and webhooks. |
response | Default blocking status code and public message. |
Runtime safety
{
"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.
{
"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:
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
npx dhal test-config
npx dhal migrate --checkThe package also exports the JSON Schema as @rokadhq/dhal/dhal.schema.json and programmatically through getDhalConfigJsonSchema().