Security & encryption
How Rust Pulse stores your Rust+ pairing token, and why that matters more than your Steam ID.
When someone asks “is it safe?”, what they're really asking is “can someone act as me in-game?”. Your Steam ID is not a secret — it's on every server roster and BattleMetrics page. The thing that lets a client pretend to be you is a different object: the Rust+ playerToken.
Rust Pulse takes that token seriously. Here's the full model.
Login: Steam OpenID 2.0
- Authentication happens on Steam's own page. Rust Pulse never sees your Steam password.
- After Steam confirms, Rust Pulse receives a signed Steam64 and stores only your
steamId,displayName, andavatarUrl— all of which are public. - Sessions are encrypted
httpOnly,secure,sameSite=laxcookies. JavaScript on the page can't read them, browser extensions can't read them.
Rust+ pairing token: AES-256-GCM at rest
When you pair a server, the Rust+ app sends a Firebase push notification through the FCM listener. That payload contains a playerToken — the long-lived secret that lets a client speak to your server's Rust+ socket as you.
In Postgres:
- The
playerTokenand the full FCM credential bundle are encrypted with AES-256-GCM before being written. - The encryption key (
CREDS_ENCRYPTION_KEY, 32 bytes) lives only in the server environment. The key is never in the database. A full DB leak yields opaque ciphertext. - The relevant schema columns are explicitly labelled “ENCRYPTED — do NOT store raw” so a future contributor can't accidentally write plaintext.
- The blob is decrypted in memory only when the relay opens a Rust+ connection on your behalf, then dropped.
Upload guard: Steam ID match
When the Credentials Helper uploads an FCM bundle, the API decodes the payload, reads the Steam ID inside, and rejects the upload if it doesn't match the Steam ID on your session. Meaning: an attacker who somehow took over your dashboard session cannot swap in someone else's Rust+ credentials and re-bind them to your account.
Internal worker calls
The FCM listener is a separate process. When it tells the API “user X just paired server Y”, the request is signed with a per-deployment HMAC (X-Internal-Signature). The API rejects unsigned or wrongly-signed requests.
Relay tokens
The relay WebSocket doesn't accept your session cookie. The dashboard mints a short-lived JWT via POST /relay/token and the relay accepts only that JWT. Tokens expire in minutes, scope to one user, and are unforgeable without the API's signing secret.
Stripe webhook integrity
- Stripe signs webhook payloads.
- The API verifies the signature against the raw body before processing.
- Every processed event ID is recorded, so a replay is a no-op.
Device pairing codes
The Credentials Helper and overlay both pair via short codes (RUST-7A3B etc).
- Codes expire 10 minutes after issue.
- Max 3 outstanding codes per user.
- The 48-byte device bearer token is returned exactly once on activation; if the helper loses it, the code is gone and a new one has to be issued.
Open-redirect protection
The Steam OpenID callback accepts a ?return= parameter so it can land you back on the right surface (dashboard, admin, overlay). The accept-list is FRONTEND_URL and ADMIN_ORIGIN from env. Anything else falls back to the dashboard. There's no way to abuse the callback to bounce a user to an attacker site.
Admin gating
/admin/*requires both a valid session and a Steam ID on theADMIN_STEAM_IDSenv allowlist.- An empty allowlist returns 503 from every admin endpoint — the safe default.
- Admin actions are recorded in an audit log table viewable from the admin portal.
One-sentence summary
Rust Pulse uses Steam OpenID for login and AES-256-GCM with a server-side key for your Rust+ pairing token. A full database leak doesn't expose the thing an attacker actually wants — unlike self-hosted bots that keep that same token in a plaintext file on whoever's computer is running them.
See also
- Comparison vs rustplusplus — the “why is this safer” long-form.
- Credentials Helper security — the desktop side.