Rule engine
Every detection in hoaxeye is a rule. A rule defines three things: the mode it runs in, the verdict strings it can emit, and the action the operator maps onto each verdict. We produce verdicts. The operator decides what to do with them.
Modes
New rules ship with mode disabled. The operator promotes them deliberately. That ordering matters — a rule that defaults to enforce is a rule that can kick a real player on day one without anyone reviewing it first.
| Mode | Behavior |
|---|---|
observe | The rule runs and writes verdicts to the audit log. Nothing player-facing happens. Use this to validate tuning before going live. |
score | Verdicts contribute to the player's aggregate risk bucket. Action is taken when the bucket crosses an operator-defined threshold combined with other signals — never from this single rule alone. |
enforce | Verdicts trigger the action you mapped to them (deny / queue / kick). One verdict, one action. No hidden multipliers. |
A fourth state, disabled, exists per rule per server. A disabled rule still ships and updates with the rest of the engine, but emits no verdicts and consumes no connect-path budget.
Verdicts
What a verdict is:
- A short, stable string (≤ 30 characters) that names a finding — examples are
vpn,datacenter,residential_vpn,cleared. - Persisted in the dashboard's flag table so you can search and filter on it.
- Returned in the API response so your tooling can match on it.
What a verdict is not:
- A numeric score. We don't expose internal scores; see risk buckets below.
- A description of which internal signal fired. The verdict tells you what we concluded, not how. That separation keeps tuning details out of reach of bad actors.
Risk buckets
Where the API needs to convey strength of a finding, it returns a bucket — low, medium, high — not a number.
Why no numbers: a public threshold becomes a tuning target. If we said "score ≥ 80 triggers", a bad actor would tune their evasion to land at 79. Buckets give operators enough resolution to act, while keeping the internal scoring schedule a moving target.
Fail modes
The on-server resource talks to our API on every join. Sometimes the API can't answer.
| API response | Resource behavior | Why |
|---|---|---|
4xx | Fail-closed — deny the join. | A 4xx means we know the request is wrong (revoked key, malformed payload). A misconfigured server should fail loud, not silently let everyone through. |
5xx / timeout | Fail-open — allow the join, write an offline-mode log entry. | A 5xx means we don't have a verdict. A verdict-less server shouldn't lock out its players because of our outage. The next successful request resumes normal operation. |
See the API reference for the full status-code table.
Detection families
Rules are grouped into families. A family shares an internal module name and a public verdict namespace, and can be configured as a unit in the dashboard.
- Network — VPN/proxy/datacenter/Tor classification at connect time. Anti-VPN page.
- Resource — scheduled audits of the resources running on your server, plus the always-on resource hygiene & risk score. Backdoor scanner, Server analytics.
- Connect-gate — Discord membership verification at join. Discord verify.
- Identity — alt-account correlation across identifiers and behavioral signals; surfaces as boost-only verdicts on the families above, no standalone enforcement.
- Roadmap — client-UI / server-event / entity abuse families are designed and threat-modelled. They land here when the first one ships, not earlier.
Per-server configuration
Mode and action mapping are per-server. The same rule can be in observe on your dev server and enforce on your production server. Operators with team access need an explicit permission to change rule modes; the default team key cannot.
Tuning updates (threshold adjustments, whitelist additions) are pushed server-side. You don't have to ship a new on-server resource version to receive them. Structural updates (new families, new verdict strings) are announced ahead of time and ship with an explicit resource version — you upgrade when you're ready. The on-server resource itself stays well below the FiveM tick budget; Server setup has the bar chart.
Audit log
Every mode change, action mapping, and emergency-bypass entry lands in the audit log with the operator's user ID, timestamp, and a short reason. Open it from the dashboard. The audit log is append-only — entries cannot be deleted by operators, only by us, and only on a written GDPR Art. 17 request.
What the engine deliberately does not do
- No automatic threshold updates by ML in the live path. Behavioral signals are advisory; they flow into the engine as additional tokens, not as overrides of operator settings. See False positives for how that boundary is enforced.
- No per-user backdoor switches. If a verdict needs to be ignored for a specific player, the right place is your operator allowlist — auditable, scoped, and revocable. Not a hidden override.
- No silent rule changes. Tuning that affects more than threshold drift is announced before it ships.