Configuration
Stoat is an ASP.NET Core application, so everything below is ordinary .NET configuration: an appsettings.json in the folder you start it from, environment variables (Stoat__DataPath, with a double underscore for each :), or command-line configuration. Every key has a working default, so nothing needs to be set to get started.
The store
| Key | Default | What it does |
|---|---|---|
Stoat:ConnectionString | — | A SQLite connection string. Wins over Stoat:DataPath when set. |
Stoat:DataPath | ~/.stoat/coordination | Directory for stoat.db when no connection string is given. |
Stoat:LiveUpdates | true | Lets the await_* tools wake the moment a fact changes, and pushes changes to the web UI. Leave it on: with it off, waiting agents and open pages no longer hear about changes as they happen. Plan status stays correct either way. |
The store is one SQLite file, created on first run. There is no database server to install.
Ports
| Key | Default |
|---|---|
Stoat:RunReceiverUrl (or STOAT_RUN_RECEIVER_URL) | http://localhost:5527: where Stoat receives Bobcat runs. Never 5525, which is bobcat ui's |
ASPNETCORE_URLS | unset: Stoat binds http://localhost:5526 and the run receiver |
To move only the receiver, set Stoat:RunReceiverUrl or STOAT_RUN_RECEIVER_URL. To move coordination off 5526, set ASPNETCORE_URLS. It replaces both addresses, so list the receiver in it too (http://localhost:6526;http://localhost:6527). Use the environment variable: a --urls argument on the command line has no effect. Every route is served on every bound port.
Binding an address other machines can reach (http://0.0.0.0:5526, a LAN address) serves them, and a Stoat that other machines reach is Stoat Team, which needs a licence: see Licence. On localhost, the default, it is free.
Observation
Stoat watches GitHub, nuget.org, npmjs.org and the runs it receives, and records something only when what it sees differs from what it last saw.
| Key | Default | What it does |
|---|---|---|
Stoat:GitHubToken | GITHUB_TOKEN, else GH_TOKEN, else gh auth token | Token for issue and PR observation, and for the GitHub text a zoomed-in node shows. With none of these, GitHub is not observed at all, issue and PR nodes read unknown, and the startup log says so. |
Stoat:GitHubTokenFromGh | true | false stops Stoat from asking a signed-in GitHub CLI for its token. |
Stoat:GitHubPollSeconds | 60 | How often GitHub is swept: one GraphQL query per repository a plan references. Open issues and pull requests are read on every sweep; closed and merged ones every 15 minutes, so a reopened issue can take that long to show. When GitHub's rate limit is spent, sweeps pause until it resets, and the plan page says so. |
Stoat:NuGetPollSeconds | 60 | How often the package feeds — nuget.org, npmjs.org and any configured feed — are polled. |
Stoat:Feeds:{name}:Url / :Path / :Registry | A named feed a publish node can reference: a NuGet V3 service index, a local folder of .nupkg files, or an npm registry. Username/Password authenticate it; for npm, Password is the auth token. nuget.org and npmjs.org need no configuration. | |
Stoat:RunObservationBackstopSeconds | 300 | Runs are observed as they arrive, so this is only a safety net. It catches a run whose publisher went silent (an orphaned run), which no arriving event can announce. |
Stoat:BobcatUrl | — | Read runs from a run console on another server instead of this process's own receiver. Leave it unset unless runs are received somewhere else. |
Stoat:BobcatPollSeconds | 15 | How often that remote console is polled. |
Stoat:RepositoryIconRefreshHours | 24 | How often each repository Stoat knows is looked at for an icon of its own: one REST tree read per repository through the GitHub token, and the file only when it changed. No GitHub token, no icons. |
Stoat:IconsPath | ~/.stoat/icons | Where the repository icons are kept, with an index of what each look found, so a restart asks GitHub nothing until an entry is due. |
Stoat:Pollers | true | false runs no pollers at all: nothing is observed, which is only useful for a test host. |
Stoat:ObservationHistoryEvents | 100 | How many recent observations Stoat keeps for each observed thing (an issue, a feed, a session) beside its latest state. Once a day older ones are compacted away; nothing reads them. A negative value turns compaction off. |
Stoat:ObservationRetentionHours | 24 | How often that compaction runs. The first run is Stoat:ObservationRetentionDelaySeconds (600) after startup. |
Plans and sessions
| Key | Default | What it does |
|---|---|---|
Stoat:PlansPath (STOAT_PLANS) | ~/.stoat/plans | A directory of plan YAML, imported at startup and on POST /api/plans/rescan. It only seeds: a file creates a plan the store has never had, and is otherwise ignored, because the store is the master copy. |
Stoat:PurgedPath | ~/.stoat/purged | Where a purged plan's YAML is written before it is deleted. |
Stoat:ClaudeConfigPath | CLAUDE_CONFIG_DIR, else ~/.claude | Claude Code's directory, read for session observation. |
Stoat:SessionPollSeconds | 5 | How often sessions are read. |
Stoat:SessionActivitySeconds | 90 | How recently a transcript must have changed to read as activity. |
Stoat:AskPersonParkSeconds | 240 | The longest one ask_person call parks before it answers pending with the question's id. Kept under the 300 seconds an MCP client commonly allows a tool call, so the client never cuts the call off; the question stays open either way. |
Starting runners from the plan page
A repository's band on the plan page can start stoat run-plan on this machine (Starting a runner from the page).
| Key | Default | What it does |
|---|---|---|
Stoat:RunnerStarts | true | false turns the page's Start… off on this server: the band shows the command to run in a terminal instead. |
Stoat:RunnerLogsPath | ~/.stoat/runners | Where a started runner's output goes, one {plan}-{owner}-{name}-{time}.log per start. |
Stoat:LaunchersPath | ~/.stoat/launchers.json | The launcher profiles a started runner's --launcher is checked against. |
Stoat:RunnerExecutable | the running stoat | The command a start runs, when it should not be the process the server runs as. |
Notifications
When something waits on you, you should hear about it wherever you are. The top bar's waiting on you badge is on every page; these reach further. Nothing leaves the machine unless a webhook or Discord is set.
| Key | Default | What it does |
|---|---|---|
Stoat:Notifications:Desktop | on on macOS, off elsewhere | An OS notification on this machine. On macOS it uses terminal-notifier when it is installed (brew install terminal-notifier): the notification says Stoat, a click opens the node's page, and a repeat replaces the earlier one. Without it, osascript posts it, and macOS shows it as Script Editor's. notify-send on Linux. false turns it off. |
Stoat:Notifications:Discord:Webhook | — | A Discord channel's webhook URL. Each notification is an embed with the plan, node, repository and a link to the node, in the plan's colour, and nothing in it can ping anyone. Always opt-in; keep it out of plans and commits. |
Stoat:Notifications:Webhook | — | A URL each notification is POSTed to: ntfy, a Slack incoming webhook, anything that takes a POST. Always opt-in. |
Stoat:Notifications:WebhookFormat | json | json (a text field Slack reads, plus the fields) or text (the sentence alone, as ntfy shows it). |
Stoat:Notifications:Kinds | question, give-up, review, person, stalled, limit, main-red, drift | What notifies: question (an agent asks you), give-up (the runner gave up on a node), review (a manual-review PR waits for you, or an agent's PR the runner could not fix or merge), person (a node of yours came ready), stalled (a plan with work waiting and nothing running), limit (a usage limit), main-red, drift, and go-ahead and cloud, which are off by default. A callout's own name (fix, usage-limit, unmergeable) still selects it. |
Stoat:Notifications:BaseUrl | http://localhost:5526 | Where Stoat is, for the link to the node's page. |
Each thing notifies when it appears, and again only after it went away and came back. A disconnected runner's callouts never notify. An agent's ask_person question waiting on a person notifies as question with no runner at all. When more than five are due at once (Stoat restarting with forty PRs open, say), the most urgent four are sent and the rest are summed up in one.
Runs and the Event Model
The run receiver uses the same Monitor: / BOBCAT_MONITOR_* names as Bobcat's own run console.
| Key | Environment variable | Default | What it does |
|---|---|---|---|
Monitor:DataPath | BOBCAT_MONITOR_DATA | ~/.bobcat/monitor/runs | Run archives and the Event Model store. |
Monitor:RetentionDays | BOBCAT_MONITOR_RETENTION_DAYS | 14 | Ages out run archives that have not changed for this many days. The only setting that ever deletes an archive file. 0 turns aging off. |
Monitor:RetentionRuns | BOBCAT_MONITOR_RETENTION_RUNS | 10 | How many runs the receiver lists per job (a repository plus a suite), so a busy repository cannot push a quiet one's runs out. Older runs are ejected: their archive moves to ejected/ and is not deleted. |
Stoat:EventModelStaleAfterDays | 7 | How far a producer's Event Model half may trail the newest half of its model before it is flagged stale. Flagged, never dropped. |
The command-line tools
stoat gate exec, stoat gate run, stoat warnings push, stoat run-plan and stoat runner are clients of a running Stoat, and find it from the environment:
| Variable | Default | Read by |
|---|---|---|
STOAT_URL | http://localhost:5526 | stoat gate exec, stoat gate run and stoat warnings push, unless --url is given (run-plan takes --url only) |
STOAT_RUN_RECEIVER_URL | http://localhost:5527 | everything that adds Stoat's receiver to BOBCAT_MONITOR_URL, and stoat runner |
BOBCAT_RUN_TAG | — | stoat gate exec: the gate to report for, {plan}/{node}; run-plan sets it for each agent |
Launcher profiles for run-plan are ~/.stoat/launchers.json (or --launchers); see Launchers.
Licence
| Key | Environment variable |
|---|---|
JasperFx:LicenseKey | JASPERFX__LICENSEKEY |
Stoat needs no key on its own machine. A server other machines reach is Stoat Team, which needs a CritterStack licence; one key serves Stoat and CritterWatch. The key is read once at startup, so restart after setting it, and a server in the Development environment needs none. The 0.6.0 preview does not check it yet. See Installation.
Memory recall
recall is a full-text search over each memory's title, tags and body, matching whole words and the start of longer ones (wolverine finds WolverineFx), best match first. The stoat tool ships without an embedding model, so there is no semantic search and nothing to configure.