Login
OCX protocol v1
Login

OCX extends Fossil, so people and programs can ask a node to run a process, talk with it, learn when it ends, and end it, and ask a user a question and learn the decision (questions). This is the protocol, written as data: the tables are the protocol, and the prose only explains them, so a node's code is small engines that read them (KEEP.md, "Structure the data"). What it rests on, Fossil as the medium, who sent an artifact, nodes and versions, is Fossil, senders and nodes. The maintainer promoted it on 2026-10-13 (HISTORY.md decisions 44 and 46), and it is stable: section 15 says what may be added and what never changes.

What it is for

Each row names a problem, its answer and the README virtue it serves.

Problem What version 1 does Virtue
Work must run on machines no one can reach from outside, such as a build machine or a lab board behind its own network a node pulls runs from the server and posts its reports through Fossil's pages; nothing listens for a connection, and no port is opened Loose Coupling
Afterwards, no one can say what ran, who asked for it, what it was given and how it ended every run, stop, report, delivery and kept log is an artifact in the repository, its sender the user the server received it from Demonstrability
An agent or program must not hold the keys that could change what it is allowed only a user holding admin or setup can send a run, or one holding R within what each node's owner allows; a run's user never holds admin or setup, though it may hold R, which lets it ask no more than that, and a node user never holds admin or setup Clarity
What a process may see and use must be known before it starts, not learned by watching it the run states its sandbox and limits in full; the node adds nothing, records the values and limits it applied in started, and refuses what it cannot provide Clarity, Parsimony
Asking for work must not depend on one program or on being online a message is a JSON block in a forum post or ticket change; Fossil's web pages, any program, or a clone offline (a ticket change, or OCX's post) can write one, and OCX's tools are a convenience, but for starting a manual run at its node Loose Coupling
Talking with a running agent must not need a connection to it a session is given each post, ticket change or check-in it subscribes to, a line of JSON on its standard input; it answers by posting Loose Coupling, Parsimony
A run must never be run twice, nor end unanswered a node writes started before it launches, so it launches at most once, and a run for a node user several clones share names its clone (section 3); every run it acts on gets one ended, and one its going down left is answered lost when it starts again Demonstrability
The node must be replaceable, and readable by people and machines the protocol is these tables; a node is small engines that read them, and another can be written from these pages Loose Coupling, Clarity
A question put to a person drops from sight once anyone posts after it, and its asker cannot tell it was answered a question is a message to a user, as a run is to a node; anyone there may send a decision, and it stays open until its sender, or whoever sent the run it names as asking it, confirms it answered, whatever else is said there (questions) Clarity, Demonstrability
Each moving part is another way to fail one daemon per machine waits on Fossil's chat, pulls whole clones and hands runs to watchers; each watcher lives as long as its run (How a post reaches an agent) Parsimony

An OCX message is a JSON object in a fenced block of a forum post or a ticket change. A run's message is built in layers, like a network packet, and a node processes them outer to inner, in the order of table 1.

Notation: {"a", "b"} stands for a JSON object with the keys a and b, whose values the table beside it gives. "The server's order" is the order the server received artifacts in: by its receipt's number, then by the artifact's row number (Fossil, senders and nodes, section 1).

1. Layers

# Layer Key Answers Section
1 Envelope ocx, kind, node, clone if given, and run in a stop or a report; in a question for, and run if given; in a decision question which version, what kind, for which node or user, and which repository of it, about which run or question 3
2 Workspace workspace the files the process works in, made step by step 5
3 Sandbox sandbox what the process sees and may do 6
4 Limits limits how much the process may use 7
5 Process process what to run, as whom, when it starts, what it is given 8

A run without workspace gets none, one without sandbox runs unconfined, and one without limits runs unlimited. A node never runs a run with less than it asks: what it cannot provide it refuses (section 11).

2. Values

Not a layer: how a string names something the node knows, written $$name$$. The node sets the values of this table before it processes the workspace, from the run itself and from the node; a workspace step sets more (section 5), for the steps, layers and process after it. started lists every value set, as it was.

