DNS Configuration
DNS Configuration Reference
Start without a config file
When launched without an explicit --config, DNS loads config.json from the
current directory if it exists. Otherwise it starts with a local profile:
127.0.0.1:5353 for DNS, 127.0.0.1:8081 for health, recursive resolution
restricted to loopback clients, built-in allow-all query policy, memory cache,
and audit logging disabled. Query it with dig -p 5353 @127.0.0.1 example.com.
A missing explicitly supplied config path or an invalid config causes startup
to fail. Partial explicit configs continue to use the JSON defaults documented
below.
{
// REQUIRED: no
// TYPE: string (host:port)
// DEFAULT: "0.0.0.0:8080"
// DNS UDP+TCP listener bind address.
"listen_addr": "0.0.0.0:8080",
// REQUIRED: no
// TYPE: string | DEFAULT: null
// Optional policy JSON path for DNS policy engine.
"policy_file_path": "/etc/titaniumguard/dns-policy.json",
// REQUIRED: no
// TYPE: RuleEngineConfig
// DEFAULT: { "max_trace_facts": 64, "enable_explain_logs": true }
"rule_engine": {
// REQUIRED: no | TYPE: u32 | DEFAULT: 64
"max_trace_facts": 64,
// REQUIRED: no | TYPE: bool | DEFAULT: true
"enable_explain_logs": true
},
// REQUIRED: no
// TYPE: string[] (IP addresses)
// DEFAULT: []
// If empty, built-in root hints are used.
"resolvers": ["1.1.1.1", "8.8.8.8"],
// REQUIRED: no
// TYPE: QueryLimitsConfig
// Bounds the complete query lifecycle across every transport.
"query_limits": {
// REQUIRED: no | TYPE: u64 | DEFAULT: 10000
"timeout_ms": 10000,
// REQUIRED: no | TYPE: u32 | DEFAULT: 1024
"max_concurrent": 1024
},
// REQUIRED: no
// TYPE: RecursionConfig
"recursion": {
"enabled": true,
"allowed_client_cidrs": ["10.0.0.0/8"],
// REQUIRED: no | TYPE: u64 | DEFAULT: 2000
"upstream_timeout_ms": 2000,
// REQUIRED: no | TYPE: u32 | DEFAULT: 24
"max_referral_depth": 24,
// REQUIRED: no | TYPE: u32 | DEFAULT: 64
"max_upstream_exchanges": 64,
// REQUIRED: no | TYPE: u32 | DEFAULT: 2
"max_parallel_upstreams": 2,
// REQUIRED: no | TYPE: u64 | DEFAULT: 50
"hedge_delay_ms": 50
},
// REQUIRED: no
// TYPE: ZoneConfig[]
// DEFAULT: []
"zones": [
{
// REQUIRED: yes
// TYPE: string
// Zone apex, usually FQDN with trailing dot.
"name": "corp.internal.",
// REQUIRED: yes
// TYPE: ZoneSoaConfig
"soa": {
// REQUIRED: yes | TYPE: string | ALIAS: MNAME
"mname": "ns1.corp.internal.",
// REQUIRED: yes | TYPE: string | ALIAS: RNAME
"rname": "dns-admin.corp.internal.",
// REQUIRED: yes | TYPE: u32 | ALIAS: SERIAL
"serial": 2026022201,
// REQUIRED: yes | TYPE: u32 | ALIAS: REFRESH
"refresh": 3600,
// REQUIRED: yes | TYPE: u32 | ALIAS: RETRY
"retry": 600,
// REQUIRED: yes | TYPE: u32 | ALIAS: EXPIRE
"expire": 1209600,
// REQUIRED: yes | TYPE: u32 | ALIAS: MINIMUM
"minimum": 300,
// REQUIRED: no | TYPE: u32 | DEFAULT: 3600
"ttl": 3600
},
// REQUIRED: no
// TYPE: { owner_name: { record_type: ZoneRecordSetConfig } }
// DEFAULT: {}
"records": {
"@": {
// REQUIRED: no
// TYPE: ZoneRecordSetConfig
"A": {
// REQUIRED: no | TYPE: u32 | DEFAULT: 300
"ttl": 300,
// REQUIRED: yes | TYPE: string[]
"values": ["10.10.0.53"]
},
"NS": {
"ttl": 3600,
"values": ["ns1.corp.internal."]
}
}
}
}
],
// REQUIRED: no
// TYPE: CachingConfig
// DEFAULT: { "type": "memory", "max_entries": 100000 }
"caching": {
// REQUIRED: yes
// TYPE: enum
// ALLOWED: "memory", "redis"
"type": "memory",
// REQUIRED: no (memory only) | TYPE: u64 | DEFAULT: 100000
"max_entries": 100000,
// REQUIRED: yes (redis only) | TYPE: string
// "url": "redis://127.0.0.1/",
// REQUIRED: no (redis only) | TYPE: string | DEFAULT: "dns:cache:"
// "key_prefix": "dns:cache:"
// REQUIRED: no (redis only) | TYPE: bool | DEFAULT: false
// "required": false,
// REQUIRED: no (redis only) | TYPE: u64 | DEFAULT: 250
// "timeout_ms": 250,
// REQUIRED: no (redis only) | TYPE: u32 | DEFAULT: 3
// "failure_threshold": 3
},
// REQUIRED: no
// TYPE: TransportConfig
// DEFAULT: {}
"transports": {
// REQUIRED: no | TYPE: TlsTransportConfig | DEFAULT: null
"dot": {
// REQUIRED: yes | TYPE: string (host:port)
"listen_addr": "0.0.0.0:853",
// REQUIRED: yes | TYPE: string
"cert_path": "/etc/titaniumguard/tls.crt",
// REQUIRED: yes | TYPE: string
"key_path": "/etc/titaniumguard/tls.key"
},
// REQUIRED: no | TYPE: HttpsTransportConfig | DEFAULT: null
"doh": {
// REQUIRED: yes | TYPE: string (host:port)
"listen_addr": "0.0.0.0:8443",
// REQUIRED: yes | TYPE: string
"cert_path": "/etc/titaniumguard/tls.crt",
// REQUIRED: yes | TYPE: string
"key_path": "/etc/titaniumguard/tls.key",
// REQUIRED: no | TYPE: string | DEFAULT: null
"dns_hostname": "dns.example.com",
// REQUIRED: no | TYPE: string | DEFAULT: "/dns-query"
"endpoint": "/dns-query",
// REQUIRED: no | TYPE: u32 | DEFAULT: 1024
"max_doh_h2_connections": 1024,
// REQUIRED: no | TYPE: u32 | DEFAULT: 100
"max_doh_h2_streams_per_connection": 100,
// REQUIRED: no | TYPE: usize | DEFAULT: 4096
"max_doh_body_bytes": 4096,
// REQUIRED: no | TYPE: u64 | DEFAULT: 5000
"read_header_timeout_ms": 5000,
// REQUIRED: no | TYPE: u64 | DEFAULT: 10000
"read_timeout_ms": 10000,
// REQUIRED: no | TYPE: u64 | DEFAULT: 10000
"write_timeout_ms": 10000,
// REQUIRED: no | TYPE: u64 | DEFAULT: 30000
"idle_timeout_ms": 30000,
// REQUIRED: no | TYPE: ODoHConfig | DEFAULT: null
// HPKE identifiers from RFC 9180.
"odoh": {
// REQUIRED: yes (when odoh is set) | TYPE: string
// ALLOWED (RFC 9180 Table 2):
// "DHKEM(P-256, HKDF-SHA256)"
// "DHKEM(P-384, HKDF-SHA384)"
// "DHKEM(P-521, HKDF-SHA512)"
// "DHKEM(X25519, HKDF-SHA256)"
// "DHKEM(X448, HKDF-SHA512)"
// Also accepts numeric values such as "0x0020".
"kem_id": "DHKEM(X25519, HKDF-SHA256)",
// REQUIRED: yes (when odoh is set) | TYPE: string
// ALLOWED (RFC 9180 Table 3):
// "HKDF-SHA256"
// "HKDF-SHA384"
// "HKDF-SHA512"
// Also accepts numeric values such as "0x0001".
"kdf_id": "HKDF-SHA256",
// REQUIRED: yes (when odoh is set) | TYPE: string
// ALLOWED (RFC 9180 Table 5):
// "AES-128-GCM"
// "AES-256-GCM"
// "CHACHA20POLY1305"
// "EXPORT-ONLY"
// Also accepts numeric values such as "0x0001".
"aead_id": "AES-128-GCM",
// REQUIRED: yes (when odoh is set) | TYPE: string (base64)
// Public key bytes for the selected KEM.
"public_key_base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
},
// REQUIRED: no | TYPE: QuicTransportConfig | DEFAULT: null
"doq": {
"listen_addr": "0.0.0.0:8853",
"cert_path": "/etc/titaniumguard/tls.crt",
"key_path": "/etc/titaniumguard/tls.key",
// REQUIRED: no | TYPE: string | DEFAULT: null
"dns_hostname": "dns.example.com",
// REQUIRED: no | TYPE: u32 | DEFAULT: 512
"max_concurrent_queries": 512,
// REQUIRED: no | TYPE: u32 | DEFAULT: 100
"max_streams_per_connection": 100,
// REQUIRED: no | TYPE: u64 | DEFAULT: 30000
"idle_timeout_ms": 30000,
// REQUIRED: no | TYPE: u32 | DEFAULT: 65535
"max_message_bytes": 65535
},
// REQUIRED: no | TYPE: QuicTransportConfig | DEFAULT: null
"doh3": {
"listen_addr": "0.0.0.0:8444",
"cert_path": "/etc/titaniumguard/tls.crt",
"key_path": "/etc/titaniumguard/tls.key",
"dns_hostname": "dns.example.com",
"max_concurrent_queries": 512,
"max_streams_per_connection": 100,
"idle_timeout_ms": 30000,
// DoH3 uses the same default body limit as DoH.
"max_message_bytes": 4096
}
},
// REQUIRED: no
// TYPE: LoggingConfig
// DEFAULT: values below
"logging": {
// REQUIRED: no | TYPE: bool | DEFAULT: true
"enabled": true,
// REQUIRED: no | TYPE: enum | DEFAULT: "strict"
// ALLOWED: "strict", "standard", "enterprise_forensics"
"default_mode": "strict",
// REQUIRED: no | TYPE: string | DEFAULT: "var/log/dns"
"log_dir": "var/log/dns",
// REQUIRED: no | TYPE: u64 | DEFAULT: 60
"key_rotation_minutes": 60,
// REQUIRED: yes in production when logging.enabled=true
// TYPE: string | DEFAULT: ""
"hmac_secret": "replace-with-strong-secret",
// REQUIRED: no | TYPE: u16 | DEFAULT: 30
"default_retention_days": 30,
// REQUIRED: no | TYPE: u64 | DEFAULT: 300
"purge_interval_seconds": 300,
// REQUIRED: no | TYPE: u32 | DEFAULT: 4096
"queue_capacity": 4096,
// REQUIRED: no | TYPE: u32 | DEFAULT: 64
"batch_size": 64,
// REQUIRED: no | TYPE: u64 | DEFAULT: 10
"flush_interval_ms": 10,
// REQUIRED: no | TYPE: TenantLoggingRule[] | DEFAULT: []
"tenants": [
{
// REQUIRED: yes | TYPE: string
"tenant_id": "tenant-a",
// REQUIRED: yes | TYPE: enum
// ALLOWED: "strict", "standard", "enterprise_forensics"
"mode": "enterprise_forensics",
// REQUIRED: no | TYPE: u16 | DEFAULT: null
"retention_days": 90,
// REQUIRED: no | TYPE: string[] | DEFAULT: []
"client_cidrs": ["10.0.0.0/8", "192.168.0.0/16"]
}
]
}
}
ODoH discovery endpoint
When transports.doh.odoh is configured, the DoH listener also serves:
GET /.well-known/odohconfigs
The response body is binary ObliviousDoHConfigs data (RFC 9230 format).
The DNS server no longer publishes placeholder ODoH keys. transports.doh.odoh.public_key_base64
must be configured with real key material matching kem_id.