Aether's conflict radar identifies active runs that edit the same files. When coordination is enabled, each run also gets a small, durable channel for communicating with radar-authorized runs; a current mission assignment may add server-authorized peers before any file overlap exists. The channel is advisory: it does not lock files, pause work, or decide which change wins.
Candidate verification and delivery is a separate authenticated service
described in integration.md. Coordination records remain
observations/evidence; they do not constitute an accepted submission, a
frozen candidate revision, or a landed upstream change. The mission-policy
adapter exposes a narrow agent integration CLI only to the current integrator:
prepare, show, verify, request-delivery, and deliver. Human review
remains the approval boundary; ordinary and worker runs do not receive these
commands.
Run-mounted surfaces
The server keeps coordination data below its private data directory:
<data>/coord/ 0700 coordination root
<data>/coord/<run-id>/ 0755 one run's coordination directory
<data>/coord/<run-id>/coord3.sock 0666 the v3 coordination socket
<data>/coord/<run-id>/co-authors 0444 server-generated commit trailers
The coordination mount contains no MCP configuration file. For the optional
manual bridge, use the /tmp configuration example in
the MCP bridge guide.
Inside a container, the run directory appears at /run/aether. Only
coord3.sock is served. A socket from an older wire version is not rebound;
affected runs must be relaunched with the current server.
The server also mounts one verified, read-only staged binary at both of these paths:
/opt/aether/aether-server
/usr/local/bin/aether-internal
The first path serves the hidden MCP entry point and the existing harness lifecycle hook. The second path is the agent-facing coordination CLI. The coordination directory and both executable mounts are constructed by Aether, not requested by a run.
The container has no coordination token or identity flag. The mounted socket
is the identity: a connection accepted by a run's socket is treated as that
run. aether-internal has no socket, run-identity, login, or credential
option and always uses /run/aether/coord3.sock.
Caller-supplied mounts are validated before these mounts are appended. A caller mount may not target or nest under /run/aether, /opt/aether, or
/usr/local/bin/aether-internal, so a credential home, profile, or worktree
cannot shadow the socket or either executable. The server fails closed if it
cannot stage and verify its binary: managed-container creation or launch is
refused rather than proceeding without the canonical CLI or bridge.
For enabled runs, the coordination directory and staged server binary are
therefore either present as verified or absent from a container; the canonical
read-only CLI mount is provisioned separately.
Wire v3
The socket carries JSON-RPC 2.0 requests and responses, one request per NDJSON line. The base coordination method set is:
| Method | Parameters | Result |
|---|---|---|
coord.status |
none | v3 wire version, this run's identity and assignment, authorized peers, unread count, and capabilities |
coord.send |
to_run_id, body, idempotency_key |
message_id |
coord.inbox |
optional ack_token, optional wait_seconds |
oldest-first messages and the ack_token for that batch |
coord.ask |
to_run_id, body, idempotency_key |
question_id |
coord.reply |
question_id, body, idempotency_key |
message_id |
coord.report |
outcome, summary, optional evidence_refs, idempotency_key |
durable report_id, outcome, summary, next action, evidence references, and automatic evidence_ref |
The coord.* wire and its six base methods are unchanged. Mission-assigned
runs additionally receive assignment-scoped task.* and worker.* methods
published by coord.status; the current integrator also receives exactly
integration.prepare, integration.show, integration.verify,
integration.request_delivery, and integration.deliver. These methods use
the same run-authenticated socket but are not part of the base coord.* set.
Every allow-list is derived from the current assignment, not from
caller-supplied roles or identities. A mission may authorize its integrator
and active worker runs as peers before any file overlap exists; ordinary runs
retain the radar active/grace authorization described below.
coord.status reports wire_version: "v3", the run, workspace, and member
IDs, the recorded task, each currently authorized peer, and the six base
coordination capabilities (or the assignment-scoped capability set for a
mission run). The sender is never a parameter. An ordinary run can message
only a peer in the same workspace that the radar currently marks as
overlapping, or a peer in its ten-minute overlap grace period. A mission run
can also message its current assignment peers, which are shown with
state: "mission" even when no file overlap exists. A question reply is the
one correlation exception: coord.reply identifies its destination from the
question and remains allowed for that question even after ordinary overlap
grace expires. It cannot be used to send an unrelated message or cross a
workspace boundary.
A run may open conversations with at most eight distinct peers. Existing conversations remain usable when this limit has been reached. The server also enforces these bounds:
- message, question, and reply bodies are at most 4 KiB;
- status returns at most 32 peers and 16 files per peer, with total and truncation metadata; task and path strings are capped at 512 bytes;
- idempotency keys are at most 256 bytes and may not contain control lines;
- an inbox holds at most 100 unacknowledged messages;
- sends allow a burst of five, then one message per five seconds;
- inbox reads allow a burst of ten, then one read per second;
- every non-empty, bounded request line consumes a per-run transport budget of 30 requests per burst, refilling at one request per second. This charge happens before JSON, method, or parameter parsing, so malformed and unknown requests cannot bypass it;
wait_secondsis a server-side wait from 0 through 30 seconds;- each run socket accepts at most 16 concurrent connections, and inactive connections are reaped after five minutes;
- each request line is limited to 64 KiB;
The method set is closed. A connection cannot invoke a control verb, access Git, read another run's transcript, or address a run outside the authorized peer or current mission-assignment set. Assignment-scoped task and worker methods still enforce the role, mission, revision, generation, and current authority checks on the server; they are not a general control API.
Delivery, acknowledgement, and retries
Delivery is at least once. An inbox read returns one oldest-first batch and an
opaque ack_token. Omitting ack_token on the next read acknowledges nothing,
so the same batch and token can be delivered again. Supplying the token on the
next read acknowledges exactly that batch while fetching the next batch. An
empty inbox has no token. Tokens are durable across server restarts while the
run's container and coordination data are retained.
coord.send, coord.ask, coord.reply, and coord.report are mutations and
require an explicit idempotency key on the wire. Repeating a mutation with the
same key and the same semantic inputs returns its original receipt; reusing a
key with different inputs is a conflict. MCP callers must supply the key.
The CLI generates one only when --idempotency-key is omitted and prints
idempotency-key: ... to stderr before the network call; save and reuse that
value if the response is lost.
A successful receipt means the server durably stored the operation. It does not mean that a peer has read the message or understood it. A timed-out request may have succeeded; retry it with the same idempotency key and use the returned receipt.
Accepted messages, questions, and replies are attributed to their originating run and appended to the workspace timeline. These coordination notices are server-originated events with an empty actor identity; ownership changes cannot rewrite their historical attribution. The timeline records durable server acceptance; it does not imply that the recipient has read the item.
aether-internal CLI
A task-bearing coordinated run receives this launch instruction automatically:
Use `aether-internal skill` to read this run's live assignment; use `aether-internal` to coordinate. Report a terminal outcome only after the assigned work is finished.
No skill package, manual identity argument, or credential setup is required.
Run skill before acting so the assignment and capabilities come from current
server state rather than copied prompt text.
All commands below run inside the container. Every command except skill
writes one JSON object followed by a newline. Successful commands use this
shape:
{"schema_version":"v3","ok":true,"result":{}}
Failures use the same envelope with an error object:
{"schema_version":"v3","ok":false,"error":{"code":-32001,"message":"..."}}
Use error.code for branching. The message is method-qualified and suitable
for logs. The process exits with 0 on success, 1 for an internal failure, 2
for usage or protocol input errors, 3 for denied, conflicting, or invalid
state, and 4 when a run or coordination surface is not found or available.
The stable error codes are:
| Code | Meaning |
|---|---|
-32700 |
parse error |
-32600 |
invalid request |
-32601 |
method not found |
-32602 |
invalid parameters |
-32603 |
internal failure |
-32000 |
run or other resource not found |
-32001 |
denied |
-32002 |
invalid state |
-32003 |
conflict or limit reached |
-32004 |
unavailable |
Inspect the assignment and peers
/usr/local/bin/aether-internal status --json
/usr/local/bin/aether-internal skill
status requires --json; skill takes no arguments and prints the current
v3 assignment plus the short coordination workflow. Integrator assignments
include the server-approved account/harness/mode choices and active/total
attempt allowance. Worker assignments identify the assigned task and state
that workers may read and propose only; workers must not spawn other workers.
Top-level and per-command help are available without a coordination socket:
/usr/local/bin/aether-internal --help
/usr/local/bin/aether-internal send --help
skill also works without the mounted socket. In that case it prints the
general workflow without claiming a run identity or assignment. Commands that
need run state return -32004 and exit with status 4 when the socket is not
available. Message, question, reply, and report bodies read from flags, files,
or standard input are capped at 4 KiB before a request is sent.
Send a message
/usr/local/bin/aether-internal send \
--to run-peer \
--body 'I am editing src/example.go.' \
--idempotency-key send-example-1
The result contains message_id. --body-file path reads a body from a
file, and --body-file - reads it from standard input. A body can also be
provided as the positional text after the peer run ID.
Read and acknowledge the inbox
/usr/local/bin/aether-internal inbox --wait 30
/usr/local/bin/aether-internal inbox --ack ack-example-1 --wait 30
Use the ack_token returned by the first command as the value of --ack on
the next command. --wait asks the server to wait once for up to 30 seconds
when no message is ready; it is not a client polling loop. If the process or
connection ends before the result is consumed, do not acknowledge the token
and read again.
Inspect and manage mission tasks and workers
Task and worker commands use the authority attached to the run's socket. They do not accept a caller-supplied role or identity:
/usr/local/bin/aether-internal task show --task-id task-1
/usr/local/bin/aether-internal task list --mission-id mission-1
/usr/local/bin/aether-internal worker list --mission-id mission-1
/usr/local/bin/aether-internal worker inspect --attempt-id attempt-1
Task revisions are bounded JSON specifications. Pass them with
--revision-file path or --revision-file - for standard input (or use
--revision '<json>'). Every task mutation (propose, revise, accept,
accept-submission, and abandon) requires a stable --idempotency-key;
integrator mutations also require the observed
--expected-integrator-generation. Accepting a submission additionally
requires --expected-accepted-set-version. If the server's exact submission
result reports non-empty ScopeViolations, the caller must explicitly assess
those deviations by passing --scope-disposition '<reason>'; the client does
not generate or infer a path list, and an empty reason is not an assessment.
Worker starts and retries require explicit --dispatch-key values. The
dispatch key is also the idempotency key for that operation, so replaying the
same command cannot create a second attempt. Worker cancellation requires its
own --idempotency-key and the observed
--expected-integrator-generation.
Ask a question and reply
/usr/local/bin/aether-internal ask \
--to run-peer \
--body 'May I update src/example.go?' \
--idempotency-key ask-example-1
/usr/local/bin/aether-internal reply \
--question-id question-example-1 \
--body 'Yes, proceed.' \
--idempotency-key reply-example-1
The first command returns the durable question_id. Use that value with
--question-id when replying; a reply does not take a target run ID. ask
and reply accept the same --body-file and standard-input forms as send.
The example IDs are ordinary non-secret values. Replace them with the peer
run ID and question ID returned by the run's own status and inbox results.
Integrator candidate integration
A candidate freezes selected accepted revisions for combined verification and human-approved delivery. Only the current mission integrator may use these agent commands:
integration prepare
integration show
integration verify
integration request-delivery
integration deliver
Each accepts --params-file FILE|- and optional --json. JSON input is
limited to 32 KiB; - reads standard input. The run socket supplies caller
identity. Parameter JSON cannot grant a role or approval. Results use the
normal schema_version, ok, result, and structured error envelope.
There is no agent decide, approve, generic RPC, list, resolve, patch, or
delete command.
Use each command's --help for its required JSON fields. Prepare requires
target_ref, expected_target_revision, and idempotency_key; the server
resolves omitted workspace, mission, and accepted inputs. Explicit
submissions select an ordered subset of current accepted revisions.
Create parameter files in a writable location, not the read-only
/run/aether mount:
aether-internal integration prepare --params-file /tmp/aether-prepare.json --json
aether-internal integration show --params-file /tmp/aether-show.json --json
aether-internal integration verify --params-file /tmp/aether-verify.json --json
aether-internal integration request-delivery --params-file /tmp/aether-request.json --json
Verification is asynchronous; poll show for its durable result. Wait for a
human delivery decision before executing the approved request:
aether-internal integration deliver --params-file /tmp/aether-deliver.json --json
Other mission work may continue, but changing the accepted submission set invalidates an older candidate's mission binding. A replacement integrator may continue a candidate when the accepted set is unchanged.
After an uncertain outcome, retry the same parameters and mutation identity.
Do not replace an idempotency_key merely because the connection failed;
delivery retries use the same request_id and request_version. A denial,
conflict, or invalid state remains a structured server error. See
Candidate integration for parameter records, retained
verification evidence, human decisions, and exact delivery.
Report an outcome
/usr/local/bin/aether-internal report \
--outcome success \
--summary 'Completed the assigned change.' \
--evidence-ref evidence-example-1 \
--idempotency-key report-example-1
--outcome is exactly one of success, failure, or blocked. A non-empty
summary is required. --evidence-ref may be repeated, and --summary-file
accepts a file or - for standard input. The result contains a durable
report_id and the server-created evidence_ref.
A worker report is one-shot and terminal. Success submits the attempt and the server then stops that worker. Failure fails the attempt without treating it as a task result. Blocked is a durable observation and does not stop the worker or submit the task. Waiting on a peer uses ask/inbox, never report.
Before accepting coord.report, Aether captures evidence for the run. The
capture retains a private Git evidence commit and the PTY transcript up to
16 MiB, then stores factual context, provenance, unresolved facts, and a next
action with the report. Evidence is retained for 30 days. Unavailable or
truncated sources are recorded explicitly. The capture is not an atomic
environment snapshot and does not claim that the reported work was verified.
If capture or durable storage fails, the outcome is not accepted, and the
runtime resources remain recoverable.
Finalization and event publication are crash-safe. Finalization writes a durable pending publication row and a deterministic evidence-event ID. The event is appended before the report is marked published; if an append result is uncertain, retrying the same ID reconciles the existing event rather than creating a duplicate. Pending rows are traversed with a stable cursor and wraparound, while attempts, next-attempt time, and the last error are durable. Transient failures remain eligible for service-lifetime retries; permanent event conflicts are quarantined with their error visible for operators. A caller may therefore receive an internal or unavailable error after capture while the finalized report remains retryable under its original idempotency key.
The shipped CLI and MCP bridge allow the full two-minute evidence-capture budget plus a small framing margin (and still honor an earlier caller deadline). Captures from 30 through 120 seconds are reachable without changing the socket protocol.
Lifecycle status is separate
coord.report is the durable worker outcome described above. It is exposed
through aether-internal report and the MCP tool aether_report.
run.report is different. It is the harness lifecycle hook that updates the
run's transient working or waiting status. Harness callbacks invoke it by
running /opt/aether/aether-server report <harness> over the same run socket.
It is not an agent outcome, is not exposed as an aether-internal command, and
has no durable evidence receipt. A lifecycle callback may fail without
blocking the agent; the run then falls back to its normal stall handling.
Coordination also does not provide a universal inbound terminal hook. When a human steers a run, Aether delivers the request through the harness's serialized PTY input path and records the delivery separately.
Retention and shutdown
Active runs and explicitly retained terminal TUI runs keep their socket,
unread mailbox, and timeline entries through a server restart. Recovery
rebinds coord3.sock for those runs. When a run's container is destroyed,
Aether releases the coordination directory and mailbox after any required
evidence capture has completed. With --conflict-coordination=false, a newly
created container still receives the read-only canonical CLI mount, but no
coordination socket or borrowed run identity is provided. Run-bound
coordination requests are unavailable; --help and the general, identity-free
skill workflow still work, and the CLI cannot authorize run operations. The
optional MCP bridge has no usable socket. The conflict radar itself remains
active.


