SentrySentry
Configuration

Configuration Reference (sentry.toml)

Every sentry.toml section — fields, types, defaults and examples, per the schema in crates/sentry-core/src/config.rs.

Configuration Reference (sentry.toml)

All Sentry configuration lives in a single TOML file. The typed schema is the SentryConfig struct in crates/sentry-core/src/config.rs — this page documents every section with fields, types and defaults taken from it. config/sentry.example.toml is the canonical, ready-to-copy example.

Table of contents

Loading and environment overlay

The file is loaded with figment: the TOML is the base and any field can be overridden by an environment variable in the format SENTRY_<SECTION>__<KEY> (uppercase, __ separates nesting levels). Secrets never go into the committed file — env only.

VariableOverrides / feeds
SENTRY_STORAGE__POSTGRES__URL[storage.postgres] url
SENTRY_CF_TOKENCloudflare API token (CF sources/actions)
SENTRY_CF_ZONECloudflare zone ID (challenge action)
SENTRY_CF_ACCOUNTCloudflare account ID (/64 IP Lists)
SENTRY_LLM_KEYLLM provider API key
SENTRY_ABUSEIPDB_KEYAbuseIPDB key ([ip_lookup], report action, feeds)
SENTRY_SESSION_SECRETHMAC key for dashboard sessions
SENTRY_WEBHOOK_SECRETHMAC signing secret for webhooks
SENTRY_EDGE_CHALLENGE_SECRETEdge challenge PoW secret

[core]

Core runtime: queue buffers and hot ingest path tuning. Under overload the action queue fills first — Sentry sheds actions, never events.

FieldTypeDefaultDescription
data_dirstring (path)/var/lib/sentryData directory (MMDB files, models, cache).
channel_bufferinteger4096Event channel buffer between sources and the daemon.
action_bufferinteger4096Capacity of the deferred action queue (post-processing workers).
action_workersinteger1Parallel workers executing network-bound actions. 1 preserves strict action ordering.
persist_bufferinteger2048Capacity of the batched persistence queue (one multi-row INSERT per batch).
dedupe_ttl_secsinteger10Ingest dedupe window: identical (ip, method, path) events inside the window are dropped before the pipeline.
[core]
data_dir = "/var/lib/sentry"
action_workers = 1
dedupe_ttl_secs = 10

[overload]

Overload response: measured pressure (queue occupancy, Postgres INSERT latency) switches on tiered degradation of telemetry. Detection (pipeline) and containment (block table, actions) never degrade.

FieldTypeDefaultDescription
enabledbooltrueMaster switch for pressure detection and degradation tiers.
queue_pressurefloat0.8Queue occupancy (0.0–1.0) that turns pressure on.
release_pressurefloat0.5Occupancy below which pressure turns off again (hysteresis).
release_secsinteger30Seconds of sustained relief before pressure clears.
pg_insert_budget_msinteger250INSERT latency EMA (ms) that turns pressure on by itself.
sample_keepfloat0.1Fraction of low-priority events (Allow < High) kept under pressure — deterministic per source IP.
coalesce_benignbooltrueCollapse repeated benign requests (Allow, GET, no signals) per (ip, path).
coalesce_window_secsinteger60Benign coalescing window: only the first hit persists.
coalesce_capinteger4096Max tracked (ip, path) buckets; the oldest is evicted beyond it.

[deployment]

Deployment positioning: where Sentry sits relative to the application.

FieldTypeDefaultDescription
modestringpassivepassive reads logs/mirrored traffic and acts ex-post via actions; inline runs the [edge] reverse proxy in front of the app and enforces verdicts before the request reaches the backend.
instance_idstring(hostname)Instance label for metrics in multi-node deployments (sentry_instance_info gauge). Empty = derived from the hostname.

[runtime]

Tokio runtime tuning (F14): worker count, CPU pinning and thread priority, applied when the binary builds its runtime — before any command runs. Everything is optional; an empty section means stock tokio behavior (one worker per core, OS placement, default priority). Useful when the protection daemon shares a box with the app it protects: dedicate cores instead of letting the scheduler fight the application.

FieldTypeDefaultDescription
worker_threadsinteger0Tokio async worker threads (0 = one per core).
pin_coresarray of integer[]Pin workers to these CPU core ids in order (workers cycle through the list); empty lets the OS place threads.
thread_prioritystringnormalidle | below_normal | normal | above_normal | high — mapped to nice/priority-class per platform; above-normal levels need privileges on unix (failure warns and continues).

[storage.postgres]

Persistence connection (Postgres via sqlx).

