rakumux-api exposes the session operations of the running Rakumux server over HTTP/1.1 and JSON,
plus WebSocket streams for a live terminal and for session events. It’s a thin adapter: the server
still decides folder confinement, permissions and the audit log. The MCP server is
its main client, and scripts can use it directly. The endpoint pages in this section are generated
from its OpenAPI 3.1 document.
The API answers only on
127.0.0.1, and refuses browsers, so these pages show requests to copy
rather than a live playground.Running
- One API runs per Rakumux home.
--detachwith a healthy API already running just prints its URL. - The API starts the Rakumux server on the first request when none runs. It never stops the server, so stopping the API leaves every session running.
SIGINTandSIGTERMstop it cleanly. A detached API logs to~/.rakumux/logs/api.log.
Finding it: api.json
While it runs, the API writes ~/.rakumux/api.json (mode 0600):
pid is gone is stale.
Authentication
- Bearer token. Every route except
GET /v1/healthneedsAuthorization: Bearer <token>, with the token fromapi.json. - Loopback only.
Hostmust be127.0.0.1,localhostor[::1](any port), which defeats DNS rebinding. - No browsers. A request with an
OriginorSec-Fetch-Siteheader gets 403, and the API never sends CORS headers. - Bodies must be
Content-Type: application/json, at most 64 KiB. Unknown fields and query parameters are rejected.
api.json.
Identity
Without identity headers the API acts as you. With both of these, the variables Rakumux sets in every session, it acts as that session, confined to its own folder:limit_exceeded. For agents whose tool calls arrive without the session’s environment
(OpenCode 2.x), X-Rakumux-Agent: opencode and X-Rakumux-Agent-Conversation: <id> act as the
session that bound that conversation.
Folders and sessions
A session’s folder is the directory it was started in. Wherever afolder is accepted it may be
an absolute directory (~/ expanded) or the name of a known folder. Sessions come back as
{"session": {...}} or {"sessions": [...]}, with folder, status (live, exited, dead),
lifecycle (working, blocked, done, idle, unknown, none), command, git, terminal
and RFC 3339 times. Earlier builds said project; those names still work and are marked deprecated.
Errors
Errors are{"error": {"code", "message", "details"?}}.
The API’s own:
method_not_allowed 405, payload_too_large 413, unsupported_media_type 415, and
upgrade_required 426 (a WebSocket route without the upgrade).
Streams
GET /v1/events and GET /v1/sessions/{id}/attach are WebSockets on the same port, with the same
checks. The server pings every 30 seconds.
/v1/eventssends{"type":"subscribed"}, then{"type":"event","folder":"/path","session_id":"01J…","at":<unix ms>,"event":{…}}, whereeventiscreated,updated,removed,exited,lifecycle,reportedoractivity./v1/sessions/{id}/attach?role=observer|controller&cols=&rows=first sendsattached, with a base64 checkpoint to feed a fresh terminal ofcols×rows, then terminal output as binary frames. Send input as binary frames (as controller), andresize,promote,demoteorfitas text. Closing the socket detaches.
Remote access
With phone access on, the API adds a remote listener on the machine’s Tailscale addresses, atremote.json’s port (7337 by default), or on remote.json’s listen
addresses or --remote-listen ADDR:PORT instead.
- Its bearer is the remote token from
remote.json, not theapi.jsontoken. - It doesn’t check
Hostand allowsOrigin(the phone app’s WebSocket sends one). - The caller is always you: identity headers are refused (403).
- It serves the same routes.
Example
jq is used only by this example; Rakumux doesn’t need it.)