Skip to content

WebSocket protocol

Protocol version 1.0. Served live and machine-readable at GET /v1/protocol.

Transport

  • control_plane: JSON text WebSocket frames (InterfaceMessage).
  • data_plane: Raw binary WebSocket frames for chunk upload/read.
  • rest: All non-WebSocket endpoints are described by /openapi.json.

Endpoints

  • /v1/interfaces/ws: Create/attach any Interface; call/chunk/read/emit.
  • /v1/executions/ws: Attach to an existing Execution by uuid.

Request headers

HeaderRequiredDescription
X-Machinable-ProjectnoProject directory to target (also ?project=); must be under the server's project_roots allowlist. Defaults to the launch project.
X-Machinable-UsernoAttributes created interfaces to this user (created_by). Defaults to the server's OS user.
X-Machinable-PythonnoInterpreter to serve the request (also ?python=); a non-gateway interpreter is routed to a subprocess worker.
AuthorizationnoBearer <token> when the server sets api_token.

Frames

typeDirectionPurpose
connectclient→serverCreate or attach to an interface and bind the project.
connectedserver→clientConfirms the connection; returns uuid and resolved config.
callclient→serverInvoke a public method on the connected interface.
resultserver→clientSingle return value of a call.
streamserver→clientOne yielded value from a generator method; final=true closes.
eventserver→clientUnsolicited push from Interface.emit() (no call in flight).
errorserver→clientFailure detail; payload.code='not_found' for missing reads.
chunk_startclient→serverBegin a binary upload (write/append) or read (mode read).
chunk_endclient→serverFinish the active upload (after one or more binary frames).
chunk_doneserver→clientUpload/read complete (bytes_written / bytes_sent).
widget_getclient→serverRequest a widget's full synced state.
widget_stateserver→clientA widget's full synced state (reply to widget_get).
widget_setclient→serverApply synced-state changes (model.set + save_changes).
widget_changeserver→clientTrait changes (reply to widget_set, or server-originated).
widget_msgeitherCustom widget message (model.send / msg:custom).
pingclient→serverLiveness probe; answered with pong.
pongserver→clientReply to ping.
closeeitherTeardown; the server evicts the interface from its cache.

connect fields:

  • target (string) (required): Module path to instantiate, or an existing uuid.
  • version (array): Version list passed to Interface.make().
  • uuid (string): Client-supplied id for create-by-id (content hash); idempotent in that the same id attaches to the existing instance.
  • label (string): Optional mutable label to set on the instance.
  • project (string): Project dir to bind (overrides header/query).
  • meta (object): Client metadata stored with the interface.

connected fields:

  • payload.uuid (string): Interface uuid.
  • payload.config (object): Resolved configuration.

call fields:

  • method (string) (required): Public method name (no leading underscore).
  • args (array): Positional arguments.
  • kwargs (object): Keyword arguments.

result fields:

  • payload (any): JSON-encoded result.

stream fields:

  • payload (any): One item; null on the terminal frame.

event fields:

  • payload (any): Emitted payload.

error fields:

  • payload.message (string): Message.
  • payload.code (string): Typed code, e.g. 'not_found' on a missing read.

chunk_start fields:

  • id (string) (required): Correlation id; must match chunk_end on upload.
  • mode (string): "write" (truncate), "append", or "read".
  • path (string): Relative path under interface dir (upload only).
  • params (object): Opaque, interface-defined read parameters (read only); passed verbatim to the interface read() hook.

chunk_done fields:

  • payload (object): bytes_written (upload) or bytes_sent (read).

widget_get fields:

  • id (string): Correlation id.

widget_state fields:

  • payload.state (object): Full anywidget model state.

widget_set fields:

  • changes (object): Changed keys/values to apply.

widget_change fields:

  • payload.changes (object): Normalized changed keys/values.

widget_msg fields:

  • content (any): Message body (client→server); reply in payload.

Binary flows

upload: Write opaque bytes to a path under the interface directory.

client → chunk_start {id, path, mode: write|append}
client → <binary frame> ×N
client → chunk_end {id}
server → chunk_done {path, bytes_written}

read: Stream the interface read() hook's bytes back, bounded.

client → chunk_start {id, mode: read, params}
server → <binary frame> ×N (bounded)
server → chunk_done {bytes_sent}
server → error {code: not_found}   # when missing

Interface hooks

Implemented by project interfaces:

<public methods>

python
def method(self, *args, **kwargs) -> Any | Iterator

Any non-underscore method is callable via call. Returning a generator streams as stream frames. Restrict with a class __api_methods__ allowlist.

read

python
def read(self, params: dict) -> bytes | Iterable[bytes]

Binary read hook for chunk_start mode=read. params is opaque to machinable. Raise machinable.errors.NotFound (or FileNotFoundError) to emit error{code:'not_found'}.

emit

python
self.emit(payload: Any) -> None

Push an unsolicited event frame to a connected client during a call (e.g. progress).

widget_state / widget_update / widget_message

python
machinable.Widget hooks (see the Widgets guide)

A machinable.Widget ships an anywidget frontend (_esm/_css) and backs the model protocol: widget_state() (default: read-only config), widget_update() (rejects by default since config is immutable; keep state via save_file or mark), and widget_message(). Pushes via self.push_widget_change/message().

local_directory

python
self.local_directory(*append, create=False) -> str

Working directory where uploaded chunks land and read() reads from. Machinable never parses the bytes.

Capabilities

CapabilitySummarySurfaces
remote_callInvoke any interface method over REST or WS, incl. streaming.POST /v1/interfaces/call; ws: call/result/stream
binary_uploadStream opaque bytes into an interface directory.`ws: chunk_start(write
binary_readStream interface bytes back via its read() hook.ws: chunk_start(read)
config_searchQuery instances by config (operators), sort, and paginate.POST /v1/interfaces/search
create_by_idContent-addressed, idempotent create with a client id.POST /v1/interfaces (uuid); ws: connect(uuid)
mutable_labelRename via a mutable label (config stays immutable).PATCH /v1/interfaces/{uuid}/label
creator_attributionDurable created_by metadata, queryable and reindex-preserved.X-Machinable-User header; search/find created_by
multi_projectServe many projects (and interpreters) from one gateway.X-Machinable-Project; X-Machinable-Python
source_editRead/write/create/rename interface source (token-gated).GET/PUT/DELETE /v1/source/{path}; POST /v1/source/move
widgetsInterfaces ship an anywidget frontend for notebook + web.`GET /v1/project/{module}/widget/{esm

MIT Licensed