Skip to content
Dashboard, webhooks & logs

Dashboard, webhooks & logs

Web Dashboard

The admin web dashboard provides remote management of claims and players through a browser interface:

web-dashboard:
  enabled: false
  port: 8095
  # Public URL appended with ?token=<...> in the in-game login link.
  # Change this when the dashboard is behind a reverse proxy or a custom domain.
  url: "http://localhost:8095"
  # Token lifetime in hours. 0 = tokens never expire.
  token-ttl-hours: 24
  # Allowed CORS origins. Fallback: localhost + 127.0.0.1 on the configured port.
  # "*" means "any origin": insecure, only for local trusted setups.
  cors-origins:
    - "http://localhost:8095"
    - "http://127.0.0.1:8095"

Key reference

KeyDescription
enabledStarts the embedded HTTP server on plugin load.
portTCP port the dashboard listens on.
urlPublic URL sent in the in-game /scs dashboard click-to-open link. The plain auth token is appended as a ?token=... query parameter so the player lands signed in. Change this when the dashboard is behind a reverse proxy or a custom domain.
token-ttl-hoursHow long an issued admin token stays valid, in hours. 0 disables expiration so a token only stops working when manually rotated.
cors-originsWhitelist of Origin header values the dashboard will send CORS-allow headers for. Incoming requests whose Origin doesn't match are silently denied at the browser level.

Run /scs dashboard in-game. The plugin sends a clickable [Click here] link in chat that opens the dashboard URL (from web-dashboard.url above) with the auth token already baked into the query string, so you land directly inside the dashboard. The token is also persisted in the browser's localStorage so a manual refresh keeps you signed in.

Tokens are stored hashed in web-tokens.json and persist across restarts.

Built-in abuse protection

  • Rate limiting — the /auth endpoint accepts at most 5 attempts per IP per minute. Further requests return HTTP 429 with a Retry-After header, making brute-force token guessing impractical.
  • Path traversal — static file requests are canonicalised; .. / ~ are rejected.
  • Prepared statements — all dashboard DB access goes through the same DAO layer, no raw SQL concatenation.

The dashboard uses plain HTTP (no built-in TLS). Tokens travel in clear text in the Authorization header, so never expose the port to the internet. Keep it bound to localhost (default) or put it behind a reverse proxy that terminates TLS.

On filesystems with multi-user access, consider restricting the web-tokens.json file (e.g., chmod 640). The tokens grant admin access to the dashboard.

Discord Webhook

SCS2 can post claim events (claims, unclaims, sales, purchases, ban/kick actions, /requnclaim submissions, etc.) to Discord. You can define multiple named webhooks and choose which event goes to which webhook.

discord-webhook:
  enabled: false
  url: ""                 # legacy single webhook — used as "default" if "webhooks" is empty

  webhooks:               # define one or more named webhooks
    default:
      url: "https://discord.com/api/webhooks/..."
      username: ""        # optional name override
      avatar-url: ""      # optional avatar override
  #  staff:
  #    url: "https://discord.com/api/webhooks/..."

  default-routes:         # events with no explicit route below go here
    - default

  routing:                # event -> webhook(s), overrides default-routes
  #  claim-deleted:
  #    - default
  #    - staff
  #  unclaim-request:
  #    - staff

Set enabled: true and configure at least one webhook URL. See Integrations → Discord Webhooks for the full list of routable event names. Leaving webhooks empty falls back to the single url (which then receives every event), so older configs keep working unchanged. Run /scs reload to apply changes.

Per-event toggles (Added in 2.6.6)

The optional events block turns individual events on or off at the source. An event set to false is never sent to any webhook — this is independent of routing, which only chooses which webhook receives an event that is being sent. Every event you omit defaults to true, so existing configs keep posting everything.

discord-webhook:
  events:
    chunk-added: false        # stop the per-chunk spam...
    chunk-removed: false      # ...while keeping everything else
    member-added: true
    member-role-changed: true
    # any event not listed here defaults to true

The example above silences the noisy chunk-added / chunk-removed events while still posting member and role changes. See Integrations → Discord Webhooks for the full list of event names.

Audit Logs

Track every action performed on claims for accountability and moderation:

audit-logs:
  enabled: false
  retention-days: 30
  # Buffer flush cadence (seconds). Lower = less data loss on a JVM crash, higher = fewer DB
  # roundtrips. The buffer is also flushed when batch-size entries accumulate, on /scs reload,
  # and on plugin disable.
  flush-interval-seconds: 5
  # Max entries kept in the in-memory buffer before forcing a flush.
  batch-size: 20

When enabled, all claim actions (permission changes, flag changes, member management, etc.) are logged with timestamps and player information. Logs older than retention-days are automatically purged.

Buffering

Writes do not hit the database one row at a time. Entries are buffered in memory and flushed in two cases: every flush-interval-seconds on a timer, or as soon as batch-size rows accumulate — whichever comes first. The buffer is also drained synchronously on /scs reload and on shutdown, so the only realistic data-loss window is a hard JVM crash within the last flush-interval-seconds seconds.

Exporting logs to CSV

/scs exportAuditLogs <claimId|*>

Dumps the audit log of a specific claim — or all claims with * — into a CSV file under plugins/SimpleClaimSystem/audit-exports/. Useful for external review, compliance, or just keeping a copy before retention-days drops old rows.

Plugin Hooks

Every optional integration can be disabled with a single flag in config.yml. Useful when another plugin is installed for its own reasons and you don't want SCS2 talking to it, or when troubleshooting ("does this still happen with the Dynmap hook off?").

hooks:
  vault: true              # economy (claim buy/sell, tax)
  worldguard: true         # registers the scs-claim custom flag
  placeholderapi: true     # %scs_*% placeholders
  geyser: true             # Bedrock client detection
  floodgate: true          # Bedrock UUID mapping
  dynmap: true
  bluemap: true
  pl3xmap: true
  squaremap: true
  quickshop-hikari: true
  griefprevention: true
  lands: true
  towny: true
  nexo: true               # <glyph:...> and <shift:N> tags
  oraxen: true
  itemsadder: true
  pvpmanager: true         # block combat-tagged players from no-PvP claims (needs PvPManager)

Set any hook to false and SCS2 skips the entire setup — no retry timer, no listener registration, no performance cost. PacketEvents is a hard dependency and cannot be disabled here.

Disabling the vault hook automatically forces claims.economy.enabled to false (can't have economy features without an economy provider). Disabling map hooks removes the overlay; it doesn't delete stored claim geometry.

Bedrock Players

When Geyser/Floodgate is installed, SCS2 detects Bedrock clients and routes them through native forms (SimpleForm / CustomForm) instead of the Java chest GUIs. Two knobs control this:

bedrock:
  # true  → Bedrock players see the Java chest GUIs (translated by Geyser).
  # false → Bedrock players see the native forms defined under bedrock-guis/*.yml.
  force-java-menus: false

When to enable force-java-menus

  • Your resource pack uses Nexo/Oraxen glyphs that don't render on native Bedrock forms.
  • You want visual parity between Java and Bedrock clients.
  • You've customized Java GUIs heavily and don't want to maintain two separate UI trees.

Caveat

Chest GUIs translated by Geyser have quirks: shift-click doesn't always fire, some items render without custom model data, and closing via "E" vs the native close button behaves differently. For anything complex, the native Bedrock forms (below) give a better UX.