Skip to content

Authority System

In a peer-to-peer game, every player runs their own copy of the game logic. This works well for cooperative or turn-based games, but some genres need a single source of truth -- one player whose copy of the game state is considered authoritative. Signal Fish Server provides an optional authority system that lets you designate exactly one player per room as the authority.

What is Authority?

Authority means one player acts as the trusted host for game-critical decisions: physics resolution, score keeping, anti-cheat validation, or any logic where conflicting results between peers would break the game. The other players send their inputs to the authority and accept its responses.

Signal Fish does not enforce what the authority player does with that role -- it simply tracks who holds authority and notifies all players when it changes. Your game code decides how to use it.

Enabling Authority

Authority is enabled by default. It is configured at room creation time via the optional supports_authority field in the JoinRoom message: omit it (or set true) to enable authority, set false to disable it. The example below sets the field explicitly, which is equivalent to the default:

JSON
{
  "type": "JoinRoom",
  "data": {
    "game_name": "my-game",
    "player_name": "Alice",
    "max_players": 4,
    "supports_authority": true
  }
}

supports_authority defaults to true when omitted, so authority is enabled and the creating player becomes the initial authority unless you explicitly set supports_authority: false. In a room created with supports_authority: false, AuthorityRequest messages are rejected with the AUTHORITY_NOT_SUPPORTED error code.

How Authority Works

Initial Assignment

The first player to join a room with authority enabled becomes the authority by default. When Alice creates the room above, her RoomJoined response includes "is_authority": true, and all subsequent players who join see her flagged as the authority in the current_players list.

Requesting Authority

Any player in the room can request authority by sending an AuthorityRequest message:

JSON
{
  "type": "AuthorityRequest",
  "data": {
    "become_authority": true
  }
}

The server responds directly to the requesting player with an AuthorityResponse:

JSON
{
  "type": "AuthorityResponse",
  "data": {
    "granted": true,
    "reason": null
  }
}

If the request is denied (for example, because another player already holds authority), the response includes a reason and the error code for that specific cause:

JSON
{
  "type": "AuthorityResponse",
  "data": {
    "granted": false,
    "reason": "Another player already has authority",
    "error_code": "AUTHORITY_CONFLICT"
  }
}

The code tells you whether retrying can ever succeed:

error_code Cause Retry?
AUTHORITY_CONFLICT Another member holds the role right now. Yes — after an AuthorityChanged clears it.
AUTHORITY_NOT_SUPPORTED The room was created with supports_authority: false. No — no member of this room can ever hold the role.
AUTHORITY_DENIED You are not a member of the room, or you released a role you do not hold. No — nothing about the room's state will change that.
ROOM_NOT_FOUND The room disappeared before the authority transition committed. Rejoin or create a room before retrying.
NOT_IN_ROOM You are not in a room at all. Join a room first.
STORAGE_ERROR The server could not decide the request. Yes — this is a transient server-side fault.
INTERNAL_ERROR The request failed before storage was consulted. Yes — this is a transient server-side fault.

Broadcasting Authority Changes

When authority changes, every player in the room receives an AuthorityChanged message. The you_are_authority field is personalized per recipient:

Sent to the new authority:

JSON
{
  "type": "AuthorityChanged",
  "data": {
    "authority_player": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "you_are_authority": true
  }
}

Sent to all other players:

JSON
{
  "type": "AuthorityChanged",
  "data": {
    "authority_player": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "you_are_authority": false
  }
}

Releasing Authority

A player can release authority by sending become_authority: false:

JSON
{
  "type": "AuthorityRequest",
  "data": {
    "become_authority": false
  }
}

This clears the authority role. All players receive an AuthorityChanged message with authority_player set to null:

JSON
{
  "type": "AuthorityChanged",
  "data": {
    "authority_player": null,
    "you_are_authority": false
  }
}

Authority Player Disconnects

If the authority player leaves or disconnects from the room, authority is cleared -- there is no automatic reassignment. All remaining players receive an AuthorityChanged message with authority_player: null, ordered after the PlayerLeft that explains it. (One exception: if storage is unavailable at the moment of the disconnect and the compensating release also fails, the durable removal is retried in the background and the role is cleared when that repair lands, without a further notification.) A member that reconnects across the change is told the room's authority as it stands when it returns: its Reconnected snapshot reports whether it holds the role (is_authority) and flags the holder in current_players, and any buffered authority event that snapshot already supersedes is not replayed. Your game logic should handle this case, either by prompting another player to claim authority or by pausing the game until someone does.

If the departed member reconnects while another player has since claimed the role, the successor keeps it: the returning member is restored as an ordinary, non-authority member.

Moderation Powers