FieldTypeDefaultDescription
urlstring(empty)Connection string postgres://user:pass@host:port/db. Prefer SENTRY_STORAGE__POSTGRES__URL.
max_connectionsinteger10Connection pool size.
acquire_timeout_secsinteger5Seconds waiting for a free pool connection before failing fast (sqlx default is 30).
statement_timeout_secsinteger30Seconds before an INSERT is cancelled and the batch dropped, keeping the persist worker from stalling.

[[source]]

Event sources: each entry declares a plugin type and a plugin-specific options map. Implemented types: nginx, syslog, cloudflare and tcp (unknown types are skipped with a log line).

type = "nginx" — nginx access.log tail with log_format parsing and automatic real-client-IP resolution.

OptionTypeDefaultDescription
pathstring/var/log/nginx/access.logLog file path.
formatstringcombined formatSame string as the nginx log_format. $http_* tokens become event headers (usable in DSL rules like header.cf-connecting-ip).
[[source]]
type = "nginx"
[source.options]
path = "/var/log/nginx/access.log"
format = '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'

type = "syslog" — RFC 5424 receiver (RFC 3164 fallback) for firewalls and network equipment; the peer address becomes the client IP.

OptionTypeDefaultDescription
bindstring0.0.0.0:5140Listen address (ports < 1024 need root).
transportstringudpudp | tcp (TCP accepts newline framing and RFC 6587 octet-counting).

type = "cloudflare" — polling of the Cloudflare Logs API (NDJSON); requires an Enterprise plan and a token with Logs:Read via env.

OptionTypeDefaultDescription
zone_idstring(required)Cloudflare zone ID.
token_envstringSENTRY_CF_TOKENEnvironment variable holding the API token.
poll_secsinteger30Interval between polls.
startstring (RFC 3339)(now)Cursor for the first poll.
api_basestringhttps://api.cloudflare.com/client/v4API base URL.

type = "tcp" — passive packet capture with SYN fingerprinting (masscan/zmap/nmap). Requires the pcap build feature and elevated privileges (Npcap on Windows, CAP_NET_RAW on Linux).

OptionTypeDefaultDescription
interfacestring(required)Capture interface.
portsstring (CSV)(empty = all)Destination-port filter, e.g. "80,443,8443".
payload_capinteger8192Reassembled bytes kept per flow.
flow_capinteger65536Max tracked flows (FIFO eviction).
channel_bufferinteger1024Queue between the capture loop and the daemon (minimum 64).
syn_window_msinteger100Per-IP SYN coalescing window (0 = off); a burst becomes 1 event with TcpData.syn_count.

[geo]

Geo/ASN enrichment with the MaxMind GeoLite2 databases. Missing files = enrichment is a graceful no-op.

FieldTypeDefaultDescription
city_dbstring (path)/var/lib/sentry/GeoLite2-City.mmdbGeoLite2-City database.
asn_dbstring (path)/var/lib/sentry/GeoLite2-ASN.mmdbGeoLite2-ASN database.

[routes]

Routes your application actually serves. Requests outside the set (or with a disallowed method) raise the UnknownRoute signal.

