Skip to content
Saiko

WebSocket protocol

A running Saiko listens on the loopback address. This is everything a client needs to talk to it.

Finding the server

When Saiko starts it writes control.json into its data folder and removes it when it exits.

path
%APPDATA%\io.saiko.workspace\control.json
json
{
  "host": "127.0.0.1",
  "port": 7390,
  "token": "9f1c0b6e5a47d2c8e3b1a0f49d7c25e6a8b3d1f04c92e7ab",
  "pid": 20388
}
  • port is 7390 when that is free, and another free port when it is not. Always read it from the file.
  • token is new every time Saiko starts. Read the file again after a restart.
  • pid is Saiko's own process id, handy for checking it is still alive.

If the file is missing, or nothing answers on its port, Saiko is not running.

Connecting

Open a plain WebSocket to ws://127.0.0.1:<port>/. There is no TLS and no subprotocol. Frames are UTF-8 text, one JSON object each.

Requests and replies

Every request carries the token. The id is optional, can be any JSON value, and is copied into the reply so you can match them up.

request
{"token":"9f1c…","id":1,"cmd":"clients"}
reply
{"id":1,"ok":true,"result":[{"pid":14212,"name":"Builderman"}]}
{"id":1,"ok":false,"error":"bad token"}

Requests on one connection are answered in order, one at a time. A slow inject holds up the requests behind it on that connection, so open a second connection if you need to keep asking things meanwhile. The error messages are the same ones the command line prints.

clients

request
{"token":"…","cmd":"clients"}

The result is the array --get-clients prints: pid for every Roblox client, and name for those with a module that has reported its player.

inject

request
{"token":"…","cmd":"inject","pid":14212}

pid is optional. The reply comes after the module has loaded and had up to twenty seconds to connect.

reply
{"ok":true,"result":{"injected":[{"pid":14212,"connected":true}]}}

execute

request
{"token":"…","cmd":"execute","script":"print('hi')","pid":14212}

script is required. pid is optional, and without it every client with a module gets the script. The reply comes once each module has accepted or refused it.

reply
{"ok":true,"result":{"sent":[14212]}}

ping

request and reply
{"token":"…","cmd":"ping"}
{"ok":true,"result":"pong"}

Checks that the token is accepted and the server is listening, without touching any client. Ordinary WebSocket pings are answered too.

Limits

Size

A message can be up to 16 MiB, which is more than any script you are likely to send. A larger one closes the connection.

Authentication

A request with a missing or wrong token gets bad token and nothing is done. The connection stays open, so a typo does not cost a reconnect.

Unknown commands

A cmd that does not exist gets an error naming it. Text that is not JSON gets not valid JSON.