Configuration
RepoOS is configured by repoos.toml at the repo root, with secrets kept in a gitignored .env. Every field is optional — a repo with no repoos.toml at all still works on defaults.
Layout
workDir = "work" # where task markdown files live
docsDir = "docs" # project context an agent reads before working
cacheDir = ".repoos" # derived index, logs, database — disposablecacheDir holds only derived state. Delete it and RepoOS rebuilds everything from the markdown files; nothing of record is lost.
Board behavior
defaultStatus = "inbox" # status new tasks start in
defaultAssignee = "ai" # "ai" or "human"
defaultTaskMode = "freeform"
maxActiveTasks = 3 # how many tasks may be active at once
autoEngineeringMode = false # automatically dispatch ready tasks to agents
worktreeWarnThreshold = 20 # warn once this many task worktrees existServer
servePort = 7171Without servePort, repoos serve derives a stable port from the repo's own path, so two repos running RepoOS at the same time never fight over one port. Pin it only when something external depends on a fixed port (a tunnel, a launchd/systemd unit, a bookmarked URL).
repoos stop finds the right process through a per-port lockfile, so stopping one repo's server never kills another's.
Agents
maxConcurrentAgents = 5 # agent CLI processes running at once; extras queue
ctoMonitorIntervalMs = 300000 # CTO agent poll intervalmaxConcurrentAgents defaults to a value computed from your machine's core count. Raise it if the machine still looks idle under load, lower it if it's straining — agents are subprocess- and network-heavy, so the right number is usually well below your core count.
Runtime
RepoOS runs under Bun when available and Node otherwise. Control it with environment variables, not repoos.toml:
| Variable | Effect |
|---|---|
(unset) or REPOOS_RUNTIME=auto | Use Bun if it's on PATH, else Node. The default. |
REPOOS_RUNTIME=bun | Require Bun; warn and stay on Node if it's missing. |
REPOOS_RUNTIME=node | Always Node. The opt-out. |
REPOOS_BUN_PATH=/path/to/bun | Use this binary explicitly, skipping the PATH lookup. |
repoos serve prints which runtime it picked at startup. Bun is substantially faster for the subprocess-heavy work RepoOS does — on this project's own test suite it's roughly a 5x difference — so the default is worth keeping unless you have a reason to pin Node.
Authentication
Auth is off by default. When enabled, the server refuses to start unless both a login provider and a bootstrapAdmin are configured.
auth.enabled = true
auth.sessionMaxAge = "2592000" # seconds
auth.bootstrapAdmin = "you@example.com" # only this address can claim the founding admin account
auth.emailProvider.type = "resend"
auth.emailProvider.fromAddress = "otp@send.example.com"
# auth.google.clientId = "..." # optional "Sign in with Google" buttonSecrets never go in repoos.toml — it's git-tracked. Put them in .env:
REPOOS_RESEND_API_KEY=...
REPOOS_GOOGLE_CLIENT_SECRET=...Auth changes take effect on the next repoos serve, not live.
Remote validation
Offloads the test suite to a disposable cloud VM, so a long check doesn't tie up (or get starved by) your laptop.
remoteValidation.enabled = false
remoteValidation.fallbackToLocal = falseNeeds HETZNER_API_TOKEN and REPOOS_REMOTE_SSH_KEY in .env, plus a prebuilt snapshot.
Releases
Opt-in. When configured, RepoOS pushes an annotated version tag from a clean trunk after repoos check passes; your CI does the actual build and publish.
[release]
enabled = true
provider = "git-tag"
branch = "main"
versionFile = "package.json"
tagPrefix = "v"
remote = "origin"The "Releases" page only appears in the UI when this block is present.
Appearance and notifications
theme = "dark"
uiTheme = "classic" # classic | clear | gen z | jelly
ntfyEnabled = true
ntfyTopic = "your_topic"