The authority holds a set of moderation operations (v3 only, via the room_operation_ids capability; see Protocol Reference):

  • KickPlayer removes a seated player from the room — or tombstones this room's pending reconnection record. The target's connection closes with close code 4007 (kicked) unless the target is live in another room, the remaining members see the usual PlayerLeft roster delta, and the kicked seat is never reconnectable. Use it to evict a disruptive player instead of waiting for them to leave.
  • BanPlayer evicts exactly like KickPlayer and records the player id on the room's ban list: while the room lives, the banned id cannot rejoin it as a player or a spectator (BANNED). UnbanPlayer lifts a ban (idempotent). Bans are room-scoped and in-memory: they expire when the room dies, and no cross-room or cross-instance effect exists.
  • RegenerateRoomCode replaces the room code with a fresh one. Existing members stay connected; the old code stops resolving immediately. Use it when an invite code leaks: the room survives, and only holders of the new code can send new joiners. The rotation result (RoomCodeRegenerated) is delivered only to the authority, who shares the new code with future invitees out of band. The rotation is never broadcast: a member who stays connected keeps the stale code until the next snapshot, and a reconnecting member receives the current code in Reconnected. A stale code is an unknown code: a join that names it opens a different room. No message queries the current code, and the transfer announcements carry no code. A role hand-over after a rotation needs the new code shared with the successor out of band first; otherwise the new authority holds only their join-time code and cannot invite (issue #566).
  • SetRoomAccess seals the room behind a join password (or reopens it with null). Sealed rooms refuse every join -- seated or spectator -- that does not present the password (PASSWORD_REQUIRED); a missing, a mismatched, and a stray password into an open room are indistinguishable. Current members and their reconnection tokens are unaffected. A pending reconnection record armed before the seal also restores its holder with no password: Reconnect carries no password field, and resuming a prior membership through a live seat credential is not a fresh admission. (A ban is the tool that refuses restores.) The password is stored only as a salted hash. See Rooms and Lobbies for the client-side flow.
  • TransferAuthority hands the authority role to a seated member of your choice. Every member receives the usual AuthorityChanged broadcast; the sender loses every authority capability (including StartGame and the operations above) and the successor gains them. The transfer carries no room code: a successor who joined before a rotation still knows only their join-time code. Share the current code out of band before or with the transfer (issue #566). If the session is already running, the transfer moves the role but not the session's transport host (see below).

All of these are authority-only: a member that is not the authority receives NOT_ROOM_AUTHORITY. The kicked or banned player cannot be the authority itself.

Handing Over Authority Deliberately

TransferAuthority is the planned successor path: before you leave -- or when the game should change hands -- name a seated member and the server moves the role atomically (validated under the room's mutation gate, so the successor cannot depart between validation and the grant). This is stronger than the drop-and-claim dance (release the role, everyone races to claim) and works even when no member was primed to claim: the departing authority picks any seated member, including one that never requested the role.

The Role and the Transport Host Are Not the Same Thing

When a v3 room finalizes, the server elects a transport host for the running session: the peer every other member connects to for P2P traffic (for a host/star topology) or the member that anchors the session. This election is recorded at finalize time and does not follow the authority role afterwards.

A mid-game TransferAuthority changes who moderates (kick, ban, room access, room-code rotation, further transfers) and who may re-start or manage the session -- it does not re-elect the transport host. After a mid-game transfer, clients receive AuthorityChanged, while the session plan keeps hosting on the finalize-time host. The host only moves when a host-failover event happens: the elected host departs (or can no longer run the session), and the server re-elects a host and re-emits fresh session plans.

If your game wants the new authority to also become the transport host, the simplest supported path is: have the old host leave (or reconnect the session through a re-plan) after the transfer. Design your client to read the transport host from the SessionPlan, not from the authority role.

Key Rules

  • Only one player can hold authority at a time per room.
  • Authority is enabled by default (supports_authority: true); pass supports_authority: false at room creation to disable it.
  • The first player to join becomes authority by default.
  • Authority transfers are explicit via AuthorityRequest.
  • Disconnection clears authority with no auto-reassignment.
  • Authority status is included in the legacy GameStarting peer metadata so clients know who the authority is at game start.
  • Moderation is authority-only: KickPlayer, RegenerateRoomCode, SetRoomAccess, BanPlayer, UnbanPlayer, and TransferAuthority are refused with NOT_ROOM_AUTHORITY for every other member, and rooms created with supports_authority: false have no moderator at all.
  • A mid-game authority transfer moves the role, not the session's transport host: the host was elected at finalize time and only changes through host failover.

Use Cases

  • Host migration -- If the current host drops out, another player requests authority and becomes the new host.
  • Authoritative physics -- The authority player runs the physics simulation and broadcasts results to peers.
  • Anti-cheat validation -- The authority cross-checks player inputs against game rules before accepting them.
  • Asymmetric gameplay -- One player acts as the game master or dungeon master with elevated control.

When NOT to Use Authority

If your game is fully symmetric -- every peer runs identical logic and resolves conflicts through deterministic lockstep or rollback netcode -- you do not need the authority system. Skipping it simplifies your room setup and avoids unnecessary authority-related messages.

Next Steps