Skip to content
§3

Configuration

Where every setting lives — the three config files, the convars that matter, and the rule that decides which value wins.

On this page10 entries

There are three places to configure Sculk, and one rule that explains all of the confusion people have with them:

  1. server.cfg — convars. Engine hardening, the licence key, webhooks, player-facing messages. The resource reads these at boot.
  2. config.lua — the resource's own defaults: timing, enforcement posture, zones, the entity firewall, the panel, the REST API.
  3. config_detections.lua — the catalogue defaults for every detection: which are on, what action each takes, and which side observes it.

The dashboard wins. Anything settable on the anticheat dashboard — detection enablement and action, messages, zones, the entity firewall — is pushed to every activated server on its heartbeat and overrides the file. (Webhooks are the exception; see below.) Edit the file only for values the dashboard does not expose, or to set what a fresh server starts with.

§3.1Connection

| Where | Setting | What it does | |---|---|---| | config.lua | Config.ApiBase (sculk_api_url) | The Sculk API the resource activates and heartbeats against. Leave it. | | config.lua | Config.IpLookupBase (sculk_iplookup_base) | IP reputation provider. Defaults to HTTPS. ip-api.com's free tier only serves HTTP, so VPN/proxy/country signals stay quiet unless you point this at a paid HTTPS endpoint or explicitly opt into an http:// one. | | config.lua | Config.HeartbeatInterval | Seconds between heartbeats (default 30). This is the latency floor for anything the website changes. | | config.lua | Config.CommandPollInterval | Seconds between command polls (default 2). Leave it — the API rate-limits per server and a 1s poll leaves too little headroom. |

§3.2Enforcement posture

| Where | Setting | What it does | |---|---|---| | server.cfg | sculk_observe | Observation mode. "true" (on in the shipped server.cfg.example) holds every automatic kick and ban — detections, the handshake policy, advanced triggers — and reports it as OBSERVE would kick / would ban plus a review event. Staff actions are never held. Read on every call, so set sculk_observe false in the live console enforces without a restart. | | config.lua | Config.HandshakeGrace | Seconds a joining client has to complete the anticheat handshake before the policy below applies. | | config.lua | Config.HandshakePolicy | log (default), kick or ban. What to do about a client whose anticheat half never loaded — which is what a stripped client looks like. | | config.lua | Config.EnforceConfidenceCaps | Keeps each detection at or below the action its evidence can support. Leave on. | | config.lua | Config.TrustClientReports | false by default. When off, a client-authored report is capped at Warn no matter what the detection asks for. | | config.lua | Config.TrustThreshold, Config.TrustRecoveryPerTenMinutes, Config.TrustForgetSeconds, Config.StrikeDecaySeconds | The trust threshold, how fast trust recovers, and how long trust and strikes are remembered. What each detection class costs is fixed in the resource, not configurable. |

§3.3Probation (3.9.0)

3.9.0 gave a number of detections the power to kick or ban for the first time: checks that could never fire before, checks that were rewritten, and a few that moved up from Warn. None of them has been seen on a live server yet, so they ship on probation. They detect, log and reach the dashboard exactly as normal, but they act at Warn, and the event says what they would have done:

… [probation: would KICK — new in 3.9.0, not yet seen live; release it with Config.Probation.antiVehicle = false]

| Where | Setting | What it does | |---|---|---| | config.lua | Config.ProbationEnabled | true by default. false releases every detection on probation at once. | | config.lua | Config.Probation.<name> | true puts the whole detection on probation. 'arms' puts only its new arm on probation; its older arms act as configured. false releases it. |

On probation as a whole: antiHeartbeat, antiProjectileAI, antiVehicle (the spawn refusal is skipped too), antiGodmode, antiWeaponSpoofer, antiInfinitePunch, antiWeapon, antiAddWeapon, antiRemoveWeapon and antiVPN (it lets VPN users in and logs who it would have refused). Only the new arm: antiSpeedChanger (positional noclip and fast-walk), antiDamageChanger (the override-damage ceiling), antiInjector (cross-client consensus) and antiHoneypot (the built-in decoys; traps you added yourself still ban).

Probation is set in config.lua only. Raising a detection's action on the dashboard does not release it. The dashboard marks these rows Probation.

How to release one:

  1. Leave it on probation for about a week of normal play.
  2. Search your console or the dashboard event log for probation: would and read each hit. Would you have kicked or banned that player?
  3. If every hit was a cheater, set Config.Probation.<name> = false in config.lua and restart the resource. If any hit was an honest player, leave it on probation and send us the event in a ticket.

§3.4Detections

Every detection is one row in config_detections.lua:

antiTeleport = { category = 'Movement', action = 'KICK', enabled = true, side = 'server' },
  • action — LOG, WARN, KICK or BAN. The pipeline may cap this down if the detection cannot observe enough to justify it; it never raises it.
  • enabled — the shipped default. The dashboard overrides it at runtime.
  • side — server, client or both. Client-sourced reports are capped harder.

