Skip to content

Configuration

roost is almost entirely stateless. It reads a handful of environment variables, and it writes to exactly two small JSON files plus one optional credentials file — nothing else ever touches disk.

VariableDefaultGates
ROOST_BACKENDgoogleWhich backend search, rates, and dates use; overridden per-call by their --backend flag. brands, doctor, and schema have no --backend flag of their own, but still resolve through this same variable (default google) — so setting it to serpapi routes those commands through the SerpApi backend too, and doctor/brands will fail with AUTH_REQUIRED if no key is configured.
ROOST_SERPAPI_KEYunsetThe SerpApi key, checked first in the auth resolution order (below). Only read when the serpapi backend is selected (via --backend or ROOST_BACKEND).
ROOST_STATE_DIRunsetOverrides the directory for both ratelimit.json and places.json. When unset, falls back to $XDG_STATE_HOME/roost (or ~/.local/state/roost).
XDG_STATE_HOMEunsetBase for the state directory when ROOST_STATE_DIR isn’t set.
XDG_CONFIG_HOMEunsetBase for the credentials file directory: $XDG_CONFIG_HOME/roost/credentials (or ~/.config/roost/credentials).
ROOST_RELEASES_URLunsetOverrides the source roost version --check polls for the latest release. Must be https:// (any host) or http:// to localhost/127.0.0.1/::1 — anything else is silently ignored and the default GitHub releases lookup is used instead.

ROOST_BACKEND and --backend both select the backend; the flag wins when both are given. Neither is required — the default is the keyless google backend, so a bare roost search works with no environment configured at all. See backends for what each backend can and can’t do.

roost runs as a fresh process on every invocation — an agent spawns one process per call — so anything that needs to persist across calls (rate-limit timing, the place-id cache) has to live on disk. That directory is:

Terminal window
$ROOST_STATE_DIR # if set
$XDG_STATE_HOME/roost # else, if set
~/.local/state/roost # else

Two files live there, and roost creates the directory (mkdir -p-style) the first time it needs to write to it.

Backs the politeness layer described in politeness: minimum request spacing, a small burst allowance, and a circuit breaker that opens after an upstream block. It is keyed by backend name, so google and serpapi track independently (in practice only google is throttled — serpapi and --no-throttle skip the throttle guard entirely):

{
"google": {
"last_request": 1755781234.12,
"window": [1755781228.9, 1755781234.12],
"consecutive_blocks": 0,
"blocked_until": 0
}
}
  • last_request — epoch seconds of the most recent request; enforces the minimum interval.
  • window — timestamps from the last 600 seconds, used to allow a burst of 2 (one logical search costs two requests: resolve the place, then fetch results).
  • consecutive_blocks — how many times the circuit breaker has tripped in a row; indexes into the backoff schedule [30, 60, 120, 300, 600, 1800] seconds.
  • blocked_until — epoch seconds the breaker stays open until, set after an upstream 429/CAPTCHA. A clean response resets both consecutive_blocks and blocked_until to 0.

roost writes this file with mode 0600 after every throttled request.

Caches the Google place id (a Freebase-style /m/… id, called mid) resolved for each location string, so repeat searches for the same city cost one upstream request instead of two:

{
"austin+tx": {
"mid": "/m/0vzm",
"name": "Austin, TX",
"at": 1755781200.0
}
}

Keyed by a slugified location (lowercased, whitespace collapsed to +). Entries are treated as fresh for 30 days (at plus 30 days); after that roost re-resolves the place on next use. A cache write that fails (read-only filesystem, permissions) is treated as a slowdown, not an error — search still works, it just re-resolves every time.

Only used for the optional serpapi backend’s API key — the default google backend never touches this file, and never needs an account or key of any kind.

$XDG_CONFIG_HOME/roost/credentials # or ~/.config/roost/credentials

Format is key=value lines, one credential per line:

serpapi-key=your-serpapi-key-here

roost creates this file with mode 0600 (owner read/write only) whenever it has to fall back to it. If the file already exists with looser permissions, roost auth login surfaces a warning telling you to chmod 600 it (checked at the moment it writes to the file) — roost does not silently tighten a file that already exists. roost auth status does not re-check the file’s permissions; it only reports whether a key is present.

Terminal window
$ echo "$SERPAPI_KEY" | roost auth login --token-stdin

The key is always read from stdin (or the ROOST_SERPAPI_KEY environment variable) — never from a command-line argument, where it would leak into ps, /proc, shell history, and an agent’s own command log.

When roost needs the SerpApi key, it checks, in order, stopping at the first hit:

  1. ROOST_SERPAPI_KEY environment variable
  2. OS keyring (service roost, username serpapi-key)
  3. The 0600 file above

roost auth login prefers the OS keyring and only falls back to the file when no keyring backend is available (common on headless boxes/containers) — that fallback is automatic, you don’t choose it. roost auth logout removes whichever copy is stored locally; it does not revoke the key with SerpApi itself.

Deleting the state files is always safe — roost recreates them as needed, and nothing else depends on their contents surviving.

Terminal window
$ rm "$XDG_STATE_HOME/roost/ratelimit.json" # or ~/.local/state/roost/ratelimit.json
$ rm "$XDG_STATE_HOME/roost/places.json"

Reasons to do it:

  • ratelimit.json — you’re stuck behind a circuit-breaker cooldown (exit code 20, BLOCKED) and want to force a retry immediately instead of waiting out the backoff. Deleting it clears blocked_until and the request-spacing history; it does not change whether the underlying block is still in effect upstream, so a deleted file can just earn a fresh block.
  • places.json — a location string is resolving to the wrong city, or you suspect a stale/incorrect cached place id. Deleting it forces re-resolution on the next search.

To reset only the credentials file:

Terminal window
$ roost auth logout

or delete $XDG_CONFIG_HOME/roost/credentials directly.

Those three files are the entire disk footprint. roost writes no logs, no cache of search results, no config file of its own settings — every other option is either a CLI flag or one of the environment variables above, resolved fresh on each run.