Name Value
root the directory under which the node makes its runs' workspaces
project the server's repository's name: the last part of the server's URL, or its file's name without extension
user the run's process.user
run the run's hash, in full
node the node user
server the server's URL, from which a run's clone is made
release the directory of the OCX release the node runs, whose programs a process may use
releases the release root the node runs from, holding current, staged and the releases
secret:NAME secret NAME in the node's clone, set by an admin with OCX's secret; it is not copied to a run user's clone; filled in process.env alone; never written in started or the kept logs, where its value reads as $$secret:NAME$$

How a string names a value:

Written Read as
$$, a name of letters, digits or _, then $$ the value of that name; a run naming a value not set by then is refused (section 11, row 3)
everything else itself, $ and { included, so a shell's ${HOME} or $$ reach the process as written

Values are read in every string of workspace, sandbox and process, at any depth, but a step's remove_on and remove, sandbox.workspace_at, process.user, process.deliveries, process.hears and process.checkins, and never in an object's keys. With sandbox.workspace_at (section 6), $$workspace$$ and each value within it, such as $$clone$$ and $$tree$$, read in sandbox and process where the process sees them, under that path, but in read_only, read_write and homes, which name host paths, and a share's at, which the node reads on the host; started lists them as on the host. The node checks every value a run names when the run arrives, before any step, knowing which value each step sets: a run naming a value that nothing sets before it is refused then, and nothing is made. A default (section 8) is read the same way, but a default that names a value not set is left out instead of refused.

3. Message kinds

Kind Sent by Written where Its fields Answered by
run a user holding admin or setup; or one holding R, within what the node's owner allows (section 11, row 1) anywhere: a new thread, a reply, a ticket change ocx, kind, node, clone and of if given, objective, and the layers of table 1 started then ended, or ended alone
stop a user holding admin or setup; or one holding R, for a run it sent where its run is ocx, kind, node, run its run's ended
started the node, as the node user where its run is ocx, kind, node, clone (required for a run naming one), run, values, limits none
ended the node, as the node user where its run is ocx, kind, node, clone (required for a run naming one), run, outcome, exit, note (table 12), and for a run whose workspace is removed once saved (section 5) workspace: removed, or kept: and the first five things it holds that the server lacks, with how many in all past five, or why it was kept: what of it could not be removed, or that it was not read none
question any user who may write where it is a reply, a ticket change, or a new thread ocx, kind, for, question, and options, stakes, references, run if given (questions) a decision; no node acts on it
decision any user who may write where its question is; from its sender, or from whoever sent the run the question names in run as asking it, when that run counts, it closes the question, but for the first decision of the user it is for (questions, section 2) where its question is ocx, kind, question, note none

objective (text, required) says what the run is for, and what counts as passing. run, in a stop or a report, is the run's hash in full: the hash of the forum post or ticket change artifact that holds the run.

node names a node user; with no clone, any node of that user may act on the run. clone names one clone among those whose node uses that user (Fossil, senders and nodes, section 5), by its server-code, as Fossil names a check-in by a prefix of its hash. The host discovers its projects from password files as section 5 of Fossil, senders and nodes describes. A stop names none: the node that holds the run acts on it. A node on a release before clone refuses a run naming one (section 15), and its ended carries no clone, so it does not count: the named clone still acts, and readers see the refusal.

Key In Holds Absent
clone run optional: left out, any node of the run's node user may act, as before the key; given, the server-code of the repository whose node is to act on the run, or a prefix of it: 10 to 40 lower-case hex digits, naming every repository whose server-code starts with it; another form refused by every node of the node user (section 11, row 2), naming clone; a node of the node user on another repository does nothing with the run, and refuses nothing, since the clone it names may take it any node of the node user may act on it
clone started, ended the server-code, in full, of the repository whose node wrote it; for a run naming a clone, a report counts only if it was received from the node user and its clone starts with the run's counts only for a run naming no clone
of run a check-in the process takes without a files step; the node resolves it before the process and sets $$checkout$$; create --of writes it here when there is no files step $$checkout$$ is set by a files or checkout step, if any

Who sent a message is the user the server received it from (Fossil, senders and nodes, section 3), never the name written in it. A post's edit is an artifact of its own: an edit holding a message is a new message, asking again, its hash the edit's.

A message that Is
is a run sent by anyone the table does not name answered ended, refused
is a stop, started or ended sent by anyone the table does not name, or written anywhere else ignored
is held private by Fossil, as a post awaiting a moderator is counts for nothing until it is public, as on a clone, which never holds it; then in the server's order as of when it arrived, since Fossil keeps the receipt it arrived by (Fossil 2.27, tried 2026-10-21; questions)
has a kind the reader does not know ignored
is a second started or ended for one run, or a decision on a question already closed ignored: the first, in the server's order, counts

4. What the node keeps

The repository holds every message; the node keeps on its own host only what its runs need, and nothing a reader of the record depends on.

Kept For
which runs it has in its care: from just before the workspace is made until ended is written so it acts on each run once, and finds what its going down left (section 10)
what each session in its care has been given so it gives each artifact at most once
the deliveries waiting for a session whose deliveries is start so each process gets one
its file of passwords its node user and the run users it may run as (section 5, password)
each run's state (NEW TERM, needs approval), runs/HASH.state, beside its lock, written only while that lock is held taken prevents relaunch; ending holds the exact ended to retry; done settles the run; unreadable JSON counts as taken
each clone's completion number (NEW TERM, needs approval), completed/SERVER-CODE the highest receipt after a whole pull, published by rename; watchers read nothing received after it

5. The workspace layer

The workspace is a list of steps, run in order. A step is an object naming its kind in step, with that kind's arguments, which may name values (section 2). A step that fails ends the run refused, its note naming the step. The process runs as the node's own account, in the workspace the steps make.

Step Arguments Needs Does Sets
directory at, within $$root$$; remove_on, optional, whose one value is saved, read also as remove, its older name, which runs and patterns written before it may hold, and which OCX writes only where a pattern given to create holds it; a step may give both, each saved, and is refused naming either that is not (section 11, row 2; HISTORY.md decision 103) none makes the directory, removing what was there, readable by the node's account alone; with remove_on, as the run ends, once no process of it is left and before its ended is written, removes it again unless it holds work the server lacks: a change, a file Fossil does not track or a stash in the checkout a checkout step made, or an artifact not sent in the clone a clone step made, and keeps what of it cannot be removed; refused without a sandbox whose ends_with_node is true; a run found not in the node's care, answered lost, keeps it, as does one whose node fails at a step of its own (section 10), unread workspace
password none none reads the run user's password from the node's file of passwords none (a password is never a value)
clone into, within $$workspace$$ password clones the server as the run's user into into, remembering the password clone
checkout of; into, within $$workspace$$ clone opens a checkout of the check-in of names (any name Fossil resolves), resolved once, into into tree, and checkout: the check-in in full
files of; into, within $$workspace$$ none writes the files of the check-in of names, resolved once in the node's own repository, into into, each executable as the check-in marks it, and nothing else: no repository, no checkout's record, no password, so a build signs in as no one and its user needs no account tree, and checkout: the check-in in full

"Within" means below that directory, not the directory itself, once links are followed. A directory whose at is not within $$root$$, or is or holds another run's workspace while that run is in the node's care, and a clone, checkout or files whose into is not within $$workspace$$, are refused when the run arrives, before any step (section 11, row 9): a step never removes or writes anything outside the run's own place.

The steps most runs take, which a pattern holds once:

"workspace": [
  {"step": "directory", "at": "$$root$$/$$user$$/projects/$$project$$/$$run$$", "remove_on": "saved"},
  {"step": "password"},
  {"step": "clone", "into": "$$workspace$$/clone"},
  {"step": "checkout", "of": "trunk", "into": "$$workspace$$/tree"}
]

A build, which only reads its check-in and holds no credential, takes its files instead; its node sends back what it produced:

"workspace": [
  {"step": "directory", "at": "$$root$$/$$user$$/projects/$$project$$/$$run$$"},
  {"step": "files", "of": "trunk", "into": "$$workspace$$/tree"}
]

6. The sandbox layer

What the process sees and may do. Every field is required, so the run says all of it, but workspace_at, added within version 1 (section 15). The node translates the fields into its confining program's arguments; on Linux, bwrap's. Every path is absolute once its values are in place, and the fields are applied in this table's order, so a later one is seen over an earlier one at the same place.

Field Type Meaning
uid, gid whole numbers the user and group the process runs as inside
capabilities list of names the Linux capabilities (not Fossil's) it keeps; empty keeps none
network true or false whether it shares the host's network; it never sees the host's processes, users, host name or message queues
own_session true or false whether it runs in a session of its own, so it cannot reach a terminal outside
ends_with_node true or false whether it ends when the node's process that watches the run ends; a stop, its wall and its program's end end it either way (section 10)
proc, dev, tmp paths where it sees its own /proc, its own /dev, and an empty directory for temporary files
workspace_at a path, optional where it sees its workspace, read-write: one path, such as /workspace, the same for every run, so a program that keeps what it allows per folder, as Claude keeps its trust, finds the same folder each run, while each run's workspace stays its own folder on the host; a host path within the workspace that read_only, read_write or homes names is seen at its place under it, and a checkout a checkout step made opens there, since it names its clone by the path from itself; an absolute path other than /, with no . or .. part and no / repeated or at its end, reading no values (section 2); refused by section 11, row 3, where nothing sets $$workspace$$, which only a directory step sets (section 5), so a run with none has no workspace to show; absent, the process sees its workspace where it is on the host
read_only list of paths host paths it sees read-only, each where it is on the host
read_write list of paths host paths it sees read-write, each where it is on the host; a path in both lists is read-write
homes list of paths host paths it sees read-write inside the workspace, each by its base name: /root/.claude as $$workspace$$/.claude
files list of {"at", "text"} files made for it alone: at a path, text what the file holds

Nothing else of the host is visible to it. A path in read_only, read_write or homes is refused (section 11, row 9) when, once links are followed, it is or holds or lies within one of the node's own files:

The node's own files But
its repository
the directory it keeps its runs' state in
its release root, which holds its programs and every run's workspace $$workspace$$ may be named in any of the three, $$release$$ in read_only, and $$releases$$/staged in read_write, so a run may stage without changing the programs that confine it
its file of passwords

7. The limits layer

Field Type Counted from Enforced, on Linux, by Past it
wall a whole number and s, m, h or d the launch; a session's first, when each delivery starts a process the node ending the process, and what it left (section 10) ended, budget
cpu whole seconds the launch, for each process the kernel, signal SIGXCPU ended, budget
processes a whole number at any moment the kernel, for the sandbox's own user a new process is not started
files a size (section 8, keep) each file a process writes the kernel, signal SIGXFSZ ended, budget
memory a size (section 8, keep) at any moment a cgroup: not yet, so a node refuses it ended, budget

started lists, in limits, the limits applied, as written.

8. The process layer

Field Type Default Meaning
user text required the Fossil user the process acts as; never a node's, or one holding admin or setup
command list of text required the program, then its arguments; a program named without / is found on env's PATH
env object of text empty the process's whole environment; the node adds nothing
cwd text $$workspace$$, or / with no workspace where it starts
manual true or false false false: the node starts it when the run reaches the node; true, a manual run: it waits until a user holding admin or setup starts it, with OCX's run tool
session true or false false false: it runs to completion; true: it is given what it subscribes to (section 9)
hears list of users, or ["*"] for every user empty whose posts and ticket changes a session is given
checkins list of branch names empty the branches whose check-ins a session is given
ends true or false false true: a session is also given each ended that counts (section 3) written where it is, such as the ended of a build it sent (section 9)
deliveries feed or start feed feed: each delivery goes to the running process; start: each starts a process of its own, the same command, one at a time
terminal true or false false the process runs on a terminal the node holds, which a person may join with OCX's attach
keep {"stderr", "stdout", "stdout_when"} 64K, 64K, failure, each key on its own how much of the end of STDERR and STDOUT the node keeps; STDERR always, STDOUT on failure (an exit but 0, or a signal) or always
verdict true or false false when its process has run and was not stopped, the node writes a ci verdict (verdicts) on $$checkout$$, the check-in the run took, as the node user, against the run: pass for an exit of 0, else fail and how it ended; refused on arrival when no step sets $$checkout$$ (row 3), and in a session (row 2)
shares a list of {"at", "as"} none when its process has run and was not stopped, the node shares each file at, a path within $$workspace$$ (row 9 refuses one outside it), as the unversioned file named as, as the node user: only a regular file, never a link nor one leading out of the workspace; only when the server lets the node user write unversioned files (y); then a reply where the run is names each file shared, with its hash, or why it was not. The run's sender sets the names; the build sets only the contents

A size is a whole number of bytes, or a whole number and K, M or G: 1024, 1024² or 1024³ bytes.

8.1 Streams

Stream Without a terminal With a terminal
STDIN the line {"run", "objective"}, run the run's hash and objective its text; then each delivery, one line each, for a session that feeds; otherwise closed the terminal: the first line and each delivery typed in, each ended by Enter (a carriage return), the line, its Enter and an end of input each typed once the program has read all typed before, never waiting on a program reading lines, and what a person joined types
STDOUT a pipe the node reads the terminal, which a person joined sees; its end kept (section 8.2) is what the terminal showed, without escape sequences or carriage returns
STDERR a pipe the node reads a pipe of its own, which the node copies to the terminal

A process a delivery starts reads the line {"run", "objective"}, then the delivery's line, then its input closes; with a terminal, each such process has a terminal of its own, its input closed by an end of input (Ctrl-D) typed.

8.2 Kept logs and where reports go

When The node writes As
a process exits, and a stream it keeps printed at least one byte $$workspace$$/stderr.log and $$workspace$$/stdout.log, when the run has a workspace files, rewritten for each process; one whose place the process took, as with a directory of that name, is not written
a process exits, and a stream it keeps printed at least one byte a reply where the run is: STDERR's and STDOUT's ends, each in a fenced block headed stderr or stdout, its exit, and what started the process, the run or a delivery, by its hash in full a plain post or ticket change, holding no OCX message
its process has run, was not stopped, and the run names shares each file as an unversioned file, then a reply naming each, with its hash, or why it was not shared unversioned files, and a plain post or ticket change, as the node user
its process has run, was not stopped, and the run asks a verdict a ci tag on $$checkout$$ a raw tag, as the node user
the run ends ended a reply in the run's thread, or a change to its ticket
a post or ticket change holding no OCX message, or holding a decision, from a user no run names as its node or process.user, reaches a running session's place and that session holds it back a reply to it beginning Not given to run HASH:, naming that run by its first ten hash digits, the first filter that held it back, and whom it hears a plain post or ticket change, as the node user

The node writes a run's kept logs, shares, verdict and ended in this table's order, and the server receives each before the node writes the next, so whoever is given or reads a run's ended finds the rest already there. Where a run is, a forum thread or a ticket, decides only where its reports and kept logs are written and what they point to.

9. Subscriptions

What a session is given: what arrives and passes every filter, checked in order.

# Filter Passes when
1 session the run's process.session is true, and its started counts (section 3)
2 after the server received it after the run
3 known the server has said who sent it
4 no request or report it holds no OCX message; or a question or a decision (questions); or it is an ended that counts (section 3) and the run's ends is true. A run, stop or started never passes
5 not its own its sender is not the run's process.user, nor a node user unless it is such an ended
6 where a post in the run's thread, or a change to its ticket, or a check-in on a branch in checkins
7 heard a check-in or such an ended; or a post or change whose sender is in hears, or hears is ["*"]
Arrives Line, a JSON object
a post post: its hash; from: its sender; text: its text
a ticket change change: its hash; ticket: the ticket; from; text: the text it adds
a check-in checkin: its hash; branch: its branch as Fossil's timeline shows it; from; comment: its comment
The node guarantees That is
whole, once, in order each delivery reaches the session whole, at most once, one at a time, in the server's order
only what arrives each watcher gives its session what complete pulls bring, after its run, including its own node user's ended once confirmed by the server; a missed wake loses no artifact
known senders first a failed receipt read gives nothing from that pull; the watcher reads the same pull again at the next completion number. An artifact the server says it has not received, such as one written straight into the clone, is given to no session, while those after it are given; if it reaches the server later, it is given then, in the server's order
bounded the inbox holds up to the node's bound; a delivery with no room for its whole line is skipped, without a log entry or retry; later deliveries that fit are still given
told when held back each session's watcher answers a person's post or change it holds back where its run is, naming that run's filter (section 8.2), without waiting to learn whether another session passed it; with no session there, no reply

An edit of a post is a post, and is given. To speak to one session alone, give it a thread or a ticket of its own. A stop ends only the run it names: every other session in its thread or on its ticket runs on, its filters as they were.

10. A run's life

The run's state is read from the repository, started and ended that count, and from whether the node has the run in its care (section 4).

State What arrives Next state The node writes or does
none a run that section 11 refuses ended ended, refused, and the note
none a run, manual false started takes it into its care, runs layers 2 to 4, writes started, then launches the command
none a manual run waiting nothing
waiting a user holding admin or setup starts it started as for a run that is not manual; a refusal then ends it refused
none or waiting a stop, the run not refused by rows 1 or 2 of section 11 ended ended, stopped, even where later rows would refuse it
none a run whose user has another run in the node's care; or one a user holding R alone sent, while another such run is in its care, as a node takes one at a time waiting nothing, until that run ends
started a delivery (section 9), deliveries feed started the delivery to its STDIN
started a delivery, deliveries start started a process for it, once the one before has exited; deliveries wait in the server's order
started a stop started ends the process: an unconfined one, which starts in a session of its own, with its process group and each group of that session holding what it left; one in a sandbox, with every process of the sandbox
started its process exits; not a session whose deliveries is start ended ends what it left: an unconfined one's, in each process group of its session, and a sandbox's, with its init, whether or not it ends with the node; once all of it has ended, kept logs, its workspace removed as section 5 says, then ended: exited, budget or stopped (table 12)
started a process exits, in a session whose deliveries is start started kept logs
started a stop, or past wall, in a session whose deliveries is start ended ends its process, kept logs, then ended, stopped or budget
started the node whose started counts (for one naming no clone, any node of its node user) finds it not in its care, or fails at a step of its own while the run is in its care ended ended, lost; for a failure of its own, once its process has ended, as for a stop, its note naming the failure
ended anything ended nothing

At start the daemon pulls and reads every message; each later complete pull adds new messages and has it decide unsettled runs. A watcher locks the run, decides, writes taken, posts started and launches only once Fossil confirms the post. The next watcher with taken and the lock free ends what the old lock names and posts lost; with ending it retries that report; with done it does nothing. A run with no state file whose ended the server's receipt page says this node user sent counts as done, so no watcher takes it, however long ago it ended; an ended another user sent, whatever its user card says, does not count. For a run naming a clone, only an ended whose clone starts with the run's counts (section 3).

11. Refusals

A run is checked against these in order, when it arrives and again when a person starts it; the first that holds ends it refused, with its note.

# Holds when Note names
1 no user holding admin or setup sent it; or one holding R, Fossil's capability letter that OCX gives the meaning "may send runs", sent it, and the node's owner, in the release the node runs ($$release$$), allows no such run, or it asks more of the host than the owner allows there: a process with no sandbox, or with a sandbox whose ends_with_node is false (what runs inside would outlive the node's process that watches it), the network or a capability the owner does not allow, a path to read that is neither in its own $$workspace$$ nor within one the owner lists, a path to write outside its own $$workspace$$ and every path the owner lists in r-senders.json, a home file other than one the owner lists, as written, a secret, a password step, a path with .. in it, even one the owner lists, a workspace other than one of its own (a directory step whose path does not end in $$run$$, since that step empties its path), a share whose name (as) does not hold $$run$$ (since a share replaces the unversioned file of its name), or a limit the owner caps left out or set above the cap which
2 a required field is missing, a field has the wrong type or form, or it has a key version 1 does not define (section 15); or a directory step's remove_on asks it without a sandbox whose ends_with_node is true, so a process of the run could outlive it the field or key, or that
3 it names a step kind section 5 does not have, a step whose needs are not met before it, or a value not set by then which
4 its user is the node's own, a node's (one named as the node of a run an admin or setup sent), or holds admin or setup which
5 its user works for another node: the node of the first run, in the server's order, that an admin or setup sent with that process.user, whatever became of it which node
6 it has a password step, and the node holds no password for its user the user
7 it has no sandbox, and the node runs nothing unconfined that
8 it asks a sandbox setting or a limit the node cannot provide which
9 a sandbox path reaches the node's own files (section 6), or a workspace step's path is not where section 5 allows the path
10 a workspace step fails the step, and why
11 it asks a verdict and the node user lacks Check-In (i) on the server that

12. ended

outcome When exit note
refused section 11 absent required: plain text, as for every note
stopped a stop ended it, or came first absent absent
exited its process exited by itself its code; 128 plus N for signal N absent
budget past a limit absent required: which limit
lost the node found it not in its care, or failed at a step of its own while it was absent required

13. Its words

Word Meaning here Instead of
layer one level of a run's message, table 1
envelope the fields every message carries, so any reader knows what it is
value something the node knows, named $$name$$ in a string macro
step one action of the workspace layer tool, which GLOSSARY.md gives OCX's commands
process a program running for a run the glossary's program, for what runs
in its care a run the node is preparing or running
works for a run user bound to one node by its first run
kept logs the ends of STDERR and STDOUT the node keeps and posts
manual run a run whose process waits until a person starts it when, hand
session a run whose process.session is true, given deliveries (section 9)
node user, run user the Fossil user OCX on a node writes as, which whoever has the command line there chooses; the Fossil user a run's process acts as the node's login, agent
clone optional; in a run, the repository, among those whose node uses the run's node user, that is to act on it, by its server-code or a prefix of it; in a report, the server-code of the repository whose node wrote it (section 3); a third use of the word, beside the step clone and the value $$clone$$ it sets (section 5)
question, decision an OCX message asking a user, which no node acts on; and anyone's words in answer to it, where it is; its sender's own decision, or that of whoever sent the run it names as asking it, confirms it answered and closes it; the maintainer's words (HISTORY.md decision 99) ask; answer, which the glossary gives to what a tool returns; reply, Fossil's word for any post answering another; response, which a decision was first written as and a reader still takes as one (questions, section 3)

14. Examples

Whole messages, each with what a node writes back, are protocol v1's examples.

15. Stability

Every message carries "ocx": 1. Version 1 grows only by addition, and a reader can tell what it does not know.

Change Within version 1
a new key, in any layer or kind, whose absence means what version 1 says without it allowed, as clone was (section 3; HISTORY.md decision 77), workspace_at (section 6; decision 86), and remove_on, beside which every node still reads remove, its older name, as remove_on, so a run holding remove asks what it did (section 5; decision 103)
a new message kind, a step kind (section 5), a value name (section 2), an outcome of ended, a refusal row allowed
renaming a key, changing its meaning, its type or its default, or removing it never: that is version 2
a node that does not know a key in a run refuses the run, naming the key (section 11, row 2), so nothing asked is silently dropped
a reader that does not know a key in a stop, started, ended, question or decision ignores the key
a reader that does not know a kind ignores the message, so a new kind asks nothing of an older node
a reader that does not know an outcome of ended takes the run as ended

A post may carry a block for each version; a reader takes the block of the newest version it knows (Fossil, senders and nodes, section 2). These rules bind version 1 from its promotion on 2026-10-13: no version 1 message was written on the server before it. Possible additions, none changing version 1, are rows of docs/backlog.md.