Protocol Specification

TopGun uses a custom WebSocket-based protocol for real-time synchronization, efficient delta updates, and distributed coordination. This document details the wire format and synchronization algorithms for developers building compatible clients.

Overview

Transport:WebSocket (Binary or JSON). Default is JSON for simplicity, MsgPack supported.
Model:Bidirectional, Event-driven. Client pushes ops, Server pushes updates.
Sync:Hybrid Logical Clocks (HLC) + Merkle Trees for convergence.

Message Format

All messages follow a standard envelope structure.

Message Envelope
{
  "type": "MESSAGE_TYPE",
  "payload": { ... },
  "timestamp": {
    "millis": 1678900000,
    "counter": 0,
    "nodeId": "client-1"
  },
  "requestId": "uuid..."
}

Connection Lifecycle

1. Handshake

Client connects to WebSocket. Server sends AUTH_REQUIRED.

2. Authentication

Client sends AUTH with JWT. Server responds with AUTH_ACK or AUTH_FAIL.

3. Synchronization

Client sends SYNC_INIT with last sync timestamp. Server responds with Merkle Root or Diff.

4. Live Updates

Bidirectional exchange of CLIENT_OP and SERVER_EVENT.

Message Types

Authentication

Client authentication flow with JWT tokens.

AUTH

{
  "type": "AUTH",
  "token": "eyJh..."
}

AUTH_ACK

{
  "type": "AUTH_ACK"
}

Data Operations

Operations are idempotent and commute based on HLC timestamps.

CLIENT_OP

{
  "type": "CLIENT_OP",
  "payload": {
    "mapName": "users",
    "key": "u1",
    "opType": "PUT", // or REMOVE, OR_ADD, OR_REMOVE
    "record": {
      "value": { "name": "Alice" },
      "timestamp": { ... }
    }
  }
}

OP_BATCH

{
  "type": "OP_BATCH",
  "payload": {
    "ops": [ ... ] // Array of CLIENT_OP payloads
  }
}

Query Subscriptions

Subscribe to live query results.

QUERY_SUB

{
  "type": "QUERY_SUB",
  "payload": {
    "queryId": "q1",
    "mapName": "users",
    "query": {
      "where": { "role": "admin" },
      "limit": 10
    }
  }
}

QUERY_UNSUB

{
  "type": "QUERY_UNSUB",
  "payload": {
    "queryId": "q1"
  }
}

Synchronization (Merkle)

TopGun uses a prefix-trie Merkle Tree. The tree depth is fixed (default 3), and buckets are determined by the hex characters of the key’s hash.

SYNC_INIT

Client requests sync state.

{
  "type": "SYNC_INIT",
  "mapName": "users",
  "lastSyncTimestamp": 1678000000
}

SYNC_RESP_ROOT

Server returns root hash.

{
  "type": "SYNC_RESP_ROOT",
  "payload": {
    "mapName": "users",
    "rootHash": 12345678,
    "timestamp": { ... }
  }
}

MERKLE_REQ_BUCKET

Client requests bucket hashes if root differs.

{
  "type": "MERKLE_REQ_BUCKET",
  "payload": {
    "mapName": "users",
    "path": "a1" // Hex path in trie
  }
}

Distributed Locks

Distributed locking with fencing tokens.

LOCK_REQUEST

{
  "type": "LOCK_REQUEST",
  "payload": {
    "requestId": "uuid",
    "name": "resource-A",
    "ttl": 5000
  }
}

LOCK_GRANTED

{
  "type": "LOCK_GRANTED",
  "payload": {
    "requestId": "uuid",
    "fencingToken": 101
  }
}

LOCK_RELEASE

{
  "type": "LOCK_RELEASE",
  "payload": {
    "requestId": "uuid",
    "name": "resource-A",
    "fencingToken": 101
  }
}

Pub/Sub

Real-time messaging with topics.

TOPIC_SUB

{
  "type": "TOPIC_SUB",
  "payload": {
    "topic": "chat"
  }
}

TOPIC_UNSUB

{
  "type": "TOPIC_UNSUB",
  "payload": {
    "topic": "chat"
  }
}

TOPIC_PUB

{
  "type": "TOPIC_PUB",
  "payload": {
    "topic": "chat",
    "data": { "msg": "hello" }
  }
}

TOPIC_MESSAGE

{
  "type": "TOPIC_MESSAGE",
  "payload": {
    "topic": "chat",
    "data": { "msg": "hello" },
    "publisherId": "client-2",
    "timestamp": 1678900000
  }
}