For AI agents: this page as Markdown · llms.txt · JSON Schema

Remote sink

Warning

In internal testing: in the code but not ready for public use, and may change or go without notice.

A remote is an HTTPS endpoint that receives this machine's sessions, events, audits, violations, the rules they ran, and headless scans. It lets a team see Rerail across machines.

With no remote added, nothing leaves the machine. Rerail ships no remote; you point it at a service that implements the protocol.

Commands

rerail remote manages remotes and opts repositories out.

sh
rerail remote add <url> [--name <name>] [--label <machine label>] (--token-stdin | --token-file <file>) [--no-content]
rerail remote list [--json]
rerail remote remove <name|url>
rerail remote status [--json]
rerail remote push [--json]
rerail remote exclude [--repo <dir>]
rerail remote include [--repo <dir>]

The daemon sends every 10 seconds; add and remove need no restart.

Command details

The daemon sends on its own connections, so a remote that is slow or down never holds up a session.

While a remote is set up, set-up's question about reading your sessions says they are sent, to which host, and how to stop, instead of "Stays on this computer".

Excluding a repository

exclude opts a repository (--repo, or the current folder's) out of every remote; include opts it back in.

include applies from that moment: what the repository recorded before it is never sent.

Edge cases

Exclusions, and when include ended them, are kept in remote.json.

On disk

Remote state lives in four files in the Rerail home, readable by you only.

The token is a plain file outside the database. Deleting remote-outbox.sqlite makes every remote receive everything again.

Files and permissions
File in the Rerail home Holds
remote.json Remotes (name, id, URL, whether content is sent, label) and exclusions.
remote-tokens/<name> That remote's token, alone. Never printed; never in the database, which support bundles and rerail scrub read.
remote-outbox.sqlite Acknowledgements (an event cursor, a hash per sent record), retry state, queued scans. Deleting it resends everything.
machine-id A random UUID made once, naming this machine to every remote.

All four are yours only (files 0600, the token folder 0700), like the rest of the home. The token is a plain file, not in the system keychain, because the daemon reads it unattended.

Protocol

Rerail posts NDJSON batches of records with stable ids to one endpoint.

POST <url>/v1/batches, Authorization: Bearer <token>, Content-Type: application/x-ndjson, at most 5 MB. Line one is the batch header; each further line is a record:

jsonc
{"type":"batch","protocol":1,"batchId":"<uuid>","machineId":"<uuid>","machineLabel":"<hostname>","rerailVersion":"<version>","sentAt":1793732129819}
{"type":"repository","id":"remote:github.com/org/repo","displayName":"repo","aliases":[]}
{"type":"session","id":"<session id>","host":"claude-code","cwd":"/abs","repositoryKey":"…","startedAt":…,"endedAt":…|null}
{"type":"event","id":"<machineId>:<sequence>","sessionId":"…","eventId":"…","kind":"user-prompt","observedAt":…,"sequence":42,"payload":{…}}
{"type":"rule","id":"sha256:…","ruleId":"no-todo","kind":"code","mode":"steer","document":{…}}
{"type":"audit","id":"<audit id>","sessionId":"…","workspaceRoot":"…","scope":"repository","mode":"log","status":"completed","startedAt":…,"finishedAt":…,"ruleRevisions":[…],"ruleProvenance":[…],"paths":[…],"failures":[…],"failureCount":0}
{"type":"violation","id":"<audit id>:<ordinal>","auditId":"…","sessionId":"…","ruleId":"…","violationKey":"…","logged":true,"code":"…","clauseId":"…","mode":"log","message":"…","path":"…","start":{"line":3,"column":5},"end":{"line":3,"column":9},"span":{…},"payloadHash":"sha256:…"}
{"type":"scan","id":"<scanId>", …the rerail.scan/1 report…}
{"type":"gap","id":"…","stream":"event","fromSequence":7,"toSequence":9,"reason":"deleted-locally"}
Record rules

Answers

200 stores the batch; Rerail skips records the remote refuses and retries other failures with backoff.

Per-status handling
Status Rerail
200 Stored; the cursor moves past the batch.
400, 413 Halves and resends until it isolates the refused record, skips it, and sends a gap (stream: "batch", its event sequence or stream and id) next batch. After three in one round, it backs off and retries later, so a remote refusing everything does not empty the queue.
401, 403 Waits five minutes, or until the remote is re-added with a new token (rerail remote remove, then add).
anything else, timeouts, network errors Retries the same batch after 2 s, doubling to at most five minutes.

Gaps

A gap record reports data Rerail could not send, instead of leaving a hole:

Data changed after sending, for example by rerail scrub, is not resent. Scrub before adding a remote, or ask the service's owner to delete it.