FieldTypeDefaultDescription
knownarray of [[routes.known]][]Each entry: path (exact or glob /api/*) and methods (empty = any method).
[[routes.known]]
path = "/api/users/*"
methods = ["GET", "POST", "PATCH", "DELETE"]

[route_learner]

Continuous route learning: background task that infers stable route shapes from recent Postgres events and hot-reloads them. Requires Postgres storage.

FieldTypeDefaultDescription
enabledboolfalseEnables continuous learning.
interval_secsinteger300How often a learning pass runs.
window_secsinteger3600Look-back window for events.
min_hitsinteger10Minimum hits for a shape to be considered stable.
min_ipsinteger2Minimum distinct IPs that hit the shape.

[rules]

Rules engine: default packs, static inline rules, reputation feeds and presets.

[[rules.pack]]

Default rule packs.

FieldTypeDefaultDescription
namestring—Pack name (list below).
modestringshadowshadow (annotate/log only) | enforce (act) | off.
paramstable{}Extra pack parameters (e.g. countries, domains), flattened as <pack>__<param>.

Available packs: vpn_proxy, tor, crawlers_bad, crawlers_good (splits into crawlers_good_verified / crawlers_good_unverified_ok with bot verification enabled), sensitive_paths (the only one in enforce by default), empty_ua, http_anomaly, rate_scan, country_blocklist (params.countries) and host_allowlist (params.domains).

[[rules.pack]]
name = "country_blocklist"
mode  = "off"
[rules.pack.params]
countries = ["RU", "CN"]

[[rules.custom]]

Static inline rules (in addition to the DB-managed ones).

FieldTypeDefaultDescription
namestring—Human-readable rule name.
priorityinteger0Lower = evaluated first.
matchstring (DSL)—Match expression; a parse failure aborts config load.
actionstring—allow | block | challenge | rate_limit | log | tag.
tagsarray of string[]Free-form tags for grouping.
log_levelstringinfoConsole verbosity: silent | info | warn | error.
[[rules.custom]]
name = "allow internal monitoring"
priority = 1
match = 'ip=10.0.0.0/8'
action = "allow"
log_level = "silent"

[[rules.feeds]]

Reputation feeds and datasets (one-per-line lists).

FieldTypeDefaultDescription
namestring—Feed name (prefix of the synthetic rule tag).
urlstring—http/https URL (loopback/private hosts are rejected — SSRF guard).
kindstringipip | user_agent | path | ja3 — one-per-line lists compile into a synthetic rule.
refresh_hoursinteger24Refresh interval in hours.
tierstringmaliciousunknown | clean | suspicious | malicious | datacenter | vpn | tor | authorized | promiscuous (only for kind = "ip").
actionstring(empty)For ip: empty = enrichment only; for user_agent/path: empty = log. Values: block | challenge | rate_limit | …
enabledbooltruePause the feed without removing it.
headers_envtable{}Header → env var map for authenticated feeds (e.g. Key = "SENTRY_ABUSEIPDB_KEY").
[[rules.feeds]]
name = "spamhaus_drop"
url  = "https://www.spamhaus.org/drop/drop.txt"
tier = "malicious"
refresh_hours = 24
action = "block"

DB-backed datasets (the Postgres equivalent of the user_agent/path/ja3 feeds) have no TOML entry — manage them via sentry datasets import | list | enable | disable | delete | fetch.

feed_presets

Built-in feed presets, expanded into feeds at load time.

FieldTypeDefaultDescription
feed_presetsarray of string[]Valid names: tor_exit, firehol_level1, proxy_list, open_source_vpn_ips, anti_vpn. Preset feeds block by default; a user-defined feed with the same name wins.

[scorer]

Signal weights and repetition bonus.

FieldTypeDefaultDescription
weightstable name → integer{}Overrides a signal's weight by snake_case key (e.g. sql_injection = 35, random_scan = 25).
repetition_bonusbooltrueScore bonus for repeated signals in the sliding window.
repetition_window_secsinteger60Repetition tracking window duration.

[policy]

Verdict policy: maps risk level → verdict. Ordered overrides via [[policy.override]]: the first matching DSL expression forces the verdict.

FieldTypeDefaultDescription
infostringallowVerdict for Info events.
lowstringallowVerdict for Low events.
mediumstringrate_limitVerdict for Medium events.
highstringchallengeVerdict for High events.
criticalstringblockVerdict for Critical events.
overridearray[]Each entry: match (DSL) and verdict (allow | rate_limit | challenge | block | quarantine).
[[policy.override]]
match = 'path ^/wp-admin$ AND NOT ip=10.0.0.0/8'
verdict = "block"

[escalation]

Repeat-offender escalation: every event with a non-Allow verdict records 1 strike for the client IP. Strikes only ever raise the verdict, never lower it, and the window should outlive the edge action TTL (returning offenders re-block on their first violating event).

FieldTypeDefaultDescription
enabledbooltrueEnables strike-based escalation.
window_secsinteger604800Strikes expire N seconds after the last violation (7 days, outliving the typical 24h edge TTL).
challenge_atinteger3Strikes to escalate to at least challenge.
block_atinteger5Strikes to escalate to block.
persistbooltrueMirrors strikes to the ip_state table (Postgres) and pre-warms the tracker on startup.

[scan]

Behavioral sweep detection: per-IP sliding window counting 4xx responses — distinct 4xx paths → RandomScan signal; total 4xx → ScanBehavior.

FieldTypeDefaultDescription
enabledbooltrueEnables the scan trackers.
window_secsinteger60Sliding window duration.
distinct_pathsinteger8Distinct 4xx paths per IP that trigger RandomScan.
not_foundinteger10Total 4xx responses per IP that trigger ScanBehavior.

[behavior]

Behavioral auth/directory attack detection over a per-IP sliding window.

FieldTypeDefaultDescription
enabledbooltrueEnables the behavior trackers.
window_secsinteger300Per-IP sliding window.
auth_failuresinteger10401/403 failures on login routes that trigger AuthBruteForce.
distinct_uasinteger3Distinct User-Agents failing auth that trigger CredentialStuffing.
wordlist_hitsinteger5404s on wordlist paths that trigger DirectoryBruteForce (good crawlers are exempt).
suspicious_success_min_failuresinteger3Auth failures that must precede a 2xx on a login route to fire SuspiciousLoginSuccess (0 disables).
login_patternsarray of string[]Substrings identifying authentication routes (empty = built-in list).
wordlist_pathsarray of string[]Well-known probe paths (empty = built-in wordlist).

[correlation]

Cross-IP scan→attack correlation ("shot calling" between nodes watching the same surface): an attack from IP B correlates with a scan from a different IP in the same /24 (IPv4), /64 (IPv6) or ASN observed within the window → ScanAttackCorrelation signal on B.

FieldTypeDefaultDescription
enabledbooltrueEnables the correlation tracker.
window_secsinteger900Maximum scan age for correlation (15 minutes).

[rate_limit]

Rate-limit backend for DSL rate conditions (RuleMatch::Rate).

FieldTypeDefaultDescription
backendstringmemorymemory (in-process sliding window, single-node) | redis (multi-node; requires the rate-redis build feature).
redis_urlstringredis://127.0.0.1/Redis URL (used only with backend = "redis").

[real_ip]

Real client IP resolution, trusted proxies and never-ban/never-allow sets.

FieldTypeDefaultDescription
trusted_proxiesarray of string[]CIDRs/IPs of peers allowed to set client-IP headers (CF-Connecting-IP, True-Client-IP, X-Real-IP, XFF). Headers from other peers are ignored (anti-spoof).
cloudflarebooltrueIncludes the bundled Cloudflare ranges (refreshed daily from cloudflare.com/ips).
trusted_ipsarray of string[]Clients never banned/blocked/reported (admins, uptime probes) — pipeline short-circuits to Allow.
trusted_listsarray of string[]Built-in presets approved by name (e.g. paypal, stripe, googlebot, bingbot) — join the never-ban set.
whitelistarray of string[]Allow everything, never ban — merged into the same never-ban set.
blacklistarray of string[]Deny immediately — the pipeline short-circuits to Block before any detector runs.
shadowarray of string[]Traffic fully processed, but the verdict is capped (Block/Quarantine downgrade to Challenge) — never banned.
refresh_secsinteger86400Cloudflare ranges refresh interval (0 disables; bundled constants stay in effect).

[ip_lookup]

On-demand external reputation: gray-band IPs (elevated local score without a verdict, or carrying a listed signal) are queried at the provider and the returned confidence re-enters the pipeline (it can only raise the verdict). Consumes API quota — off by default.

FieldTypeDefaultDescription
enabledboolfalseEnables the lookup fork.
providerstringabuseipdbReputation provider.
key_envstringSENTRY_ABUSEIPDB_KEYEnvironment variable holding the API key.
trigger_aboveinteger25Minimum local risk score (0–100) to trigger a lookup (verdict not yet Block).
on_signalsarray of string[]Additional trigger: signal kinds (snake_case) that fire a lookup. Empty = score band only.
cache_ttl_secsinteger86400Per-IP result cache TTL.
max_per_hourinteger500Quota guard: max lookups per rolling hour.
timeout_secsinteger10Per-request timeout.

[bot_verification]

Forward-confirmed rDNS bot verification: UAs claiming to be verifiable crawlers (Googlebot, bingbot, Slurp, Baiduspider, YandexBot) are validated via PTR on the engine's domains plus forward-resolution back to the IP. Verified bots bypass the edge challenge; spoofed claims raise the SpoofedBot signal. DNS runs off the hot path. Inspect with sentry bots check <ip> --ua "Googlebot/2.1".

FieldTypeDefaultDescription
enabledboolfalseEnables verification (requires DNS resolution).
cache_ttl_secsinteger3600Verified result TTL, per IP.
failed_ttl_secsinteger600Spoofed result TTL (short, so bots recover quickly from DNS outages).
timeout_msinteger2000Timeout for one PTR+A round.
batch_sizeinteger16Max claims verified per background drain cycle.

[ai]

Local classic anomaly model (ONNX) running as an async pipeline fork; the re-score can only raise. Requires the onnx build feature. Training: sentry model export [--synthetic] + tools/train_model.py.

FieldTypeDefaultDescription
enabledboolfalseEnables the model stage.
model_pathstring (path)models/anomaly_v1.onnxONNX model file.
thresholdfloat0.7Anomaly probability that emits the AnomalousPayload signal (0.0–1.0).
signal_weightinteger25Weight of the emitted signal (also overridable via [scorer.weights]).
modestringforkfork (async) | inline (blocks before actions) | shadow (log only).
triggerstringabove_scorealways | above_score | quarantine_only.
min_scoreinteger20Minimum hot-path score for trigger = "above_score".
concurrencyinteger4Max concurrent inferences (fork mode).
cache_ttl_secsinteger300Result cache TTL keyed by payload hash.

[llm]

Optional LLM for event re-scoring (key via SENTRY_LLM_KEY). Mirrors [ai] behavior: fork/shadow, cache keyed by payload hash, only raises the score.

FieldTypeDefaultDescription
providerstring(empty)openrouter | openai | ollama | jev | mock. Empty or unknown names disable the LLM stage with a warning.
modelstring(empty)Provider-specific model ID (e.g. meta-llama/llama-3.3-70b-instruct); empty uses the provider default.
base_urlstring(empty)Base URL override (self-hosted Ollama / OpenAI-compatible servers).
modestringforkfork (async re-score + action re-dispatch) | shadow (logs the decision, never acts).
only_aboveinteger30Only invoke the LLM above this score (or on quarantine).
concurrencyinteger4Max concurrent LLM requests.
cache_ttl_secsinteger300Verdict cache TTL keyed by payload hash.

[edge]

Inline edge (required with [deployment] mode = "inline"). Chain: client → sentry-edge → backends. The startup health check requires at least one healthy server per upstream group (a down secondary only warns — failover covers it); verdicts apply before proxying (Block→403, RateLimit→429, Challenge→challenge page). Blocked IPs are denied by a fast-path before the pipeline and blocks survive restarts.

Upstreams may be IPs or hostnames (http://web:3000 — Docker container names resolve through the compose DNS; Kubernetes service names the same). Requests are routed by Host header to per-site groups via [[edge.vhosts]].

FieldTypeDefaultDescription
listenstring0.0.0.0:80Public HTTP listen address. listen = "" disables the plain listener (HTTPS-only).
upstreamstring(empty)Default (catch-all) upstream base URL (e.g. http://127.0.0.1:8080).
upstreamsarray of string[]Default group with N servers — replaces upstream and load-balances across them.
balancestringround_robinGroup balancing strategy: round_robin | least_conn | ip_hash.
max_failsinteger2Connection-level failures inside the window before a server is ejected (0 = never).
fail_timeout_secsinteger30Failure-counting window AND ejection duration.
response_bufferingbooltrueBuffers upstream responses (enables compression). false streams them — text/event-stream always streams.
upstream_timeout_secsinteger300Total per-request upstream budget (connect + read + write).
health_pathstring/Path for the startup health check (per group).
health_timeout_secsinteger5Health check timeout.
body_capture_kbinteger0Request-body bytes captured for inspection, in KiB (0 = off).
challenge_backendstringsentryWho executes Challenge verdicts: sentry (built-in PoW) | cloudflare (becomes a CF rule via the API; requires an action with provider = "cloudflare").
tls_cert / tls_keystring (path)(empty)TLS certificate and key (edge-tls build feature; both enable TLS) — also the default chain for hostnames without a vhost cert.
tls_listenstring0.0.0.0:443HTTPS bind used together with tls_cert/tls_key; the plain listen stays up (both 80 and 443 monitored).
tls_redirect_httpsboolfalse301 HTTP → HTTPS after the pipeline (port-80 traffic keeps being monitored/blocked).
tls_allowed_hostsarray of string[]Hostnames accepted in the ClientHello SNI; empty disables the check. Missing/unknown SNI scores as a probe (TlsSniMismatch).
tls_handshake_eventsbooltrueEmits one TlsHandshake event per handshake (SNI + JA3 + JA4); mismatches are always emitted.
tcp_listen / tcp_upstreamstring(empty)Inline TCP listener for non-HTTP services (e.g. 0.0.0.0:2222 → 127.0.0.1:22); verdict on connect.
forwarded_headersbooltrueStrips client-supplied X-Forwarded-*/X-Real-IP and sets the edge's resolved view (XFF = client IP, proto, host). false restores pass-through.
http2booltrueNegotiates HTTP/2 on the TLS listener (ALPN h2 + http/1.1); false pins HTTP/1.1.
upstream_connect_timeout_secsinteger5Connect timeout for the edge → upstream hop.
upstream_pool_idleinteger32Idle keepalive connections pooled per upstream host.
max_concurrent_requestsinteger1024Max requests served concurrently (0 = unlimited); beyond the cap, immediate 503 (custom error pages apply).
reuseport_workersinteger1Accept loops sharing the port via SO_REUSEPORT (unix; Windows caps at 1).
tcp_backloginteger0Listen backlog (0 = kernel maximum / somaxconn).
tcp_nodelaybooltrueTCP_NODELAY on client-facing connections.
tcp_keepalive_secsinteger60Client-facing keepalive idle time (0 = off).
tcp_send_buffer_kb / tcp_recv_buffer_kbinteger0SO_SNDBUF/SO_RCVBUF in KiB (0 = system default).
[edge]
listen = "0.0.0.0:80"
upstream = "http://127.0.0.1:8080"

[edge.vhosts]
host = "app.example.com"
upstreams = ["http://web:3000"]

[[edge.vhosts]]

Nginx-style virtual hosts (F14): requests whose Host header matches route to the vhost's own upstream group with its own balancer and passive health checks. host matches case-insensitively (the :port suffix is ignored); *.domain matches any subdomain (multi-label) but not the bare domain — nginx wildcard semantics. Omitted fields inherit the [edge] globals. Each vhost may declare tls_cert/tls_key, served via SNI on the shared HTTPS listener (the global cert stays as the default chain; requires edge-tls).

FieldTypeDefaultDescription
hoststring(required)Host header to match, e.g. app.example.com or *.api.example.com.
upstreamsarray of string(required)Backend URLs for this site (IP or hostname).
balancestring[edge] balanceround_robin | least_conn | ip_hash.
max_failsinteger[edge] max_failsPassive health check failures before ejection.
fail_timeout_secsinteger[edge] fail_timeout_secsFailure window / ejection duration.
health_pathstring[edge] health_pathStartup health-check path override.
tls_cert / tls_keystring (path)(empty)Per-vhost certificate served via SNI (both together; requires the global TLS front).

Failover: a connection-level failure (connect error/timeout) retries the next healthy server in the group (nginx proxy_next_upstream error timeout; 5xx responses are not retried) — sentry_edge_upstream_failovers_total{vhost} counts the retries, and a group with every server down answers 502. https:// upstreams always verify certificates; for self-signed backends use plain http:// on the private network.

[edge.compress]

Edge response compression: negotiated against Accept-Encoding (zstd > brotli > gzip) over already-buffered bodies. Never compressed: responses with content-encoding, 206, no-transform, bodies below min_length and content types outside the allowlist. Turns itself off under overload pressure. zstd requires the edge-zstd build feature.

FieldTypeDefaultDescription
enabledbooltrueEnables compression.
min_lengthinteger256Smaller bodies pass through untouched.
level_brinteger4Brotli quality 0..=11.
level_gzipinteger6gzip level 0..=9.
level_zstdinteger3zstd level 1..=22 (only with edge-zstd).

[edge.challenge]

Interactive SHA-256 proof-of-work challenge for Challenge verdicts (used with challenge_backend = "sentry"). Stateless: share the secret across edge nodes. PoW never unlocks a Block; rDNS-verified crawlers bypass it.

FieldTypeDefaultDescription
enabledboolfalseEnables the interactive challenge.
secret_envstringSENTRY_EDGE_CHALLENGE_SECRETEnv holding the shared secret (32+ random bytes recommended); required when enabled — startup fails without it.
bucket_secsinteger3600Bucket length — the challenge rotates and cookies expire with it.
difficultyinteger16PoW leading zero bits (clamped 8..=28).
titlestringVerifying your browser...Interstitial page title.
template_pathstring (path)(empty)HTML override for the interstitial — re-read on every render (edits go live without a restart); must keep the required {{…}} markers.

[edge.error_pages]

Custom HTML error pages for every response generated by the edge.

FieldTypeDefaultDescription
dirstring (path)(empty)Directory with <status>.html files (e.g. 403.html, 413.html, 429.html, 502.html) plus an optional default.html. Placeholders {{SENTRY_STATUS}}, {{SENTRY_TITLE}}, {{SENTRY_MESSAGE}}, {{SENTRY_TRACE_ID}}, {{SENTRY_ICON}}; substituted values are HTML-escaped. Read once at startup (files > 256 KiB are skipped with a warning).

[uploads]

Body and upload inspection (inline only): bodies are buffered, multipart/urlencoded/JSON parsed, and the upload heuristics score what they find (SQLi/XSS in filenames and form fields, polyglot images, executables in disguise, floods). The memory ceiling is ~concurrent requests × inspect_kb.

FieldTypeDefaultDescription
enabledboolfalseEnables upload inspection (requires inline mode).
modestringshadowshadow (detect + log, weight-0 signals) | enforce (full weights, blocks before the upstream).
inspect_kbinteger4096Per-request inspection cap in KiB.
max_filesinteger16Max multipart parts parsed per request.
scan_jsonbooltrueText-scan JSON bodies too (SQLi/XSS inside API payloads).
blocked_extensionsarray of stringphp, phtml, php5, jsp, jspx, asp, aspx, exe, dll, sh, bat, ps1Extensions that always raise UploadExecutable (including double extensions shell.php.jpg).
allowed_extensionsarray of string[]Allowlist: when non-empty, an extension (or double extension) outside the list raises UploadDisallowed; extensionless files are unaffected; blocked_extensions wins.
oversizestringrejectreject (413) | skip (forward uninspected) | flag (forward + UploadOversize signal, counts toward flood). Applies to bodies larger than inspect_kb.

[uploads.flood]

Per-IP upload flood volume thresholds.

FieldTypeDefaultDescription
window_secsinteger60Per-IP sliding window duration.
max_uploadsinteger30Files per IP in the window that trigger UploadFlood (0 = off).
max_total_mbinteger50Total uploaded MiB per IP in the window that trigger UploadFlood.

[posture]

Web security posture advisories (inline only): upstream responses are checked for missing/weak security headers (CSP, HSTS, COOP, frame protection, Trusted Types, nosniff, referrer policy). Findings become weight-0 signals — they never change a score or verdict, and [scorer.weights] cannot override that.

FieldTypeDefaultDescription
enabledbooltrueEnables advisories (zero enforcement impact).
modestringshadowshadow (report only) | enforce (reserved for header injection — roadmap; rejected at load).
dedupe_ttl_secsinteger3600Dedupe period per (host, check) before re-reporting.
hsts_min_max_ageinteger31536000HSTS max-age below which the header counts as weak (1 year).
checksarray of string[] (all)Enabled check ids: csp, hsts, coop, clickjacking, trusted_types, nosniff, referrer_policy. An explicit list replaces the whole set.
hostsarray of string[]Restrict advisories to these Hosts (empty = any; capped at 64 hosts).

[protocol]

Protocol validation: compiles YAML schemas (*.protocol.yaml) and validates frames on non-standard ports in edge-tcp. Live hot-reload of the directory; a failed compile keeps the previous set.

FieldTypeDefaultDescription
enabledboolfalseEnables the protocol validator.
dirstring (path)schemasWatched directory (live add/modify/remove).
safety_poll_secsinteger60Safety rescan interval that catches lost fs events.
debounce_msinteger500Debounce window coalescing fs events before a rescan.
max_schemasinteger64Max compiled schemas accepted from the directory.

[metrics]

Prometheus metrics: serves /metrics and /api/events (recent events, for the Grafana dashboard) in the daemon process. Unauthenticated — keep the port internal.

FieldTypeDefaultDescription
enabledbooltrueEnables the /metrics endpoint.
hoststring0.0.0.0Metrics server bind address.
portinteger9100Metrics server port.

[server]

Web dashboard and JSON API (sentry serve) — separate process from the daemon; requires Postgres.

FieldTypeDefaultDescription
hoststring127.0.0.1Server bind address. With auth disabled only loopback is safe.
portinteger8080Server port.
authtable—The [server.auth] sub-table below.
webhook_secret_envstringSENTRY_WEBHOOK_SECRETEnv holding the shared secret: requests with a matching X-Sentry-Webhook-Secret header may ack/resolve incidents.

[server.auth]

Dashboard/API auth + RBAC.

FieldTypeDefaultDescription
modestringnonenone | password | token | both. Roles: admin (mutations) and viewer (read-only).
usersarray[]Dashboard users (password mode), each with username, password_hash (Argon2id via sentry auth hash-password), role (default viewer).
tokensarray[]Static API tokens (token mode), each with token_sha256 or token_env + role (default viewer; hash via sentry auth token-hash).
session_secret_envstringSENTRY_SESSION_SECRETEnv holding the HMAC signing key for session cookies.
session_ttl_secsinteger43200Session cookie lifetime (12h).

[[action]]

Response actions. Each entry has a type, an optional provider, an optional dispatch and a plugin-specific options map.

FieldTypeDefaultDescription
typestringlogType-safe discriminator: cloudflare (alias for challenge + CF) | challenge | webhook | blocklist | report | log. Typos fail at load time.
providerstring(empty)Only for type = "challenge": cloudflare | firewall | nginx | opnsense | pfsense | … (extensible via the ChallengeProvider trait).
dispatchstring(natural default)inline (local containment, on the hot loop) | deferred (network I/O on workers). Defaults: blocklist/log/firewall = inline; cloudflare/nginx/opnsense/pfsense/webhook/report = deferred.
optionstable{}Plugin-specific options (below).

type = "blocklist" — in-memory blocklist feeding the shared block table; the same blocks are mirrored to ip_state (dashboard-visible, restart-safe).

OptionDefaultDescription
ttl_secs86400TTL of pipeline blocks end to end.

type = "challenge", provider = "cloudflare" — applies verdicts as Cloudflare rules (env: SENTRY_CF_TOKEN, SENTRY_CF_ZONE, optional SENTRY_CF_ACCOUNT).

OptionDefaultDescription
modemanaged_challengeblock | js_challenge | managed_challenge | rate_limit.
ttl_secs86400Access rule TTL (commented as sentry:<ts>:<ttl> for the reaper).
max_failures3Circuit breaker: disables the provider after N consecutive failed inserts (until restart).
ipv6_prefix(empty)1..=128 — blocks IPv6 by /64 via an account-level IP List (ip.src in $<list_name>); absent/128 keeps exact-address rules.
list_namesentry_blocksIP List name (^[a-z0-9_]+$, max 50 chars).

provider = "firewall" — kernel bans (Linux only; auto-detect nftables → ipset+iptables → firewalld; needs root or CAP_NET_ADMIN).

OptionDefaultDescription
backendautoauto | nftables | ipset | firewalld.
ttl_secs86400Block/Challenge ban duration.
rate_limit_ttl_secs600Shorter TTL for RateLimit verdicts.
tablesentrynftables table name.
set_prefixsentry_blocksSet name prefix (sentry_blocks_v4 / sentry_blocks_v6).

provider = "nginx" — generates deny/challenge includes on a co-located nginx and reloads it (works with Sentry in passive mode).

OptionDefaultDescription
conf_dir— (required)Directory for the generated includes (sentry-deny.conf, sentry-challenge.conf, …).
reload_cmdnginx -s reloadReload command (no shell).
validatetrueRuns nginx -t before every reload.
ttl_secs86400Deny TTL (# sentry:<ts>:<ttl> stamps).
rate_limit_denyfalseAlso deny RateLimit verdicts.
provision_botstrueEmits the bot_verifier snippet (opt-in module).
geo_var$sentry_challenge_ipGeo variable of the challenge map.

provider = "opnsense" / provider = "pfsense" — drops at the firewall appliance (drop-only platform: Challenge/RateLimit are logged as unenforced).

OptionDefaultDescription
base_url— (OPNsense, required)OPNsense REST API base URL.
api_key_env / api_secret_envSENTRY_OPN_API_KEY / SENTRY_OPN_API_SECRETEnvs holding the credentials (OPNsense).
tablesentry_blocksBan alias/table.
pfctl_pathpfctlpfctl binary (pfSense backend).
accept_invalid_certstrueAccepts the appliance's self-signed certificate.
ipv6_prefix(empty)IPv6 entries as CIDR (otherwise exact address).

type = "report" (provider = "abuseipdb" | "reportedip") — reports IPs earning an enforcing verdict to community abuse databases.

OptionDefaultDescription
key_envprovider default (e.g. SENTRY_ABUSEIPDB_KEY)Env holding the API key; a missing key skips the action with a warning.
min_verdictblockMinimum reported verdict: block | challenge | rate_limit.
dedupe_hours24One report per IP per window.
timeout_secs10Per-request timeout.

type = "webhook" — JSON alert POST (Discord automatically gets a rich embed).

OptionDefaultDescription
url / url_env— (one of the two is required)Webhook URL; prefer url_env (the URL is a credential). An empty url_env skips the action with a warning.
on_levels["high", "critical"]Risk levels that trigger the POST.
timeout_secs10POST timeout.
secret_envSENTRY_WEBHOOK_SECRETWhen the env exists, requests are signed with X-Sentry-Signature: sha256=<hmac> and the payload carries incident_id/ack_url for callbacks.

type = "log" — built-in; emits a tracing line on act.

[[action]]
type = "challenge"
provider = "cloudflare"
[action.options]
mode = "managed_challenge"
ttl_secs = 86400
max_failures = 3
ipv6_prefix = 64

Validation and secrets

sentry config validate   # validates schema, DSL rules and protocol schemas
sentry config show       # prints the resolved config (overlay applied)

sentry config validate compiles custom rules (the DSL is parsed at load), compiles protocol schemas when [protocol] enabled, rejects [posture] mode = "enforce" (still roadmap), warns about unknown ids in [posture] checks, and prints the edge topology/performance advisories (double TLS, off-loopback http upstream, inert compression outside inline).

Security rules:

  • Secrets (Cloudflare/LLM/DB tokens, API keys, session secrets) never in committed config — via env (SENTRY_*).
  • sentry.example.toml has placeholders, not real values.
  • The sensitive_paths pack blocks .env, .git/, .ssh/, etc. by default — when exposing an allowlisted route, justify it in the PR.

On this page