Skip to main content
Heretic provides the ruling and the evidence; what you do with it is entirely up to you. Heretic never blocks traffic on your behalf — it hands your application a structured verdict and a list of signals, and your code decides whether to block, challenge, flag for review, or pass the request through. This separation keeps enforcement logic in your control and makes it auditable, version-controlled, and easy to tune over time.

Decision Framework

The table below maps each verdict and conclusive value to a recommended enforcement action. Treat this as a starting point; adjust thresholds to match your product’s risk tolerance.

Middleware Example

The pattern below shows a minimal Express.js middleware that reads the session ID from a request header, fetches the verdict, and blocks on a conclusive contradiction. All other sessions continue through the middleware chain with the verdict attached to the request for downstream handlers.
Keep the fetchVerdict call non-blocking for error cases (if (!res.ok) return null). If the Heretic endpoint is temporarily unreachable, fail open rather than blocking legitimate traffic.

Keying Policy on Signal IDs

Signal IDs like geo.rtt-below-vacuum and stack.os-contradiction are stable across releases. That means you can hard-code them in your policy rules and rely on them not changing between Heretic versions. Use this to build granular rules rather than acting only on the top-level verdict. For example:
Signal IDs are dot-namespaced by family (geo.*, stack.*, render.*, etc.), so a startsWith filter gives you family-level granularity without enumerating every individual ID.
contradicting_families tells you at a glance which families fired. Use it in your UI layer — for example, if contradicting_families includes network-geometry, surface a regional restriction notice rather than a generic “access denied” message. Specific messaging reduces support tickets.

What Not to Do

Do not block on insufficient alone. Some legitimate visitors — those behind strict corporate proxies, certain mobile carriers, or privacy-hardened browsers — naturally produce fewer measurable signal families. An insufficient verdict means Heretic couldn’t gather enough data to rule, not that the visitor is malicious. Apply only low-friction challenges (if anything) to these sessions.
Do not call the explain endpoint more than once per session ID. Session IDs are single-use from a rate-limit perspective. Cache the verdict in your session store and reuse it for the lifetime of the user’s session rather than re-fetching on every request.