Skip to content

WebSocket API

MAGK provides real-time data streaming over WebSocket connections. The data flow is:

TAK Clients → Python TAK Service → RabbitMQ (magk.cot fanout) → WebSocket Gateway → Browser

Connection Endpoints

Path Auth Description
ws(s)://domain/ws Session cookie or ?token= Authenticated WebSocket — full message stream
ws(s)://domain/ws/public None Public read-only CoT stream

Authenticated Connection

Connect with a session token via cookie or query parameter:

// Using session cookie (automatic in browsers)
const ws = new WebSocket('wss://magk.example.com/ws')

// Using explicit token
const ws = new WebSocket('wss://magk.example.com/ws?token=SESSION_TOKEN')

The server validates the session before upgrading the connection. Invalid or missing tokens result in the socket being destroyed without upgrade.

Public Connection

The public endpoint requires no authentication and provides a read-only CoT event stream:

const ws = new WebSocket('wss://magk.example.com/ws/public')

Public clients are auto-subscribed to the cot channel only.

Message Format

All WebSocket messages are JSON-encoded and follow a discriminated union pattern. Each message has a type field that determines the payload shape:

type WSMessage =
  | WSCotMessage
  | WSNodeStatusMessage
  | WSScoreUpdateMessage
  | WSEventStatusMessage
  | WSMissionChangeMessage
  | WSChatMessage

Every message includes a timestamp (ISO 8601) and an optional eventId to scope the message to a specific event.


Message Types

cot — Cursor on Target

Real-time position and status updates from TAK clients and sensors.

interface WSCotMessage {
  type: 'cot'
  payload: {
    uid: string          // Device unique ID (e.g. "ANDROID-a1b2c3d4")
    type: string         // CoT type (e.g. "a-f-G-U-C" for friendly ground unit)
    how: string          // How the position was determined (e.g. "m-g" for GPS)
    time: string         // Event time (ISO 8601)
    start: string        // Valid start time
    stale: string        // Stale time — marker disappears after this
    lat: number          // Latitude (WGS84)
    lon: number          // Longitude (WGS84)
    hae: number          // Height above ellipsoid (meters)
    ce: number           // Circular error (meters)
    le: number           // Linear error (meters)
    detail?: unknown     // Extended CoT detail fields
    callsign?: string
    affiliation?: 'friendly' | 'hostile' | 'neutral' | 'unknown'
  }
  timestamp: string
  eventId?: string
}

node_status — MAGK Node Status

Status changes from MAGK Node hardware devices (battery, signal, online/offline).

interface WSNodeStatusMessage {
  type: 'node_status'
  payload: {
    nodeId: string
    name: string
    status: 'online' | 'offline' | 'configuring' | 'error'
    previousStatus?: string
    batteryLevel?: number      // 0–100
    signalStrength?: number    // dBm
    lastSeen?: string          // ISO 8601
  }
  timestamp: string
  eventId?: string
}

Example:

{
  "type": "node_status",
  "payload": {
    "nodeId": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Objective Alpha",
    "status": "online",
    "previousStatus": "configuring",
    "batteryLevel": 87,
    "signalStrength": -65
  },
  "timestamp": "2025-01-15T14:30:00.000Z",
  "eventId": "660e8400-e29b-41d4-a716-446655440000"
}

score_update — Score Change

Score updates when a team captures an objective, scores a point, or the leaderboard changes.

interface WSScoreUpdateMessage {
  type: 'score_update'
  payload: {
    groupId: string
    groupName: string
    eventId: string
    points: number
    objectivesCaptured: number
    objectivesDefended: number
    eliminations: number
  }
  timestamp: string
  eventId?: string
}

Example:

{
  "type": "score_update",
  "payload": {
    "groupId": "770e8400-e29b-41d4-a716-446655440000",
    "groupName": "Blue Platoon",
    "eventId": "660e8400-e29b-41d4-a716-446655440000",
    "points": 1500,
    "objectivesCaptured": 3,
    "objectivesDefended": 1,
    "eliminations": 12
  },
  "timestamp": "2025-01-15T14:35:00.000Z"
}

event_status — Event Lifecycle

Notifications when an event transitions between lifecycle states.

interface WSEventStatusMessage {
  type: 'event_status'
  payload: {
    eventId: string
    eventName: string
    status: 'draft' | 'active' | 'paused' | 'completed' | 'archived'
    previousStatus?: string
  }
  timestamp: string
  eventId?: string
}

Example:

{
  "type": "event_status",
  "payload": {
    "eventId": "660e8400-e29b-41d4-a716-446655440000",
    "eventName": "Operation Nightfall",
    "status": "active",
    "previousStatus": "draft"
  },
  "timestamp": "2025-01-15T14:00:00.000Z"
}

mission_change — Mission Updates

Notifications when mission content changes (data packages added, CoT markers updated).

interface WSMissionChangeMessage {
  type: 'mission_change'
  payload: {
    missionId: string
    missionName: string
    changeType: string       // e.g. "ADD_CONTENT", "REMOVE_CONTENT"
    contentUid?: string
    creatorUid?: string
  }
  timestamp: string
}

chat — Chat Messages

Real-time chat messages within event chatrooms.

interface WSChatMessage {
  type: 'chat'
  payload: {
    id: string
    chatroomId: string
    senderCallsign: string
    message: string
    latitude: number | null
    longitude: number | null
    createdAt: string
  }
  timestamp: string
}

Client Usage Example

const ws = new WebSocket('wss://magk.example.com/ws')

ws.onopen = () => {
  console.log('Connected to MAGK WebSocket')
}

ws.onmessage = (event) => {
  const msg: WSMessage = JSON.parse(event.data)

  switch (msg.type) {
    case 'cot':
      // Update map marker position
      updateMarker(msg.payload)
      break
    case 'node_status':
      // Update node status indicator
      updateNodeStatus(msg.payload)
      break
    case 'score_update':
      // Refresh scoreboard
      updateScoreboard(msg.payload)
      break
    case 'event_status':
      // Handle event lifecycle change
      handleEventChange(msg.payload)
      break
    case 'mission_change':
      // Sync mission content
      syncMission(msg.payload)
      break
    case 'chat':
      // Display chat message
      displayChat(msg.payload)
      break
  }
}

ws.onclose = (event) => {
  console.log(`Disconnected: ${event.code} ${event.reason}`)
  // Implement reconnection logic
}

Reconnection

The MAGK web client uses automatic reconnection with exponential backoff. If the WebSocket connection drops, the client retries with increasing delays (1s, 2s, 4s, 8s, up to 30s max). The useCoTStream hook handles this automatically for the tactical map.