Rivets
Documentation

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 = true to 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.json inside 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:

  1. Archive tasks you care about.
  2. Run rivets worker uninstall to stop the service.
  3. Delete the folder.

Type to search the docs.