YUME Internet freedom by FixCraft
On this page

Control protocol 1

Status: normative contract for runtime/control_socket.* and the status replies of yume and yumed. yume --status, yume --accept-route, yumed --status and the desktop GUI are its clients. It is a local interface between processes of one user, not a network protocol.

Purpose

A running yume or yumed answers questions about its state and takes a few requests on a UNIX socket. The GUI is a separate, unprivileged program that talks to the running yume only through this socket, so it can crash or be replaced without touching the tunnel. The rules below let newer programs add fields and requests without breaking an older GUI, and let a newer GUI see what an older program lacks.

The GUI controls a separate client process The window validates and starts a detached client, then uses its owner-only UNIX socket. Closing the window leaves the tunnel running. Disconnect waits for the client to exit. run with --validate validation result start detached, socket path listen, connect, serve socket status and messages state and printed lines close window, client runs reopen window, find client Disconnect: stop acknowledge stop drain and exit process ended: stopped yume-gui window yume tunnel process
The window validates and starts a detached client, then uses its owner-only UNIX socket. Closing the window leaves the tunnel running. Disconnect waits for the client to exit.
What each part does
yume-gui
Owns the display and kit selection. It links no YUME library and sends no traffic through the tunnel itself.src/gui/tunnel.cppsrc/gui/control_client.cpp
yume
Owns credentials, adapters, session and the control socket. The same user can ask status, follow messages and request stop.src/runtime/native_cli.cppsrc/runtime/control_socket.cpp
Text version
+-----------------+                 +-----------------+
|  yume-gui       |                 |  yume           |
|  window         |                 |  tunnel process |
+-----------------+                 +-----------------+
         |                                   |
         |  run with --validate              |
         |---------------------------------->|
         |                                   |
         |  validation result                |
         |<----------------------------------|
         |                                   |
         |  start detached, socket path      |
         |---------------------------------->|
         |                                   |
         |                                .--|
         | listen, connect, serve socket  |  |
         |                                '->|
         |                                   |
         |  status and messages              |
         |---------------------------------->|
         |                                   |
         |  state and printed lines          |
         |<----------------------------------|
         |                                   |
         |--.                                |
         |  | close window, client runs      |
         |<-'                                |
         |                                   |
         |--.                                |
         |  | reopen window, find client     |
         |<-'                                |
         |                                   |
         |  Disconnect: stop                 |
         |---------------------------------->|
         |                                   |
         |  acknowledge stop                 |
         |<----------------------------------|
         |                                   |
         |                                .--|
         |                drain and exit  |  |
         |                                '->|
         |                                   |
         |--.                                |
         |  | process ended: stopped         |
         |<-'                                |
         |                                   |

The socket

The configuration’s control.socket names the socket’s path, and yume --control-socket names another for one run, so a program that starts yume chooses where to find it. The directory that holds it must belong to the running user and be closed to writes by group and others. The socket is mode 0600, and a connection from another user is closed before it is read. The server serves at most 4 connections at once, and later ones wait. Each ends after one reply, or after 2 seconds without a complete request.

A connection carries one request and one reply:

  1. The client sends one JSON object and a newline, at most 512 bytes.
  2. The server sends one JSON object and a newline, under 64 KiB, and closes the connection.

Replies are UTF-8. Text from a peer or the system that is not valid UTF-8 is replaced, so a reply is always one JSON line.

Versions and extension

Every request and reply carries "control": 1. The number changes only for a change an older client cannot survive, which needs a new version of this document. Within protocol 1:

  • Fields and requests are only added. An existing field keeps its name, type and meaning, and an existing request keeps its fields and its reply.
  • A request is closed: it holds exactly the fields its definition lists.
  • A reply is open: a client ignores fields it does not know and does not rely on their order.
  • Every status reply lists, in requests, the requests that program takes. A client sends only a listed request. Any other gets unknown_request.
  • A field described as present when something holds is absent otherwise. A field that can be null is always present.

A new request, such as a command the GUI needs later, gets a name, a closed list of fields and a reply that names its result in a field of its own. A request that changes anything says so in its definition here. No request keeps its connection open: a client that follows a state asks for status again.

Errors

A request that fails gets an error reply:

{"control": 1, "error": "no route proposal has that id", "code": "not_found"}

A client acts on code. The text is for people and may change.

Code Meaning
malformed The request is not a JSON object, is too long, or lacks or adds a field
unsupported control is missing or not 1
unknown_request This program does not take that request
not_found The request names something that does not exist, such as a proposal id
failed The program could not carry out a well-formed request
unavailable The program could not produce its status

Requests

status, taken by both programs, changes nothing:

{"control": 1, "request": "status"}

accept-route, taken by a yume whose configuration has a circuits section, accepts the shorter route a status reply proposes, by its id, a string of at most 64 characters. It changes which routes the client uses. A proposal is described in cluster 1:

