Skip to content

Security

How this platform protects your server, your data, and itself.

Reporting a vulnerability

Email [email protected] with enough detail to reproduce the issue. Please do not open a public issue, and please do not test against a server you do not own. We will confirm receipt, keep you updated, and credit you if you would like that.

What we hold, and how

  • Discord OAuth tokens — encrypted with AES-256-GCM at rest, never sent to the browser, and only ever used to read your identity and the servers you administer.
  • Sessions — a signed JWT in an HttpOnly, Secure, SameSite=Lax cookie that points at a server-side record. Revocation is immediate; signing out everywhere invalidates every device on the next request.
  • API keys — only a scrypt hash is stored. The key is displayed once at creation and cannot be recovered, only rotated.
  • Sensitive identifiers (FiveM licences, Steam IDs, IPs) — stored as a keyed hash for matching plus an encrypted copy for authorised disclosure during an appeal. The plaintext is never queryable and never rendered.
  • Client IPs — pseudonymised with a keyed hash before they reach the audit log. The raw address is not stored.

Controls in the application

  • Every API route authorises server-side against the database — never against a claim in a token or a value from the client.
  • Changing a server ID in a URL cannot return another server’s data; “not found” and “no access” are deliberately indistinguishable.
  • CSRF protection is an origin check plus a double-submit token, on top of SameSite.
  • Every input is validated with Zod; every database query is parameterised through Prisma.
  • Outgoing requests (webhooks, FiveM checks) are validated against an SSRF policy that resolves DNS and rejects private ranges, re-validating on every redirect.
  • Rate limits apply per user, per server, per API key and globally, backed by Redis so they hold across processes.
  • Secrets are redacted from structured logs and from audit-log diffs by a shared sanitiser.
  • Error responses carry a request ID, never a stack trace, in production.

Deliberate design decisions

  • Discord Administrator does not bypass protection. Only an explicit whitelist entry does. Granting yourself Administrator is the first move after compromising a staff account, so treating it as trust would defeat the product.
  • The bot does not request the Message Content intent. No protection reads message bodies, so asking for them would only enlarge what a compromise of this bot could expose.
  • The invite does not request Administrator. It asks for the specific permissions each protection needs, and tells you what stops working without each one.
  • There is no in-memory fallback for anti-raid counters. A per-process fallback would give each process its own view of a raid. If Redis is down, the service reports itself not-ready instead of protecting inconsistently.

Self-hosting

This platform can be self-hosted. In that case your PostgreSQL and Redis hold all of the above, and the security of the deployment is yours — see the deployment documentation for the production checklist.

Security · Sentinel Anti-Raid