What protocol v1 rests on and does not repeat: Fossil as the medium, how a message is found in a post or a ticket change, who sent an artifact, verdicts, nodes, tools and versions. Protocol v1 itself, the messages and what a node does with them, is written as tables there. These sections were tested on fresh readers with the rest of the protocol (the battery's definition tests), and each ends with the values in README.md that justify it.
1. Fossil, the medium
Everything that must last is a Fossil artifact: posts, check-ins, tags, ticket changes. These are the evidence of what happened; a host's own log is for diagnosing problems, and may be lost. OCX treats Fossil as a network whose every message lasts:
- Sending is writing an artifact. Sync carries it to every clone; nothing is lost on the way.
- The daemon pulls whole clones. Fossil's chat wakes it, but no chat text is work. It publishes a completion number only after a successful pull; its watchers then read through that number and post through Fossil's pages (How a post reaches an agent).
- The answer is the acknowledgement, and it lasts. The record holds the answer; a run's local state also prevents a second launch while a report's outcome is unknown (protocol v1, section 4).
- Nothing is retracted. Stopping, correcting and replacing are new artifacts that point at old ones.
- Order comes from references and receipts, such as a reply naming the post it answers, never from clocks.
Each repository keeps a receipt for every artifact it receives: the time, the IP address it came from, and the Fossil user it was received from, by an authenticated sync, through its web page, or by a write on the repository itself, which has no address and records whatever user the writer names. So only a node, its tools and its host's administrators may write to a node's repository file. A repository does not copy its receipts to its clones, and a clone records everything it pulls as received from its own user, which holds setup there (Fossil 2.27, tried 2026-10-12): only the repository an artifact's writer sent it to knows who sent it. The user written inside an artifact is whatever its writer's clone said, and proves nothing by itself. Receipts are numbered as they arrive, and the artifacts of one receipt are taken by their record ids, as Fossil assigns them: that is the order this protocol means within a repository. Where readers on different repositories must agree, as on the latest verdict, the order is the server's (section 3).
Why: Demonstrability (artifacts are immutable, synced, readable later), Parsimony (Fossil carries artifacts and their receipts), Ax4 (every open request is found again after any failure).
2. Finding a message
An OCX message is a JSON block in a forum post of any mimetype, or in the
text a ticket change adds to its ticket's comment (its icomment field): a
fenced code block, its fences paired as Markdown (CommonMark) pairs them,
opened by a line that is exactly
```json or a bare ```.
Other text may come before or after it, and nodes ignore it; keep it short. A
block holds an OCX message when its JSON repeats no key and has an ocx key,
a whole number, naming a version the reader knows, whether or not the message
is valid or acted on. A post's or change's message is the first such block
carrying the newest protocol version the reader knows, read from that
version of the post: an artifact never changes, so an edit holding a message
is a new message asking again, named by the edit's own hash, and the request
it edits stands until it is stopped (the maintainer's review, 2026-10-05,
10e85df270). Several runs may share a thread or a ticket; each is answered on its
own. What each kind of message holds, and who may send it, is
protocol v1, section 3.
OCX's create, stop and post tag the artifacts they write with the raw
Fossil tag ocx-message: the kind, and for a stop the full run hash after a
space. The watcher posts reports through Fossil's pages, without tags. Neither
the daemon, watcher nor status reads runs by tags: each reads the messages
through the completed pull, irrespective of missing, changed or cancelled tags.
questions and post --decision still read every post or ticket change the
tag was ever put on, whatever its value or cancellation. Its message says
what it is; a tag confers no authority. tag_messages back-fills missing or
wrong tags in one control artifact (tools).
Whether to keep this tagging is open (backlog).
Why: Clarity (one envelope), Loose Coupling (anything that writes a post or a ticket change can send a message), Parsimony (no tag repair in run-work).
3. Who sent it, and who may do what
The server is the repository people and nodes send their artifacts to, and that
every clone syncs with; a project has one. Who sent an artifact is the user
the server received it from, by its receipt (section 1), with that
user's capabilities on the server, inherited ones included, when the node
decides.
Every node asks the server's OCX receipt page over HTTPS, signed in as the
node user, about every artifact it decides by, including its reports. The
page also answers the server's order. The sender never changes, so it may
be kept. An artifact not received by the server allows nothing.
If the server pulls from another repository at all, it pulls with Fossil's
--user naming a user of its own who holds no capability this protocol asks
for, so whatever it pulls allows nothing (Fossil 2.27, tried 2026-10-12). In a
clone's checkout, a tool that writes as a person writes in that clone and
pushes it, so the server receives it from that person.
So that the server gets every artifact from its writer, all a run writes
reaches it as the run's user, from the run's own clone, synced by its
process; all a node writes reaches it as the
node user, which never holds admin or setup; and OCX's post writes no run,
stop or report: from its own fields it writes only a question or a
decision, and it refuses text holding any OCX message. On a node, only the node, OCX's tools and the host's
administrators write to the repository itself, each tool naming the user it
signed in.
- A
runor astopmay be sent by any user holding admin or setup. The node acts on one only if a user holding admin or setup sent it, or a user holdingR: a run within what the node's owner allows, a stop for a run it sent (protocol v1, section 11, row 1). Fossil keeps a letter it does not define, soRis granted on the Users page like any other, andcreategives it to each user it makes for a run. startedandendedare written only by the node, as its node user, and count only if that user sent them; for a run naming a clone, only if theirclone, the node's word, starts with the run's (protocol v1, section 3).- A
decisioncounts from whoever sent it, written where its question is, and closes the question only if the question's sender sent it (questions). - A post or ticket change is given to a session only if a user in the run's
hearssent it, or it is anendedthat counts and the run'sendsis true; a check-in, only if the run's own user did not send it. Astopends only the run it names, never another session where it is. - Which verdicts count, section 4 says.
Who may read threads, tickets and check-ins is Fossil's own business, by its capabilities; OCX adds nothing to it.
Why: Demonstrability (every act is attributed by the repository its writer sent it to), Ax1 (what each user may cause is fixed before it acts, and an artifact no receipt vouches for allows nothing), Parsimony (Fossil's receipts on the server are the one record of who sent what; HISTORY.md decision 37).
4. Verdicts
A verdict is a tag on a check-in recording a judgement of it, pass or fail
with a reason and the objective it was judged against: audited for a person's
or an agent's, ci for a program's. Anyone may write one, with OCX's audit
or with fossil tag add, and it needs no run; but a verdict on your own
check-in never counts, nor an audited verdict that a node user sent.
Verdicts says how to write and count them.
Why: Ax2 (always judged against a named objective), Loose Coupling (a verdict needs no run, and any program may read one).
5. Nodes
A node is a clone running OCX on an agent machine, not a user. Whoever has
its command line chooses the node user. Its capabilities on the server are
the most OCX can do. Runs name that user in node and optionally the clone's
server-code in clone (protocol v1, section 3).
Reports are posted through Fossil's pages as that user, each naming the
clone's server-code. The server vouches for the sender; the report's clone
is that sender's word. There is no node on the served repository.
Several clones may share a node user when the runs for it name their clone. A run
naming no clone may be taken by any node of that user, and a started
naming none, as one written before the key, may be answered lost by any of
them: a node user shared by clones should get only runs that name one, and a
machine joins a node user only once every run for it naming no clone has ended.
Every node of a user runs a release that knows clone before runs for it
name one, since an older one refuses them. The machine that joins takes a
copy of the first's file of passwords, readable by its owner alone, and
never registers again: approve would set a new password, and the first's
would stop working. Fossil gives a clone made over HTTP a
server-code of its own; a copy of the file, or a clone made from the file,
keeps the one it had, so two nodes would share it: clone a node's repository,
never copy it (GLOSSARY.md, "Server-code"). A clone made
again gets a new server-code, and no node answers a run naming the old one,
not even lost.
The node user never holds admin or setup. The daemon requires goC4w:
clone, read code, chat and unmoderated forum and ticket writing; it also
refuses to serve when ticket changes await moderation. Verdicts need i and
shares need y. An administrator approves the user through registration
(USAGE.md, "Run a node"). A run taking password needs its
user's password in the node's credentials file. The node sets no password
on the server. A run user works for the node user named by its first run,
in server order, from admin or setup; a run for another node is refused.
One run at a time for each process user, and one from a user holding R
alone, is kept in care (protocol v1).
One daemon per machine serves the projects named by ROOT/node/credentials/NAME.json, using ROOT/node/clones/NAME.fossil for each clone. The file holds the server URL; older files without one use the clone's named ocx-server remote. A project with neither is reported and skipped. At each
start it pulls first and reads every message once. After each complete pull
it adds new messages and reconsiders every unsettled run. Decisions use
server receipts, under the node's lock; every act other than waiting or
nothing is handed to a watcher, which decides again under the run's lock.
After a failed pull the daemon publishes no completion and decides nothing. A failed
pull, chat request or receipt read ends the daemon; its service manager
starts it again. No hook schedules work; each complete pull has the daemon
decide open runs again.
Each watcher keeps taken, ending or done beside its run's lock, changing
the state by rename only while holding that lock. The next watcher finding
taken and the run lock free takes the lock, ends what the old lock names
and posts lost, never relaunching; ending holds the exact report to retry,
and done settles it. A run without a state file counts as done when the server's
receipt page says the node user sent its ended; another user's ended does not count.
For a run naming a clone, only an ended whose clone starts with the run's
counts (protocol v1, section 3).
A run left waiting has no state and is reconsidered on later daemon rounds.
Care lasts from just before workspace creation until the ended is stored.
A watcher owns reports, kept logs, session deliveries and stops. It reads
only through completion numbers published by rename after whole pulls.
An unknown post outcome is checked at the next completion: a matching reply
from the node user counts as stored; only if absent is it tried again.
Retries hold only the run's lock, so other watchers and the daemon can decide.
The watcher writes no tags and hands on no run. Its confirmed post generates
a Fossil chat event; the daemon's next round reconsiders waiting runs.
Verdicts and shares alone are written into the node's clone and sent using
fossil push --once ocx-server and fossil uv sync ocx-server, leaving the
default remote unchanged (Fossil facts).
Before posting lost, the next watcher checks the boot and process starts
recorded in the old run lock and ends the processes it names. It ends the run's
process groups and sandboxes, with the remaining limits described in
failure cases 34 and 36. A watcher's own
failure after started ends the process and reports lost; if posting
fails, the exact ending remains for the next watcher to post (case 33).
Why: Ax4 (open runs are found again), Parsimony (one daemon, a watcher per run, Fossil's receipts and server-code), Loose Coupling (outgoing requests alone from agent machines).
6. Tools
OCX's tools write these artifacts, and perform runs by hand, as a convenience,
never a requirement: the same artifacts written by other means, such as a
verdict with fossil tag add, count the same.
OCX's tools for protocol v1 lists them.
Why: Loose Coupling (the artifacts are the standard, and no tool is required).
7. Versions
Every OCX message carries "ocx": 1. What may be added to version 1, and what
never changes, is protocol v1, section 15. A
reader takes the block of the newest version it knows
(section 2), so one post may carry a block for each version.
A message whose kind is response, the name a decision was first written
under, tagged so, is read as a decision
(questions, section 3).
Why: Loose Coupling (other programs can rely on the formats), Clarity (meanings cannot drift, and nothing asked is silently dropped).