{"control": 1, "request": "accept-route", "id": "00112233aabbccdd"}
{"control": 1, "accepted": "00112233aabbccdd"}

An id that no current proposal has gets not_found, so an answer to a proposal the user has not seen accepts nothing.

messages, taken by both programs, changes nothing. It returns the lines the program has printed, numbered from 1 in order, after the number after:

{"control": 1, "request": "messages", "after": 0}
{"control": 1, "instance": "5f0c9a3e11d2b746", "messages": [{"seq": 1, "time": "2026-09-30T12:00:00.250Z", "text": "session authenticated"}], "missed": 0, "more": false}

A program keeps its latest 256 lines, each cut to 512 bytes. The reply holds the kept lines numbered above after, oldest first, each with its seq, its time in UTC and its text, as many as fit in 63 KiB. missed counts the lines numbered above after that the program no longer keeps, and more is true when kept lines were left out, so the client asks again after the last seq it received. instance is the same for the life of the program and changes when it starts again, when numbering restarts at 1. The lines are the ones the program writes to its standard error, and they hold no key, credential or payload.

stop, taken by yume, changes the program’s state: it answers, then stops the program as SIGTERM does, closing its sessions and listeners and removing the TUN networking it owns. A program that started yume stops it this way:

{"control": 1, "request": "stop"}
{"control": 1, "stopping": true}

The program stops also when the client leaves before reading the answer, since the request itself was valid.

The status of yume

Field Value
control, requests 1, and the requests this program takes
program, version "yume", and the product version
state idle, connecting, connected, waiting between attempts, or closed
server host and port of the server the kit names
server_identity When connected: the server’s verified composite fingerprint
connected_ms When connected: how long the session has been up
retry_ms When waiting: the delay before the next attempt
sessions Sessions authenticated since the program started
failed_attempts Failed attempts since the last authenticated session
last_failure null, or the latest failure’s code and message
traffic payload_bytes_sent, payload_bytes_received, record_bytes_sent and record_bytes_received, summed over every session
socks5 The SOCKS5 listeners, as address:port
forwards The forward listeners, as address:port or a socket path
posture The fixed composition and the tuning limits in use, below
circuits null, or the circuits below when the configuration has a circuits section

posture holds what this build and configuration run, for a display that states the posture rather than offering switches: the transport version (YTP/1), its suite, the captured evidence_profile the outer layers follow, the security provider and the crypto_backend library, as yume --version prints them, and limits, the configuration’s max_queued_bytes, max_epoch_bytes, credit_returns_per_window and idle_epoch_rotation, which the tuning presets set. When connected it also holds epoch_bytes, the session’s key epoch: the smaller of this client’s and the server’s max_epoch_bytes.

circuits holds hops, min_hops, accepted_hops (0 unless a shorter route was accepted), current_hops (the length new streams take, 0 while none can be used), plain_http (refuse or allow), serial (the routes view’s, 0 before one is verified), stopped (null, or why circuits stopped), routes and proposal. Each route has its nodes by name, the entry first, its age_ms and its open streams. proposal is null, or a shorter route’s id, hops, nodes, the view’s serial, its cost, what the user gives_up in plain English, and latency_ms when every hop has been measured. cost names what is given up. Its values are these, and a value is added only with a new kind of shorter route:

cost Route What is given up
entry_sees_all 1 hop The entry server sees both who the user is and every site they visit, as a direct connection does
entry_exit_adjacent 2 hops The entry is the exit’s neighbour, so a party that runs or watches both can link the user to the sites they visit

A client may show its own words for a cost it knows, such as a translation, and shows gives_up for any other value.

The status of yumed

Field Value
control, requests 1, and the requests this program takes
program, version "yumed", and the product version
listeners The listening endpoints, as address:port
client_sessions Client sessions open now
loops The sessions open on each event loop, the first loop, which also accepts every connection, first
cluster null, or the cluster below when the configuration names one

cluster holds the list’s id (the operator’s fingerprint), serial, not_after in UTC, whether it has expired, this server’s name as self, its links and its circuits. Each link names the peer and its identity, the outbound link this server keeps (its state, sessions, failed_attempts, last_failure, and connected_ms or retry_ms as for yume), the inbound sessions the peer holds here (sessions, and connected_ms when there is one) and the circuits it carries in and out. circuits counts whether this server is an exit, circuits open, those it is the entry of, those it relayed, exit_streams, failed circuits and those refused, as a total and by bound (client_circuits, circuit_rate, handshakes, streams). No count names both neighbours of a circuit.

Limits

  • Replies hold no key, credential, admission secret or payload. They do name server identities, cluster node names, local listener addresses and the program’s printed lines, which the socket’s permissions keep to the user who runs the program.
  • Any process of that user can send accept-route and stop. That user could edit the configuration or end the process anyway.
  • The socket is not a stable interface for other users or other hosts, and the C ABI does not serve it.