You are reviewing a protocol specification for the first time. Read only the text below. Do not use any tools, read any files, or run anything.
Context: OCX extends Fossil, a version-control system whose repository also holds forum threads and tickets. Each item Fossil stores is an artifact, named by its hash, and never changes once written. Every person and program acts as a Fossil user.
Your task: find every place where the protocol below contradicts itself, leaves a behavior that two careful implementers could build differently, or asks for something that cannot be implemented as written. For each, quote the words at fault, say what goes wrong in a concrete case, and suggest the smallest fix. Do not report matters of style. If you find nothing in an area, say so. End with the three findings you consider most serious, in order.
# The protocol
# OCX protocol v1
OCX extends Fossil, so people and programs can ask a node to run work and read
what came of it. This document is the protocol. [GLOSSARY.md](../GLOSSARY.md)
defines its words and points here. Each section was tested, a few at a time,
on readers who knew nothing else ([docs/battery/defs](battery/defs/README.md)).
Each ends with the values in [README.md](../README.md) that justify it.
[Failures, worked through](protocol-v1-failures.md) shows what the rules make
of crashes, races and refusals, case by case.
## <a id="fossil"></a>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 hook is a signal; the repository is the truth.** Fossil runs the
after-receive hook from its backoffice after artifacts arrive. A write made
on the repository itself starts no backoffice, so whatever writes there
runs it afterwards. A node that missed a signal reads its repository again.
- **The answer is the acknowledgement, and it lasts.** A request that has its
answer is never acted on again, and what is still open is read from the
repository, never kept anywhere else.
- **Nothing is retracted.** Stopping, correcting and replacing are new
artifacts that point at old ones.
- **Order comes from references,** such as a reply naming the post it answers,
not from clocks.
Each repository keeps a receipt for every artifact it receives: the time, the
IP address, and the Fossil user it was received from, by an authenticated sync,
through its web page, or by a write on the repository itself, for which it
records whatever user the writer names. 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 stored in turn: that is the order this protocol means.
Why: Demonstrability (artifacts are immutable, synced, readable later),
Parsimony (no second store, no queue, no retries), Ax4 (every open request is
found again after any failure).
## <a id="messages"></a>2. OCX messages
An OCX message is a JSON block in a forum post or a ticket change, fenced in
Markdown: write it with a line ```` ```json ````, the JSON, and a line
```` ``` ````; a reader also takes a bare ```` ``` ```` fence. Other text may
come before or after it, and nodes ignore it; keep it short. An artifact holds
one OCX message: a reader takes the first fenced block whose JSON has an `ocx`
key, and ignores any other. Every message has `"ocx": 1`, a `kind`, and `node`:
the one node it is for, named by its repository's server-code (40 hex digits,
matched exactly). There are four kinds:
- **Requests,** `run` and `stop`. A node acts on one only when it is for that
node and the user it was received from holds admin or setup.
- **Reports,** `started` and `ended`. Only the node a run is for writes them,
as its own Fossil user.
Every message but a `run` names the run it is about in its field `run`: the
run's Fossil hash, in full, in hex. It is written where that run is, as a reply
in its thread or as a change to its ticket. Several runs may share a thread or
a ticket; each is answered on its own. A message never changes: an edit of one
is ignored, so to change a request, stop it and send another.
Why: Clarity (one envelope, four kinds), Loose Coupling (anything that can
write a post or a ticket change can send one), Ax1 (only those who hold admin
or setup can ask a node for anything).
## <a id="runs"></a>3. Runs
A run is the OCX message of kind `run`: a request that a node run one program.
Its fields, besides `ocx`, `kind` and `node`, are these, and those that
[runners](#runners), [subscriptions](#subscriptions) and [limits](#limits) add:
```json
{"ocx": 1, "kind": "run", "node": "6cbe285e95a3a124176569ee9243f2a4e9ecdc20",
"objective": "Build check-in abc123 for the board; it passes when the build succeeds.",
"runner": "command", "command": ["make", "board"], "checkout": "abc123",
"user": "builder", "session": false, "limits": {"wall": "30m"}}
```
- `objective` (text): what the job is for, and what counts as passing. Only a
run has one.
- `runner`: how the node runs the program.
- `user` (text): the Fossil user the program acts as.
- `checkout`: the check-in its program works in ([section 5](#runners)).
- `session` (true or false): how long the program runs.
- `false`, the default for every runner but `ci`: to completion. It
starts, does its work and exits, like a build or an on-target test.
- `true`: as a session. It stays running and is given what its run
subscribes to, until it exits, is stopped, or reaches its limits.
- `limits`: what the program may use.
- `manual` (true or false, default false): whether the node waits for a person
to start the run by hand, with OCX's `run` tool, instead of starting it when
it arrives.
Why: Ax1 (the whole boundary is fixed and readable before anything runs),
Clarity (one shape for a build, a CI check and an agent).
## <a id="reports"></a>4. Started and ended
The node's reports on a run, written where the run is.
```json
{"ocx": 1, "kind": "ended", "node": "6cbe…", "run": "FULL-HASH-OF-THE-RUN", "outcome": "exited", "exit": 0}
```
A node first prepares a run: it checks its runner, its user and its limits,
checks out its check-in and makes its sandbox. Only then does it write
`started`, and only once that is written does it launch the program.
- `started`: the run's program is launched, or about to be. It names the
check-in checked out, in full (`checkout`, which a `ci` run has none of),
and every limit applied (`limits`, [section 7](#limits)). A run gets at most
one, a session too, however many posts it is given, so its program is
launched at most once.
- `ended`: the run's program is not running, and never will be again. A run
gets exactly one, and its `outcome` says why:
- `refused`: the node did not start it: it cannot give it what it asks, or
preparing it failed. `note` says which, in words; no `started` was
written;
- `exited`: the program exited by itself; `exit` holds its exit code, a
whole number. A program its sandbox cannot launch exits non-zero;
- `stopped`: a `stop` ended it, or came before it started;
- `budget`: it reached a limit, which `note` names in words;
- `lost`: after its `started`, the node went down or lost track of the
program, which may have run in part, or not at all; the run is never
launched again ([section 10](#nodes)).
Of a run's stops, the node acts only on the first it may act on at all
([section 2](#messages)). Received before the run's `started`, it ends the run
`stopped`, and the run never starts. Received while it runs, it ends the
program, and the run ends `stopped`, unless it ended otherwise first. Any later
`stop`, and any received after the run's `ended`, does nothing. A run with a
`started` and no `ended` is running; a run with an `ended` is over. So a run's
OCX messages come in one of these orders, and later stops, which do nothing,
may follow any of them:
- `run`, `ended` (refused);
- `run`, `stop`, `ended` (stopped before it started);
- `run`, `started`, `ended`;
- `run`, `started`, `stop`, `ended`.
Why: Demonstrability (every request is answered where it was asked, even a
lost one), Ax4 (no run is left open), Ax1 (a reader always knows whether a run
is running, and a program never runs twice for one run).
## <a id="runners"></a>5. Runners
A runner is how a node runs a run's program; the run's `node` says where. A
node offers some runners, and OCX's `status` lists them; a node answers a run
whose runner it does not offer with `ended`, `refused`. Every runner but `ci`
needs the run's `checkout`, and runs its program in a fresh checkout of it: a
check-in's hash, or a branch's name, meaning the check-in Fossil resolves the
name to when the node checks it out, which `started` names in full. The
runners:
- **`command`** runs the program named in the run's `command` (a list: the
program, then its arguments) once, to completion: a build, a test, a check of
one check-in.
- **`ci`** runs it for every check-in. It is always a session, so a `ci` run
that says `session: false` is refused, and it takes no `checkout`: for each
check-in on a branch in the run's `checkins`, it runs the run's `command`
once, to completion, in a fresh checkout of that check-in, and records the
result as a `ci` [verdict](#verdicts) on it, `pass` if the command exits 0,
and `fail` if not.
- **An agent runner,** such as `claude` or `codex`, runs that agent program,
usually as a session. Its program, the home files its runs share and its
environment come from the node's wiki page `sandbox/NAME`, written by a user
holding admin or setup. The run's `remote` (true or false, default false)
says whether people may also reach the session through the agent's own
remote control, as Claude's offers; what is said that way leaves no artifact.
For both `command` and `ci`, `command` is what is run, and the run's
`objective` says in words what counts as passing. So to run something once, use
`command`; to run it for every check-in on some branches, use `ci`. Special
hardware is a node, not a runner: to test each check-in on `dev` on a board
attached to a host, send a `ci` run whose `node` is that host's, such as
`{"runner": "ci", "command": ["make", "board-test"], "checkins": ["dev"], …}`.
Why: Loose Coupling (a runner is replaceable, and hardware is chosen by node),
Parsimony (three kinds of runner cover builds, CI and agents).
## <a id="subscriptions"></a>6. Subscriptions
What a session is given while it runs, set by two fields of its run, both
optional and empty by default:
- `checkins`: branch names. Each check-in that arrives on one of them, unless
it was received from the run's own user, is given as
`Check-in HASH on BRANCH by USER`.
- `hears`: Fossil users. Each post one of them makes in the run's thread, or
each change one of them makes to the run's ticket, is given as
`Post HASH from USER:` or `Change HASH to TICKET from USER:`, followed by its
text as written, which may run over several lines.
USER is the user the artifact was received from. A session is first given
`Run HASH: OBJECTIVE`, then each artifact it subscribes to that arrives after
its `started`. The node guarantees each running session these:
- **Whole, once, in order:** each delivery reaches the program whole, at most
once, one at a time, in the order the node's repository received them.
- **A frame it can read:** an agent is given each delivery as one turn of its
conversation, once its last turn has ended. Any other program reads them on
its standard input, one JSON object to a line: first
`{"run": HASH, "objective": TEXT}`, then each
`{"post": HASH, "from": USER, "text": TEXT}`,
`{"change": HASH, "ticket": TICKET, "from": USER, "text": TEXT}` or
`{"checkin": HASH, "branch": BRANCH, "from": USER}`; what it has not read
waits there. `ci` takes its check-ins itself ([section 5](#runners)).
- **Nothing missed:** after a missed signal, the node gives each running
session what its repository received since the last artifact it gave that
session.
The node keeps which artifact it gave a session last, only while it runs: that
is all its delivery state. If the node loses it while the program still runs,
it ends the program, and the run ends `lost`. Nothing is given to, or kept for,
a run that is not running, and a run that is not a session is given nothing; a
session may read its thread itself for anything older. A run's own posts, such
as its questions, answers and progress, are plain posts in its thread, with no
OCX message.
Why: Ax1 (a session acts only on what its run names, and nothing polls),
Parsimony (one field per kind of delivery, one mark of delivery state), Loose
Coupling (any program that reads lines of JSON can be a session).
## <a id="limits"></a>7. Limits
What a node holds a run's program to, in the run's `limits`. Each is optional;
a limit left out takes the node's own default, and the node's `started` report
lists, in a `limits` of the same shape, every limit it applies. A node that
cannot enforce a limit a run asks for answers it `ended`, `refused`.
- **Time and resources:** `wall` (a whole number followed by `s`, `m`, `h` or
`d`), `cpu` (a whole number of seconds, in total), `memory` (a whole number
of bytes, or one followed by `K`, `M` or `G`, for 1024, 1024² or 1024³
bytes), `processes` (at once). Reaching one ends the run, `budget`.
- **What it sees:** `network` (true or false), and `read_only` and
`read_write` (lists of absolute host paths). Besides these it sees only its
checkout, its program and the host's system files, which it may only read.
- **What it may write in Fossil:**
- `no_password` (true or false): the program holds no password for its user.
Its posts, verdicts and check-ins go through OCX's tools, which the node
runs as its user.
- `branch` (a branch name): its check-ins go on that branch only.
- `own_thread` (true or false): it posts only in its run's thread or ticket.
Only OCX's tools can enforce `branch` and `own_thread`, so setting
`branch`, or `own_thread` to true, also means `no_password`: a run that
sets either and `no_password` to false is refused, and `own_thread` false
asks for nothing. A tool refuses a write these forbid, and the run goes on.
Without `no_password`, the program's Fossil capabilities are its only limit
in Fossil, and Fossil's `i` admits any write.
Why: Ax1 (the boundary holds, or the run does not start), Parsimony (no limit
unless asked), Loose Coupling (a node that enforces few limits still runs the
runs that ask for no more).
## <a id="permissions"></a>8. Who may do what
OCX decides by the user an artifact was received from, as the node's
repository records it ([section 1](#fossil)). On a node, only OCX's tools write
to the repository itself, each naming the user it signed in.
- A `run` or a `stop` may be sent by any user holding admin or setup. The node
acts on one only if it was received from a user holding admin or setup.
- `started` and `ended` are written only by the node, as its own user.
- A post or ticket change is given to a session only if it was received from a
user in the run's `hears`; a check-in, only if it was not received from the
run's own user.
- A verdict counts only if it was received from a user who holds check-in
(`i`) and who neither sent the check-in nor is the user named in it. No one's
verdict on their own check-in counts.
Who may read threads, tickets and check-ins is Fossil's own business, by its
capabilities; OCX adds nothing to it.
**Open:** a clone records what it pulls as received from its own user
([section 1](#fossil)), so how a node on a clone, such as a build host's, learns
who wrote what it pulled is not yet settled. Until it is, a node neither acts
on nor gives a session anything its repository pulled, and acts only on what
was written to its repository directly; a reader on a clone counts verdicts as
unverified ([section 9](#verdicts)).
Why: Demonstrability (every act is attributed by the repository that received
it), Ax1 (what each user may cause is fixed before it acts, and a receipt that
cannot say who wrote an artifact allows nothing).
## <a id="verdicts"></a>9. Verdicts
A verdict is a tag on a check-in that records a judgement of it: `audited` for
one made by a person or an agent, `ci` for a program's check. Write it as a raw
tag (Fossil's `--raw`), so it never becomes a name for the check-in. A reader
reads older `sym-audited` and `sym-ci` tags as verdicts too. Its value is
`pass: ` or `fail: `, then the reason on one line, then ` (against X)`, where X
names the objective it was judged against: a run's full hash, or the path of a
file that states the objective, such as `PLAN.md`, read as it stands in the
check-in judged. Two examples:
- a reviewer's judgement: tag `audited`, value
`fail: the new command has no tests (against PLAN.md)`;
- a `ci` run's check: tag `ci`, value `fail: 2 tests failed (against RUNHASH)`.
Anyone may write an `audited` verdict, with OCX's `audit` tool or with Fossil's
own `fossil tag add`; it needs no run. But a verdict on your own check-in never
counts: the author of a check-in cannot pass it. A `ci` run writes a `ci`
verdict, as the run's user, on each check-in it checks, against the run itself.
**Reading verdicts.** A verdict counts when it was received from a user who may
add one ([section 8](#permissions)). A run named in it must be about the
check-in: its `checkout` is that check-in, or its `checkins` include the
check-in's branch. Fossil shows only a tag's latest value on a check-in, but
keeps every tag change, with its user, in its `event` table (type `g`). So a
reader takes each user's latest verdict from there, and every user's latest
verdict counts. How to combine them is the reader's choice: any fail fails, a
majority, one named reviewer. OCX's `queue` and `activity`, CI checks and merge
gates are all readers. A reader on the repository a verdict was sent to counts
by whom it was received from; a reader on a clone can count only by the user
named in each tag change, and says its count is unverified.
Why: Ax2 (always judged against a named objective), Demonstrability
(attributed and kept), Loose Coupling (a verdict needs no run, and any program
may read one).
## <a id="nodes"></a>10. Nodes
A node is a repository with OCX's hook installed, on the host that runs its
work, named by the repository's server-code. Its repository is made new or
cloned over HTTP: a copy, or a clone made from a file, keeps the original's
server-code, and two repositories that share one would both act on the same
runs. A node writes its reports as its node user, a Fossil user of its own, and
OCX's `status` lists the runners and limits it offers.
A node learns of an artifact only when the artifact reaches its repository.
On the repository's server, that is as soon as it arrives. A node on any other
host, such as a build host with its own clone, learns of a run only when it
syncs its clone, how often being its own choice, and acts on it only once it
knows who wrote it ([section 8](#permissions)). Its reports reach the
server the next time it syncs. Fossil tells the node of each arrival through
its after-receive hook, run from Fossil's backoffice. A write made on the
repository itself starts no backoffice, so OCX's tools run it after writing,
and a run written with OCX's `create` on the node is acted on at once.
When a node starts, and whenever it may have missed a signal, it reads its
repository for runs that name it and have no `ended`:
- a run with a `started` whose program is no longer running, or no longer
given its deliveries ([section 6](#subscriptions)), it answers `ended`,
`lost`, ending the program first if it still runs;
- a run with no `started` it acts on as if it had just arrived: it starts it,
refuses it, or ends it `stopped` if a `stop` for it has arrived, and a run
whose `manual` is true goes on waiting for a person.
Why: Ax4 (open runs are found again from the repository), Parsimony (the
repository is the only state), Loose Coupling (a build host joins by syncing a
clone).
## <a id="tools"></a>11. Tools
OCX's tools write these artifacts, and perform runs by hand, for people and
programs, as a convenience, never a requirement: anyone may write the same
artifacts by other means, such as a verdict with `fossil tag add` or a run as
a forum post, and they count exactly the same. [OCX's tools](protocol-v1-tools.md)
are `create`, which writes a run; `run`, which performs one by hand, starting a
`manual` run or trying a definition without writing anything; and `attach`,
`stop`, `post`, `audit`, `push` and `status`.
Why: Loose Coupling (the artifacts are the standard, and no tool is required).
## <a id="versions"></a>12. Versions
Every OCX message carries `"ocx": 1`. Version 1 may gain new keys and stay
version 1, and a key never changes its meaning or its name. An unknown key in
`limits` may be a limit the node cannot enforce, so the node answers the run
`ended`, `refused`. An unknown key anywhere else is ignored: the message is
acted on as if the key were not there. A node also refuses a run whose runner
it does not offer, and ignores a message whose `kind` it does not know. A
change that breaks these rules makes version 2, and a version 1 node ignores
version 2 messages.
Why: Loose Coupling (other programs can rely on the formats), Ax4 (meanings
cannot drift, and no limit is silently dropped).
# OCX's tools for protocol v1
[Protocol v1](protocol-v1.md) is the standard: its artifacts count however
they are written. OCX's tools write them, and perform runs by hand, for people
and programs, as a convenience, never a requirement. Anyone may write the same
artifacts by other means, such as a verdict with `fossil tag add` or a run as a
forum post, and they count exactly the same.
## <a id="tools"></a>The tools
- `create` writes a `run`: in a new thread, in reply to a post (`to`), or on a
ticket (`ticket`).
- `run` performs a run by hand, on the node, from its definition: the same
checkout, sandbox and limits the node would give it. From the command line it
runs in your shell, its program's terminal yours until it ends, or until you
leave a session. From the web page it starts detached, for `attach` to join.
It has two uses:
- starting a run whose `manual` is true, which the node does not start
itself: only a user holding admin or setup may, and the node reports it as
usual, `started` and then `ended`. A run already `started` or `ended` is
refused, so however many start one at once, it starts once;
- a trial, `run --trial`: it performs any run's definition, or one not yet
sent, and writes nothing to Fossil, no `started` and no `ended`; it prints
the outcome instead.
- `attach` joins a running session's terminal from a shell at the node, and
leaves it running when you go; each attachment leaves a post in the run's
thread.
- `stop` writes a `stop` for a run.
- `post` writes a plain post in reply to a post.
- `audit` writes a verdict.
- `push` copies a run's check-ins from its clone to the node's repository, for
a run whose limits ask for `no_password`.
- `status` writes nothing: it says who and where you are, and what this node
offers.
## <a id="answers"></a>Where they are offered, and what they answer
The command line offers every tool; the web page and MCP offer every tool but
`attach`, which needs a terminal at the node. Each tool checks its input before
anything happens, and naming a field it does not know is refused. Every tool
takes `dry_run`, which checks everything and writes nothing, and every tool
answers in one shape: `{"tool", "dry_run", "log", "result", "stopped"}`.
`result` is the tool's output, once it has run to the end:
- for `create`, `stop`, `post` and `audit`, the hash of what it wrote;
- for `push`, the hashes of the check-ins it copied;
- for `run`, the outcome as `ended` gives it, a trial's too;
- for `attach`, the hash of the post it left;
- for `status`, what it says.
`stopped` says why it did not run to the end, naming the field at fault.
Why: Clarity (typed input, one answer shape), Loose Coupling (the same tools
from every place, and none required), Demonstrability (the dry run, and the
trial).
# OCX protocol v1: failures, worked through
Each case is a failure or a race, and what [protocol v1](protocol-v1.md) says
comes of it, artifact by artifact, with the section that decides it. Together
they pin down what the rules might otherwise leave to a reader, as codex's
review of c28028fbd5 asked (stone, 2026-10-12).
## <a id="crash"></a>Crashes
1. **The node goes down before `started`.** It had checked the run out, and
written nothing. When it starts again it finds the run with no `started`,
and acts on it as if it had just arrived: it prepares it again, writes
`started` and launches it. The program had never been launched, so it is
launched once ([4](protocol-v1.md#reports), [10](protocol-v1.md#nodes)).
2. **The node goes down after `started`,** whether before launching the
program or while it ran. When it starts again it finds the run with a
`started`, no `ended`, and no program running, and writes `ended`, `lost`.
It never launches the run again, so no program runs twice for one run, and
`lost` says it may have run in part, or not at all ([4](protocol-v1.md#reports)).
3. **The node loses a session's delivery mark** while its program runs on.
The node ends the program and writes `ended`, `lost`; it does not guess
where delivery stopped ([6](protocol-v1.md#subscriptions)).
4. **A manual run is waiting when the node goes down.** When it starts again,
the run has no `started`, and its `manual` is true: it goes on waiting for a
person ([10](protocol-v1.md#nodes)).
## <a id="starting"></a>Starting
5. **The checkout fails.** A run names `"checkout": "release-9"`, a branch the
node's repository does not have, perhaps not yet synced. The node writes
`ended`, `refused`, with a `note` saying so, and no `started`. To try again,
send a new run ([4](protocol-v1.md#reports)).
6. **The program cannot be launched.** `command` names `make`, which the
sandbox does not have. `started` is written, the sandbox exits non-zero,
and the run ends `exited` with that code ([4](protocol-v1.md#reports)).
7. **A branch moves after the run starts.** A run names `"checkout": "dev"`.
The node resolves `dev` to a check-in, writes `started` naming it in full,
and launches. A check-in landing on `dev` a minute later changes nothing:
the program works on the check-in its `started` names ([5](protocol-v1.md#runners)).
8. **Two people start one manual run at once,** each with OCX's `run` tool at
the node. One `started` is written and one program launched; the other
tool finds the `started` and is refused, writing nothing
([tools](protocol-v1-tools.md#tools)).
## <a id="stopping"></a>Stopping
9. **Stopped before it starts.** stone sends a run with `manual` true, then a
`stop` for it. The node writes `ended`, `stopped`, and no `started`: the
thread holds `run`, `stop`, `ended`. A later `run` tool on it is refused,
since it has an `ended` ([4](protocol-v1.md#reports)).
10. **Two stops.** While a run runs, two admins each send a `stop`. The node
acts on the first it received: it ends the program and writes `ended`,
`stopped`. The second does nothing, and nothing answers it.
11. **The program exits as a `stop` arrives.** Whichever the node meets first
decides: `ended`, `exited`, the stop then doing nothing; or `ended`,
`stopped`. Never both, since a run gets one `ended`.
## <a id="delivery"></a>Delivery
12. **A busy session.** Three posts from users in its `hears` arrive while
its agent is in a turn. They wait; when the turn ends the agent is given
the first as one turn, then the second and the third, each once the last
turn has ended. A program that is not an agent finds them in its standard
input, one JSON object to a line, and reads them when it is ready
([6](protocol-v1.md#subscriptions)).
13. **A missed signal.** The hook does not run for a post a session should be
given. The next time the node reads its repository, it gives the session
everything received since the last artifact it gave it, in the order
received: the missed post, and nothing twice.
## <a id="refusing"></a>Refusing
14. **Contradictory limits.** A run asks `{"own_thread": true, "no_password": false}`.
Only OCX's tools can enforce `own_thread`, which means `no_password`, so
the run asks for both and neither: `ended`, `refused`, with a note. A run
asking `{"own_thread": false}` asks for nothing ([7](protocol-v1.md#limits)).
15. **`ci` told it is not a session.** A run says `"runner": "ci"` and
`"session": false`: `ended`, `refused`, since `ci` is always a session
([5](protocol-v1.md#runners)).
16. **A forged run.** A user holding only check-in (`i`) pushes a forum post
whose U card says `stone`, holding a run for the server's node. The
server's repository received it from that user, not from stone, so the
node does not act on it. A node on a clone pulled it, received from its
own user, and cannot tell who wrote it: until that is settled, it acts on
nothing it pulled ([8](protocol-v1.md#permissions)).