Skip to Content
Core conceptsPlayer auth & reconnection

App keys

A client authenticates to Plot with a publishable app key (pl_pub_live_…) from your dashboard. The key identifies your app and is safe to ship in client code — it authorizes only the player handshake (/v1/connect), never writes.

Developer tooling uses a separate developer key (pl_sk_…), a secret revealed in the dashboard. The plot CLI and the developer asset + AI write APIs require it; because it can create, overwrite, and delete your app’s data, it must never be shipped to clients. (Custom player JWTs are signed with your app’s JWT secret — a third, distinct credential; see Player identity.)

Player identity

The simplest setup is anonymous players — you supply any stable playerId:

const plot = new Plot({ appKey, playerId: crypto.randomUUID() });

To tie Plot identity to your own accounts, issue a custom player JWT signed with your app’s JWT secret (HS256) and pass it instead:

const plot = new Plot({ appKey, playerToken });

The server verifies the signature and trusts the sub claim as the player id, so players can’t impersonate one another.

Either a playerId or a playerToken is required — construct Plot with one or the other. When a playerToken is supplied its sub claim becomes the authoritative player id.

Reconnection

Network drops happen. A single resume window governs what a returning player gets back: the room holds their slot (defers onLeave) and issues a short-lived reconnect token, both valid for the same duration. A reconnect inside the window resumes the slot in place — onJoin is not re-run and the player keeps their in-room state — and the token returns them to the same room without re-running matchmaking.

The window is tier-derived:

TierResume window
Free30 seconds
Starter2 minutes
Pro / Studio5 minutes

A longer window lets a player drop off Wi-Fi, background the app, or reload the page and still return to their exact seat. The reconnect token is bound to the player and app and expires with the slot, so it can never resume a seat the room has already released.

room.on('disconnect', () => {/* the client retries with backoff automatically */});

The SDK retries with exponential backoff by default (250 ms doubling to a 30 s cap, ~90 s of attempts). If the resume window has elapsed by the time the player returns, they rejoin as a fresh session and the handler’s onJoin runs again.

Session resume

Reconnection alone survives a dropped socket; session resume additionally survives a full page reload. When enabled, the SDK persists the last authoritative snapshot, any unacked predicted inputs (the outbox), and the reconnect token to a durable store — so a reload picks up where it left off, replaying pending inputs and resuming the same slot rather than starting cold.

It is on by default. Two options control it:

  • PlotOptions.store?: LocalStore — the backing store. Defaults to localStorage when available, falling back to an in-memory store otherwise.
  • JoinOptions.resume?: boolean (default true) — set false to opt out and always join as a fresh session.
const plot = new Plot({ appKey, playerId }); const room = await plot.join({ room: 'LOBBY1' }); // resume: true by default

The session is keyed per app/room/player and is cleared when the player leaves intentionally. The server’s snapshot always stays authoritative, so a stale cache is corrected on the next update.

Last updated on