Auth & Admission
How PlayMesh handles authentication and decides which instances a session joins.
Authentication
PlayMesh does not implement authentication. It provides a hook where your application authenticates the incoming connection and returns a user ID. If the hook throws, the connection is rejected.
ℹ
Without an
onAuthenticate hook, sessions are anonymous - the socket ID is used as userId.The hook
JWT example
Sending auth from the client
Admission
After authentication, the admission hook decides which instances the new session joins. The framework then automatically joins the session before the client connection resolves.
✦
References can be
"domainId/instanceId" paths or bare instance IDs (unique across all domains).Roles
The authentication hook can grant roles to a session (
roles: ['admin']). Inside handlers, session.roles and session.hasRole(role) are available for custom checks, and the requireRole middleware protects events by role - events without a rule stay open to everyone:Role-protected domains and instances
Domains and instances can require roles to join. Every join path enforces it - client join requests, admission, and server-side
session.join(). Instance roles apply on top of the domain's.One session per user
By default a user can hold multiple concurrent sessions (several devices or tabs). The
uniqueUser option enforces one live session per userId:Client join requests
Clients can ask to join an instance with
client.join(path). These requests are denied by default - the server stays authoritative. Opt an instance in with a veto hook:Return
true to admit; return falseor throw to veto (a thrown error's message is sent to the client). Server-side session.join(...) and admission are unaffected. client.leave(path) is always honored.Manual assignment
Connection flow
1. Client connects with auth payload
2. onAuthenticate hook - verify token, return userId
3. Session created
4. onSessionCreate hooks run
5. onConnect hooks run
6. onAdmission hook - return instance list
7. Session joins each instance (join hooks fire)
8. playmesh:session event sent to client
9. client.connect() resolves with SessionInfo
Message signing
Opt-in integrity for application events. When both sides enable
signing, every application event in both directions is signed with ECDSA P-256 / SHA-256 (WebCrypto, no extra dependencies). It must be enabled on both sides - a signing server rejects unsigned clients and vice versa, with no silent downgrade to unsigned mode.Replay protection
Every signed envelope is bound to a random session nonce the server generates per connection (a reconnect gets a new one), the direction and event name, and a strictly increasing sequence number. Together these prevent replay within a session, across reconnects, and against other server nodes. Messages that fail verification are dropped - the wire error is always a generic
Signature verification failed, sessions accumulating failures are disconnected, and on the client failures are reported via onError.Payload restrictions
Signed payloads must be JSON-compatible (objects, arrays, strings, booleans,
null, finite numbers). Date values arrive as ISO strings. Binary values, BigInt, functions, symbols, non-finite numbers and circular structures make emit() throw instead of silently corrupting the message.⚠
Signing does not replace TLS - keys and nonces are exchanged over the transport at connect time, so production must use
https/wss. And it does not prevent cheating: a malicious client signs its own payloads with a valid key, so the server must still validate and authorize everything.ℹ
In multi-node deployments the server's signing keypair is shared between nodes through Redis (stored unencrypted at
playmesh:signing:keypair), so treat Redis as part of the trust boundary: require auth, keep it on a private network, and never log or dump that key. There is no automatic key rotation - to rotate, delete the key and restart the cluster.