The dashboard can also set a per-detection threshold (strikes before acting), window (seconds the strikes must fall within), ban duration, a custom message, and an escalation (escalateTo after escalateAfter strikes). config_detections.lua has no field for these; they exist only as dashboard overrides applied on top of it.

Tune before you enforce: run in observation mode (sculk_observe) first, and set anything you want silenced permanently to Log. The Event Policy Worksheet is how you decide.

§3.5Zones and whitelisting

| Where | Setting | What it does | |---|---|---| | config.lua | Config.WhitelistZones | Radius zones that suppress named detections for players inside them — a mechanic shop where antiNoclip is meaningless, a spawn area during a cutscene. Config zones and dashboard zones go through the same validation. | | config.lua | Config.MaxZoneRadius, Config.MaxZones | Caps on what a zone may claim, so a typo cannot whitelist the map. |

A zone whitelists specific detections, never "everything", and a small set of detections can never be whitelisted at all — see Commands & Permissions.

§3.6Entity firewall, triggers and Sculk Mind

  • Config.EntityFirewall is an ordered, first-match list of rules. Each rule matches entity type, population type and model and says Allow or Deny; DefaultAction (Allow by default) covers anything no rule matches. It ships with no rules, and the dashboard can replace the whole ruleset.
  • Config.AdvancedTriggers holds owner-authored IF/THEN rules (conditions on player or detection fields → log, warn, kick, ban or webhook), defined in config.lua or added with the AddAdvancedTrigger export. The dashboard's triggers page drives a separate engine — connection and log triggers, delivered with the settings on each heartbeat.
  • Sculk Mind is the learning layer. It only ever makes enforcement more cautious: an unknown pattern is held for review rather than banned, and a pattern staff keep clearing is suppressed. It cannot upgrade an action.

The Staff Review Workflow guide covers reviewing what Mind holds.

§3.7In-game admin panel

Config.Panel controls /sculkpanel:

| Key | Meaning | |---|---| | Enabled | Whether the command exists at all. | | Command, Alias | The chat command and its alias (default sculkpanel / sculkmenu). | | Aces | Which ACEs may open it. Default { 'sculk.admin' }. | | RefreshMs | How often the open panel refreshes (default 2500). | | MaxActionsPerMinute | Per-admin action budget. | | AllowDetectionToggle | Whether the panel may switch detections on/off for the session. |

Full walkthrough: In-Game Admin Panel.

§3.8Webhooks and player-facing messages

Both settable as convars or on the dashboard, but they behave differently. For the ban and kick messages the dashboard wins over the convar. Webhooks do not override each other: the resource posts to the sculk_webhook_* convars itself, and the website posts to the dashboard's webhooks separately. Set one or the other, not both, or every warn, kick and ban posts twice. The convars are the ones that still post while the website is unreachable.

The webhook convars are sculk_webhook_warn, sculk_webhook_kick, sculk_webhook_ban, sculk_webhook_connect and sculk_webhook_disconnect, and only discord.com / discordapp.com / ptb.discord.com webhook URLs are accepted.

set sculk_webhook_ban "https://discord.com/api/webhooks/..."
set sculk_msg_ban    "You have been banned by Sculk AntiCheat."

A webhook URL is a credential: anyone holding it can post into your channel. Keep it out of any repo.

§3.9Capture, REST API and debug

| Where | Setting | What it does | |---|---|---| | config.lua | Config.CaptureOnAction | { warn = false, kick = false, ban = false }, all off by default. Turn one on and Sculk takes a game-view capture when it warns, kicks or bans a player, tags it with that case's ID, and shows it on the dashboard's Warnings, Kicks and Ban log pages. A kick or ban waits at most three seconds for the picture. The dashboard's "Capture when Sculk acts" switches override these once saved. Needs a capture provider installed. | | config.lua | Config.CaptureOnDetection | Legacy, off by default. Requested a capture after the player had already been removed, so it rarely produced one. Prefer Config.CaptureOnAction. | | server.cfg | sculk_screenshot_resource | Force a capture provider by folder name if auto-detection picks the wrong one. | | config.lua | Config.RestApiEnabled, sculk_rest_api_key | An authenticated REST API served on the server's own HTTP endpoint (the game port, plain HTTP) — not a separate localhost listener, so put TLS in front of it or keep callers off the public internet. Off by default. The key must be 24+ characters or the API refuses to start, and enabling it grants ban authority to every resource on your server. | | server.cfg | sculk_debug | Verbose logging while tuning. Noisy — off in production. |

§3.10Weapon and voice tables

antiFastShoot, antiRapidFire and antiMagicBullet compare against vanilla weapon tables. If your server ships custom weapon meta, set Config.CustomWeaponMeta = true in config.lua, which quiets all three, or disable the two fire-rate tables individually or they will false-positive:

set sculk_weapon_fastshoot_off "true"
set sculk_weapon_rapidfire_off "true"

antiGlobalVoice compares talker proximity against Config.VoiceProximityMax (default 25 m, which already clears pma-voice's shout mode). Raise it in config.lua only if a proximity-voice script on your server legitimately sets it higher.