Skip to content

Versioning

The real-time protocol is versioned at the subprotocol level and evolved additively at the event level. This page defines compatibility rules, breaking-change policy, deprecation, and event evolution.

Reference: protocol.md · connection.md


Protocol versions

The protocol version is negotiated during the handshake via the Sec-WebSocket-Protocol header.

Subprotocol Meaning Status
websocket.v1 Current protocol version. Active — use this.
auction-room.v1 Legacy auction room protocol. Accepted for backward compatibility.

The server accepts websocket.v1 and auction-room.v1. Both may be combined with bearer.<token>:

Sec-WebSocket-Protocol: websocket.v1, bearer.<access-token>

Negotiation behaviour

  • The server echoes a supported protocol in the 101 response.
  • If the client offers only unknown protocols, the upgrade fails.
  • The client must keep sending a supported protocol; a future websocket.v2 will not be accepted by a server that only supports v1.

Version compatibility

Compatibility is defined as follows:

Change Compatible? Policy
New event type (client or server) ✅ additive Old clients ignore unknown server events; the server rejects unknown client events with BAD_REQUEST.
New optional field in data ✅ additive Validators accept extra fields; missing optional fields are fine.
New optional envelope field ✅ additive Base envelope ignores unknown top-level keys.
New permission ✅ additive Existing role permission sets grow; old clients unaffected.
Changed required fields / renamed events ❌ breaking Requires a new protocol version.
Changed error codes ❌ breaking Requires a new protocol version or a documented migration window.
Removed event ❌ breaking Deprecate first; remove only in the next major protocol.

Guiding rule: the server never removes or reshapes a documented event within the same protocol version.

Breaking changes

A change is breaking when it can break an existing client:

  • removing or renaming an event type;
  • making a previously optional field required;
  • changing the semantics or units of a field;
  • changing an error code or close code meaning;
  • reducing a rate limit below client-negotiated expectations;
  • changing authentication transport.

How breaking changes are shipped

  1. Introduce the new behaviour behind a new subprotocol (e.g. websocket.v2).
  2. Keep websocket.v1 functional for the full deprecation window.
  3. Publish migration guides and a cutover date in release notes.
  4. Remove the legacy version only after the window closes (see Deprecation).

Deprecation policy

Phase Client behaviour Server behaviour
Additive No action. Ships new fields/events; old ones unchanged.
Deprecation notice Schedule migration. Old behaviour still works; logs may flag usage; docs mark it Deprecated.
Soft removal Must migrate. Old events return a BAD_REQUEST with a deprecation message, or are gated.
Hard removal Upgrade required. Legacy subprotocol removed from negotiation; old events removed.

Minimum deprecation window: one major release cycle (announced at least 90 days ahead in the changelog) for public API events.

Event evolution

Event evolution follows strict rules to keep both directions safe:

Client → Server (commands)

  • Additions: new event types, new optional fields, new permissions.
  • Deletions: never within a version.
  • Field changes: additive only; changing data shape requires a new version.

Server → Client (pushes)

  • Additions: new event types are safe — clients must ignore unknown types.
  • Payloads: additive fields are safe; removing/renaming requires a new version.
  • Clients must treat unknown event types as ignorable but still recover state from the next auction.snapshot.

Aliased / documented names

The documentation lists names such as auction.updated, auction.ended, auction.bid.created, presence.update that map to canonical server events (auction.status_changed, auction.closed, bid.created, presence.changed). These aliases are part of the documented contract, not separate wire events; if a canonical event name ever changes, the alias table in events/auction.md and events/presence.md is updated at the same time.

Request/response versioning

  • The envelope itself is versioned by the subprotocol; there is no per-message version field.
  • A requestId is never reused to mean different things across versions.
  • New envelope fields (like timestamp) are additive and optional.

Snapshot/replay as a versioning safety net

Because clients can always recover from auction.snapshot + lastSequence replay, a dropped or re-shaped server event does not leave a client permanently out of sync. This makes many otherwise-risky event evolutions safe within a version — provided the snapshot schema itself remains compatible.

Release cadence

  • Additive changes: shipped continuously; no client action required.
  • Breaking changes: coordinated through a new subprotocol and a scheduled deprecation window; announced in the changelog and migration guide.
  • Removal: only after the deprecation window, with usage telemetry showing legacy clients have migrated.