The .rivets directory
Two different things share the name .rivets. One is a folder you commit to your repository to tell Rivets how to prepare and run a workspace. The other is a worker’s private state directory in its home folder, which you should not edit by hand.
<repo>/.rivets/ |
~/.rivets/ |
|
|---|---|---|
| Belongs to | The repository | The machine running a worker |
| Committed | Yes | Never |
| Written by | People | The worker |
| Contains | settings.toml |
Configuration, clones, worktrees, logs |
Why settings.toml exists
A worker clones each repository once and cuts every task’s worktree from that clone. A fresh worktree only has tracked files: no node_modules, no build output, no .env. Only the repository knows how to fix that. .rivets/settings.toml says so once, for every worker.
Example
[scripts]
# Runs on every fresh worktree, before the agent's first turn.
setup = """
bun install --frozen-lockfile
bun run db:migrate
"""
# Runs before an archived worktree is deleted.
archive = "docker compose down"
# "nonconcurrent" (the default) stops the other run scripts before starting one.
run_mode = "nonconcurrent"
[scripts.run.dev]
command = "bun run dev"
default = true # what the Run button starts on a single press
icon = "play"
[scripts.run.test]
command = "bun test"
icon = "test-tube"
[scripts.run.seed]
command = "bun run db:seed"
hide = true # runnable by id, kept out of the Run menu
The three kinds of script
| Kind | When it runs | Who starts it |
|---|---|---|
setup |
Once per worktree, right after it is created | The worker |
run |
On demand | A person, from the Run control |
archive |
Before an archived worktree is deleted | The worker |
setup
setup runs once per worktree, not once per message. Follow-up messages in the same task reuse the worktree and do not run it again.
- It blocks the agent’s first turn, so the agent never starts on a half-installed tree. Set
async = trueto let the agent start immediately. - A failed setup does not fail the task. The agent still starts. The result and output show in the script’s terminal tab.
- Editing the script re-runs it. Rivets records the command that last succeeded in
.context/setup-state.jsoninside the worktree. A changed command, or a previous failure, runs again on the next turn.
run
Named scripts shown behind the Run control in each client’s terminal strip. A single press starts the script marked default = true. The others are one menu away: right-click on web, or the menu arrow on Mac and iOS.
Run scripts have no timeout: servers and watchers are meant to keep running until you stop them. With run_mode = "nonconcurrent" (the default), starting a script first stops the others and waits for them to exit, so two dev servers do not race for the same port. Set run_mode = "concurrent" to allow several at once.
archive
Teardown for anything that outlives the directory, such as containers or tunnels. It runs while the worktree is still on disk, just before the worker deletes it. It cannot be run by hand, because that would tear down a workspace someone may still be using.
Field reference
setup and archive each take a string (the command) or a table:
| Field | Default | Meaning |
|---|---|---|
command |
none | Shell command, run through the worker user’s login shell. |
cwd |
Worktree root | Relative path inside the worktree. |
async |
false |
true lets the agent start before the script finishes. |
timeout_seconds |
900 |
Time limit in seconds. Maximum 3600. |
Each [scripts.run.<id>] table takes:
| Field | Default | Meaning |
|---|---|---|
command |
none | Shell command. |
name |
The id | Label on the tab and in the menu. |
cwd |
Worktree root | Relative path inside the worktree. |
icon |
play |
One of the icons below. |
default |
false |
Started by a single press of Run. |
hide |
false |
Kept out of the menu, but still runnable by id. |
Script ids use lowercase letters, digits, -, and _. A repository can define up to 24 run scripts, and the file can be up to 64 KB.
Icons: play, hammer, test-tube, circle-check, monitor, database, terminal, rocket, book, wrench, globe, package. An unrecognized icon falls back to play.
setup and archive are reserved: a [scripts.run.setup] entry is rejected and reported.
Environment
Scripts get the environment an interactive shell in the worktree would get, including the git identity and credential helper, plus:
| Variable | Meaning |
|---|---|
RIVETS_WORKTREE_PATH |
The worktree the script runs in. |
RIVETS_REPO_PATH |
The parent clone, for caches the worktree does not have. |
RIVETS_WORKSPACE_NAME |
The worktree id, for naming per-workspace resources. |
RIVETS_SCRIPT_ID |
The script being run. |
Output and stopping
Every script runs in its own read-only terminal tab on web, Mac, and iOS, showing its state and a Run or Stop control. Anyone with access to the task can read it, so a reviewer can see why setup failed. Script terminals do not count toward the per-person terminal limit.
Stopping sends SIGTERM, then SIGKILL 5 seconds later if the process is still running. A script is only reported as stopped once its process has exited.
Errors in the file
A malformed settings.toml never fails a task. Rivets applies the valid parts and skips the rest, and every client lists the problems next to the scripts.
Turning scripts off on a worker
A worker operator can stop a worker from running any repository’s scripts:
rivets worker connect --no-scripts
This saves "projectScripts": false in ~/.rivets/config.json. The worker stops advertising scripts, and clients hide the Run control for its tasks.
The worker’s home: ~/.rivets
Each worker keeps its state in ~/.rivets/, created with mode 0700. Set RIVETS_HOME to use a different location. The Mac app gives its bundled worker a separate home under ~/.rivets/desktop/<workerId>/.
~/.rivets/
├── config.json # backend URL, worker name, and operator options
├── auth/ # worker signing-key metadata
├── bin/rivets-cred-helper # git credential helper the worker installs
├── run/ # temporary: credential socket, git identity files
├── repos/<repoId>/ # one clone per repository
├── worktrees/<worktreeId>/ # one working tree per task
├── assistant/<worktreeId>/ # the assistant's scratch workspaces
├── attachments/ # prompt attachments downloaded for a job
├── terminal-history/ # encrypted terminal scrollback
├── logs/ # redacted service logs
├── service/ # Windows only: the scheduled-task definition
├── worker-health.json # what `rivets worker status` reads
├── outbound-spool.json # events not yet acknowledged by the server
└── host-id # identifies this machine
config.json
The only file here you might edit, and rivets worker connect flags are the better way to change it. It is written with mode 0600.
{
"url": "https://app.rivets.dev",
"name": "mac-mini",
"tokenCredential": "worker-bearer-...",
"maxConcurrentJobs": 4,
"autoUpdate": false,
"projectScripts": false
}
tokenCredential is the name of an entry in the OS credential store, not the token itself. Options are written only when they differ from the default:
| Key | Default | Set by |
|---|---|---|
maxConcurrentJobs |
Unlimited | --max-workers |
directTransport |
Off | --direct |
loopbackTransport |
On | --no-loopback |
autoUpdate |
On | --no-auto-update |
projectScripts |
On | --no-scripts |
Worktrees and the .context folder
Each worktree contains a .context/ folder for prompt attachments and the setup marker. Rivets adds it to the repository’s local excludes (.git/info/exclude), never to your tracked .gitignore.
Cleaning up
repos/ and worktrees/ are the only folders that grow without bound. The worker deletes archived worktrees on a six-hourly sweep, after running their archive script.
Deleting ~/.rivets/ is safe when no worker is running, with two consequences: the worker re-clones every repository the next time it needs one, and any worktree with uncommitted work is lost. Prefer this order:
- Archive tasks you care about.
- Run
rivets worker uninstallto stop the service. - Delete the folder.