Skip to content

Defendr protocol

Defendr is the proprietary binary wire protocol that the EnforceGate engine and its connectors use to exchange URL queries and verdicts. It runs over TCP (with TLS upgrade) and is what makes the engine extensible to proxies beyond Squid — future HAProxy and TLS-proxy connectors will speak the same protocol.

This page is a high-level description of the protocol surface operators encounter through logs and CLI output. The exact on-the-wire format is engine-internal and subject to change between releases (the engine and connector negotiate a compatible version at session establishment).

Verdict codes

Every URL the proxy intercepts is sent through the engine, which returns exactly one of four numeric verdict codes. The same codes appear in the engine's logs, the connector's response to Squid, and the captive portal's redirect path.

Code Action Visitor experience
200 Permit Request proceeds unchanged. Squid forwards to the origin.
300 Deny Squid responds with HTTP 301 to the captive portal /blocked page. Terminal — visitor cannot proceed.
310 Warn HTTP 301 to the captive portal /warned page. Page renders with a "Proceed anyway" CTA; clicking it sends an ack_token to the engine and the next request from the same (rule_name, client_ip) pair is permitted for the ack_session_duration_s window.
320 AUP HTTP 301 to /aup (Acceptable Use Policy). Same flow as warn but a distinct route lets the portal render different copy (e.g. a longer policy text and a checkbox the visitor must tick before proceeding).

Policy actions → verdict codes

The action field of a .policy rule determines which verdict the engine returns when the rule matches:

.policy action Verdict code
permit or accept 200
deny or block 300
warn 310
aup 320

An unrecognised action value is treated as deny (the engine logs a [debug] line so misconfigurations are visible) — typos should never accidentally permit traffic.

When a verdict cannot be returned

If the engine is unreachable, the connector emits a Squid ERR response within roughly 600 ms so Squid doesn't hang. Squid's behaviour on ERR (permit-by-default vs deny) is a Squid-side configuration; the shipped bundle defaults to deny so a missing engine cannot accidentally open the proxy.

If the regex matcher fails to compile the loaded policy set, the engine falls back to permit-by-default with a [critical] log line — and no rules fire until the offending pattern is fixed. See troubleshooting.

The redirect URL the engine builds

For verdicts 300, 310 and 320 the engine builds a pre-signed redirect URL of the form:

https://<captive-portal-host>/blocked?p=<encrypted-payload>&v=1&ts=<unix>
                            /warned
                            /aup

The p value is an encrypted blob carrying the original destination URL, the matched rule_name, the rule's description (shown to the visitor), engine timestamp, connector identity, client IP, and — for warn/aup — an ack_token. The portal verifies and decrypts it using the shared secret from [captive_portal].secret and renders the page. See captive portal for the operator-facing reference.

The rule_name operators see

The matched rule's bareword key from the .policy file (e.g. deny-urlshortening-domains) is surfaced in three places: the captive portal verdict page, the engine logs at [debug] level, and the show policy match <url> diagnostic output. A global rule ID suffix _e<N> is appended on the wire — e.g. deny-urlshortening-domains_e14.

Because the suffix is the global ID across all loaded policy files (assigned in lexicographic load order of rules.d/), it changes when policy load order changes. Operators reading a deny page's rule_name should not assume _e14 always points at entry 14 of any one .policy file — use show policy match <url> to identify the rule, not the ID.

Transport

The engine listens on a TCP port (default 11224) configured in the [connectors.<name>] section of engine.conf. Connectors establish a session, mutually authenticate with a shared pre-shared key, optionally upgrade to TLS, and then exchange request and verdict messages until the session is torn down.

In the standalone bundle the engine and connector run inside the same container and communicate over loopback. Multi-host deployments use the same protocol over a routed TCP path; TLS is mandatory in that posture.

See engine and Squid connector for the operator-tunable connection parameters on each side.