Skip to content

WebSocket protocol

The WebSocket protocol is how you get realtime state out of Skynet and (where permitted) how you push commands at telescopes. Two distinct WebSocket endpoints are exposed by the public API:

  • /v1/ws/stream — subscribe/publish for snapshots, events, and log entries. The common case.
  • /v1/ws/commands — command publish + command-result events. For sending commands at telescopes and receiving their results.

The protocol is defined by Pydantic models in skynet_sdk.schemas.ws_protocol and rendered into the OpenAPI spec for type-generation purposes. The TypeScript SDK exposes the message types as a discriminated union.

This section is structured as:

  • Streams — snapshots and events you subscribe to.
  • Commands — commands you can publish (mount, camera, filter wheel, focuser, rotator, lease).
  • Direct request/response — synchronous RPC-like calls over the WebSocket transport.

Connection

The connection URLs are:

wss://api.skynetgo.org/v1/ws/stream
wss://api.skynetgo.org/v1/ws/commands

Use the appropriate base URL for your environment (see Environments). Locally, ws://127.0.0.1:5001/v1/ws/stream etc.

Authentication

Bearer token, passed via one of:

  • Query string?token=<access-token>. Easiest for browser WebSocket APIs (which don't let you set custom headers).
  • Authorization header — for non-browser clients.
  • Session cookie — for browser clients already signed into the web app (the same skynet_session cookie).

The token is the same OAuth access token used for REST requests. See Auth.

Message envelope

Every message on the WebSocket carries a kind discriminator (WsMessageKind). The full set:

Lifecycle

Kind Direction Purpose
ping both Liveness check
pong both Response to ping
ack both Acknowledge a request that has no other response
error server → client Error envelope (see below)

Stream messages (on /v1/ws/stream)

Kind Direction Purpose
subscribe_stream client → server Subscribe to one or more streams
unsubscribe_stream client → server Unsubscribe
publish_device_snapshot client → server (SkyNode) Publish a new device snapshot
publish_telescope_snapshot client → server (SkyNode) Publish a new telescope snapshot
publish_constraint_snapshot client → server (SkyNode) Publish a new constraint snapshot
publish_constraint_status_snapshot client → server (SkyNode) Publish constraint status
publish_telescope_log client → server (SkyNode) Publish a telescope log line
event_device_snapshot server → client Subscribed-stream device snapshot delivery
event_telescope_snapshot server → client Telescope snapshot delivery
event_instrument_snapshot server → client Instrument snapshot delivery
event_constraint_snapshot server → client Constraint snapshot delivery
event_constraint_status_snapshot server → client Constraint status delivery
event_manual_control_lease server → client Manual control lease state change
event_telescope_log server → client Telescope log delivery

Command messages (on /v1/ws/commands)

Kind Direction Purpose
subscribe_commands client → server Subscribe to command and command-result streams
unsubscribe_commands client → server Unsubscribe
publish_command client → server Publish a command at a telescope
cancel_command client → server Cancel a previously-published command
publish_command_result client → server (SkyNode) SkyNode publishes a command result
event_command server → client Command delivery to subscribers
event_command_result server → client Command result delivery

Direct request/response (on /v1/ws/commands)

Kind Direction Purpose
direct_request client → server Synchronous RPC-style request
direct_response_start server → client Response stream start (metadata)
direct_response_chunk server → client Response data chunk
direct_response_end server → client Response stream end
direct_error server → client Direct-request error

Error envelope

WebSocket errors carry more structure than REST errors:

{
  "kind": "error",
  "id": "<optional correlation id>",
  "code": "invalid | forbidden | not_found | conflict | internal",
  "message": "<human-readable message>",
  "details": { /* per-error structured detail */ }
}

The code field is from a fixed enum so clients can dispatch without parsing message.

Subscribe/publish model

The fundamental pattern: open the WebSocket, send a subscribe_* message identifying what you want, and start receiving event_* messages. Multiple subscriptions are fine on a single connection; each subscription has its own filter.

For the specific filter shapes and what each stream carries, see Streams.

SDK support

The TypeScript SDK exposes the WS message types under packages/ts/skynet-sdk/src/schemas.ts as a discriminated union. The Python SDK ships them at skynet_sdk.schemas.ws_protocol. Use these — don't hand-roll the message JSON.