Skip to content

Environment Variables

NM_HOME

Override the data directory.

Typestring
Default~/.no-mistakes

When set, everything else moves under this root:

  • Global config: $NM_HOME/config.yaml
  • Gate repos: $NM_HOME/repos/<id>.git
  • Worktrees: $NM_HOME/worktrees/<repoID>/<runID>/, unless worktree_roots places a repository’s run worktrees elsewhere
  • Logs: $NM_HOME/logs/
  • Database: $NM_HOME/state.sqlite
  • Socket / PID / singleton lock: $NM_HOME/socket, $NM_HOME/daemon.pid, and $NM_HOME/daemon.lock
  • The socket path must fit the platform’s Unix socket limit: 103 bytes on macOS, 107 on Linux. daemon start, daemon restart, daemon status, init, attach, rerun, axi run, and the push hook fail with a message that names the path, its length, and the limit when it is too long. Use a shorter NM_HOME. The physical path counts: a symbolic link to a long directory does not help, because the push hook dials the resolved path.
  • Managed agent server PID records: $NM_HOME/servers/
  • Local evaluation cases and registry: $NM_HOME/eval/ (created by automatic collection or an explicit no-mistakes eval command)
  • Managed service names get a short stable suffix derived from $NM_HOME so multiple installs don’t collide.

A push is handled by the root that owns the gate it was pushed to, not by the root NM_HOME names. Git does not set NM_HOME for a hook, so the managed pre-receive and post-receive hooks would otherwise reach whichever daemon the pushing shell happened to point at - usually the default ~/.no-mistakes, since the variable is normally unset. Instead the CLI resolves its root from the gate path the hook passes it: a gate always lives at $NM_HOME/repos/<id>.git, so the owning root is a property of where the gate sits. A push to a gate under one root therefore reaches that root’s daemon even when NM_HOME is unset or names a different root. A path that is not a managed gate is refused rather than resolved against the default root, so pre-receive fails the push and post-receive stays non-blocking and records the skipped notification in the gate’s notify-push.log. The daemon enforces the same ownership on its own side of both calls, refusing to admit or to start a run for a gate that does not sit under its root. Existing installs get this by updating the binary; the hook scripts are unchanged, so no gate needs regenerating.

NM_DAEMON_CONNECT_TIMEOUT

Override how long a CLI client waits for an existing daemon socket to accept a connection before failing instead of hanging.

Typestring (Go duration)
Defaultunset (falls back to the daemon_connect_timeout global config value, itself defaulting to 3s)

Takes precedence over daemon_connect_timeout in config.yaml. An empty, unparsable, or non-positive value is ignored and the config value (or its default) is used instead. The config value is read from the root whose socket is being dialed, not from the root NM_HOME names, so a push handled by the root that owns the gate also waits for the timeout that root configured.

FORGEJO_BASE_URL

Canonical Forgejo web base URL used for provider discovery and forgejo-axi commands.

TypeURL
Default(none)

Set this for every SSH origin, because an SSH remote does not identify the canonical Forgejo web scheme, port, or path prefix. Also set it when an HTTPS origin uses a self-hosted hostname other than codeberg.org or one containing forgejo. Recognized HTTPS origins are detected automatically, including non-default ports and path prefixes inferred from the origin. When set, the host and prefix must match the repository’s upstream origin - name the clone hostname with FORGEJO_SSH_DOMAIN when the instance serves SSH from a different host; credentials, query strings, and fragments are rejected.

FORGEJO_SSH_DOMAIN

Hostname a Forgejo instance publishes in SSH clone URLs when it differs from the FORGEJO_BASE_URL host.

Typestring
Default(none)

Set this to the instance’s own SSH_DOMAIN value when Forgejo serves SSH from a hostname other than its web host, so an origin such as git@ssh.forgejo.example:octo/widgets.git is still recognized as that instance.

Give a bare ASCII hostname or IP address, with an optional port that is ignored, because an instance’s SSH clone port varies independently of its hostname. Hostname labels hold letters, digits, hyphens, and underscores, with a hyphen or underscore never first or last in a label, and an IPv6 address is bracketed ([2001:db8::1]). Any other value is treated as unset: a scheme, path, credentials, query string, or fragment, and also a name that could never match an origin, such as a wildcard or a trailing dot. no-mistakes never echoes a rejected value back into a log or an error, because the setting can hold a pasted credential.

The variable only widens which SSH origins belong to the instance FORGEJO_BASE_URL already names, so it does nothing while that variable is unset or invalid. The upstream path prefix must still match, and HTTPS origins are unaffected. GitHub, GitLab, Bitbucket, and Azure DevOps hosts, plus any host configured in glab, gh, or tea, are all matched before the declared domain; against the remaining hostname guesses, the declared domain carries exactly the weight a FORGEJO_BASE_URL match already carries.

If you worked around a split host by adding a tea login for it, delete that login when you set this variable. Because tea’s configured hosts are matched first, a login whose url host or ssh_host equals the origin’s host keeps the repository on the Gitea provider: the PR and CI steps keep running through tea instead of forgejo-axi, and where tea is not installed or not logged in, the PR step is skipped. Find the login with tea logins list and remove it with tea logins delete <name> (on tea 1.0: tea auth status and tea auth logout --host <url>).

FORGEJO_TOKEN_<HOST_KEY>

Preferred host-scoped token used by forgejo-axi for Forgejo PR and CI operations.

Typestring
Default(none)

HOST_KEY is the uppercase URL host, including a non-default port, with each non-alphanumeric ASCII character replaced by its uppercase hexadecimal code point between underscores. For example, forgejo.example:8443 uses FORGEJO_TOKEN_FORGEJO_2E_EXAMPLE_3A_8443. Host-scoped names prevent credentials from being reused for a look-alike host.

FORGEJO_TOKEN

Generic Forgejo token fallback. When present, no-mistakes passes its name explicitly to forgejo-axi; prefer a host-scoped token when the daemon serves multiple Forgejo instances.

Typestring
Default(none)

Tokens remain in the subprocess environment: no-mistakes never puts a token value in command arguments and redacts Forgejo token values from surfaced provider errors.

FORGEJO_TIMEOUT_MS

Per-request forgejo-axi timeout in positive integer milliseconds.

Typeint
Default15000

The pipeline context still bounds and cancels the complete subprocess independently.

FORGEJO_CA_FILE

Path to a replacement CA trust bundle used by forgejo-axi for HTTPS requests.

Typestring
Default(none)

This replaces rather than appends to the platform trust store. See Provider Integration for provider setup.

NO_MISTAKES_FINDINGS_FILE

Absolute path to the empty JSON report file supplied to each repository gate command.

This is a command-child variable, not a daemon setting. no-mistakes creates a fresh file outside the worktree before every gate command and removes it after reading the result. Commands can write {"findings":[...]} using the existing finding shape to report stable finding IDs and locations. Commands that leave it empty retain their exit-code-only behavior.

See the gate contract for the report schema, limits, verdicts, and approval behavior.

NO_MISTAKES_BITBUCKET_EMAIL

Bitbucket Cloud account email used for PR creation and CI monitoring.

Typestring
Default(none; Bitbucket PR/CI steps skip when unset)

Used alongside NO_MISTAKES_BITBUCKET_API_TOKEN. See Provider Integration.

NO_MISTAKES_BITBUCKET_API_TOKEN

Bitbucket Cloud API token.

Typestring
Default(none)

Get one from Bitbucket account settings.

NO_MISTAKES_BITBUCKET_API_BASE_URL

Override the Bitbucket Cloud API base URL.

Typestring
Defaulthttps://api.bitbucket.org/2.0

Useful for mocking in tests or pointing at a proxy.

AZURE_DEVOPS_EXT_PAT

Azure DevOps Personal Access Token inherited by the daemon for non-interactive az CLI auth. Alternatively, authenticate the Azure DevOps extension with az devops login.

Typestring
Default(none)

See Provider Integration.

GITHUB_TOKEN

GitHub token used to authenticate updater release asset downloads.

Typestring
Default(none)

When set, the updater sends the token as a Bearer authorization header for release asset downloads. Version metadata is fetched anonymously from a GitHub release-asset manifest that is not subject to the unauthenticated REST rate limit. GITHUB_TOKEN takes precedence over GH_TOKEN; when neither variable is set, asset downloads remain anonymous. The token is not printed, logged, or persisted.

GH_TOKEN

Fallback GitHub token used by no-mistakes update when GITHUB_TOKEN is unset or empty.

Typestring
Default(none)

See GITHUB_TOKEN for the updater’s authentication behavior and precedence.

NO_MISTAKES_NO_UPDATE_CHECK

Disable background update checks.

Type1 to disable, anything else to leave enabled
Defaultunset (checks enabled)

Update checks run on every CLI invocation except update itself and version queries (--version / -v, which stay side-effect-free), fetch the GitHub release-asset channel manifest, cache the result in $NM_HOME/update-check.json, and print a one-line notification to stderr when a newer version is available. Dev builds (non-semver versions) and binaries that resolve into /nix/store suppress the check automatically.

XDG_DATA_HOME

Data directory used to discover OpenCode transcripts for intent extraction.

Typestring
Default~/.local/share

When set, no-mistakes looks for OpenCode’s intent transcript database at $XDG_DATA_HOME/opencode/opencode.db. When unset, it falls back to ~/.local/share/opencode/opencode.db.

GLAB_CONFIG_DIR

Directory holding glab’s config.yml, consulted when detecting self-hosted GitLab.

Typestring
Default(none)

When the upstream hostname carries no gitlab marker, no-mistakes reads glab’s configured hosts from $GLAB_CONFIG_DIR/config.yml to decide whether the host is a GitLab instance. It takes precedence over XDG_CONFIG_HOME. A selected forge_profiles entry overrides this variable for that run and removes GITLAB_TOKEN, GITLAB_ACCESS_TOKEN, OAUTH_TOKEN, CI_JOB_TOKEN, GLAB_ENABLE_CI_AUTOLOGIN, GITLAB_HOST, GL_HOST, GITLAB_URI, GITLAB_API_HOST, GITLAB_REPO, GITLAB_GROUP, REMOTE_ALIAS, and GIT_REMOTE_URL_VAR from all child processes. See Provider Integration.

GH_CONFIG_DIR

Directory holding gh’s hosts.yml, consulted when detecting self-hosted GitHub Enterprise.

Typestring
Default(none)

When the upstream hostname is not github.com, no-mistakes reads gh’s configured hosts from $GH_CONFIG_DIR/hosts.yml to decide whether the host is a GitHub Enterprise instance. It takes precedence over XDG_CONFIG_HOME. A selected forge_profiles entry overrides this variable for that run and removes GH_TOKEN, GITHUB_TOKEN, GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN, GH_HOST, and GH_REPO from all child processes. See Provider Integration.

TEA_CONFIG_DIR

Directory holding tea 1.0’s hosts.yml, consulted when detecting self-hosted Gitea.

Typestring
Default(none)

tea 1.0 (gitea/tea#1126) keeps its instances in $TEA_CONFIG_DIR/hosts.yml, and no-mistakes reads the same file to match the upstream host to an instance URL or ssh_host. It takes precedence over XDG_CONFIG_HOME. tea 0.x ignores it. See Provider Integration.

XDG_CONFIG_HOME

Config directory used to locate glab’s config.yml for self-hosted GitLab detection, gh’s hosts.yml for self-hosted GitHub Enterprise detection, and tea’s config.yml (tea 0.x) and hosts.yml (tea 1.0) for Gitea detection.

Typestring
Default~/.config

When GLAB_CONFIG_DIR is unset, no-mistakes looks for glab’s configured hosts at $XDG_CONFIG_HOME/glab-cli/config.yml, falling back to ~/.config/glab-cli/config.yml when XDG_CONFIG_HOME is unset. When GH_CONFIG_DIR is unset, no-mistakes looks for gh’s configured hosts at $XDG_CONFIG_HOME/gh/hosts.yml, falling back to ~/.config/gh/hosts.yml when XDG_CONFIG_HOME is unset. tea 0.x has no CLI-specific override env var (unlike GLAB_CONFIG_DIR/GH_CONFIG_DIR); no-mistakes always looks for its configured logins at $XDG_CONFIG_HOME/tea/config.yml, falling back to ~/.config/tea/config.yml when XDG_CONFIG_HOME is unset. When TEA_CONFIG_DIR is unset, tea 1.0’s configured instances are read from hosts.yml in that same directory. See Provider Integration.

COMPACT_ADVISER_DISABLE

Kill-switch injected into every pipeline agent subprocess so compact-adviser stays inert during unattended work.

Typealways 1 for agent subprocesses
Defaultinjected; not a daemon-wide setting

no-mistakes stamps COMPACT_ADVISER_DISABLE=1 onto every spawned gate agent, including ACP aliases and managed agent servers that can load host plugins. Forge and profile overlays cannot drop the flag. The daemon process itself is unchanged; this is agent-child policy only, not a user-facing knob for the service environment.

NO_MISTAKES_UMAMI_HOST

Override the telemetry collection host.

TypeURL
Defaulthttps://a.kunchenguid.com

When set, telemetry sends events to this host’s /api/send endpoint. If it is unset in a dev build, no-mistakes also checks a repo-local .env file for NO_MISTAKES_UMAMI_HOST. If no runtime value is found, it falls back to any host embedded at build time and then the default self-hosted Umami instance.

NO_MISTAKES_UMAMI_WEBSITE_ID

Override or enable the telemetry website ID.

Typestring
Defaultembedded in Makefile, Nix flake, and release builds; unset in unembedded dev builds

When set, telemetry uses this website ID at runtime. If it is unset in a dev build, no-mistakes also checks a repo-local .env file for NO_MISTAKES_UMAMI_WEBSITE_ID. If no runtime value is found, it falls back to any website ID embedded at build time.

When telemetry is enabled, no-mistakes sends command, run, approval, fix, and wizard events, completed step events with awaiting_approval, fix_review, or failed status, and pageviews for the human surfaces /wizard and /tui and the state-changing agent surfaces /axi/run, /axi/respond, /axi/abort, and /axi/retry to Umami. Mutation pageviews are sent alongside command events, so command status and duration remain available. They include only flag-derived context: /axi/run records booleans such as whether --yes or --skip was supplied and whether explicit intent was supplied through any intent input transport; /axi/respond records the sanitized action and whether --yes was present. The intent boolean does not distinguish text, file, and stdin input, and intent input rejected by the CLI resolver emits no axi-run command event or pageview.

Read-only surfaces (axi home, axi status, axi logs, status, runs) emit no telemetry at all, so agent status polling cannot flood remote analytics. Mutation surfaces (axi run, axi respond, axi abort, axi retry, run lifecycle, approvals, and fixes) stay full-fidelity. Each explicit human CLI, AXI, or TUI branch-sync check/apply attempt emits one command event and no additional pageview. Its fields are bounded enums and booleans only: surface, mode, state, relation, target kind, pipeline phase, PR state, result, refusal reason, dirty state, and duration. It never sends a SHA, run ID, path, branch name, URL, remote name, or command argument.

What stays local and what leaves the machine

Everything sent remotely is low-cardinality: command names, statuses, durations, counts, flag booleans, agent names, fixed step categories, and - on the single terminal run finished event - the bounded performance rollup agent_invocations, resumed_invocations, and fallback_invocations (small counts only). Run IDs, repository paths, branch names, session identities, prompts, model outputs, diffs, and per-invocation performance records are never sent. Repository-declared gate labels stay local. Remote step, approval, fix, and failed-step fields record them as the fixed token gate.

Quota-aware routing records candidates, quota evidence, the chosen order, and mid-run switches in runs.quota_routing in the local state database (<NM_HOME>/state.sqlite). These records are never sent in remote telemetry.

Detailed performance evidence stays on the machine in the local state database (<NM_HOME>/state.sqlite): one agent_invocations row per agent invocation, plus each run’s accumulated parked-at-gate time. Each row records run and step identity, purpose (such as review/review-fix/housekeeping), the reported model and its provider, the cold/started/resumed/fallback session mode, a truncated session-identity hash, timestamps, duration, exit status, and failure category, alongside the session-fidelity metrics below. It never stores prompts, model outputs, diffs, raw command arguments, secret values, or credentials - only bounded counts, low-cardinality categories, and durations.

Token counts and the additive session-fidelity fields are nullable and read back as unknown (rendered -) rather than a fabricated zero when the adapter did not report them, so a failed or cancelled invocation, a row written before a field existed, and an adapter that does not surface a datum stay honest. A recorded 0 means the adapter reported zero; it is distinct from unknown.

  • Token detail: input_tokens/output_tokens/cache_read_tokens (adapter counters, cumulative across a resumed session for codex; per-invocation for pi; unknown when the adapter did not report usage). Claude, Pi, and OpenCode normalize their disjoint provider input buckets into total input, including cache reads and reported cache creation. Pipeline fresh_input_tokens remains input minus cache reads, including reported cache creation; eval token accounting separates cache writes as well. Other fields are cache_creation_tokens (unknown when the provider does not surface it), reasoning_tokens, and delta_input_tokens/delta_output_tokens/delta_cache_read_tokens (the correct per-round amounts, so a resumed session’s cumulative counter is never mistaken for one round’s usage).
  • Activity: model_roundtrips (a proxy for productive model turns), tool_calls, and a bounded tool-category histogram (tool_wait_calls, tool_test_lint_calls, tool_edit_calls, tool_read_calls, tool_git_calls, tool_other_calls); a compound command counts once per sub-command, so the histogram can sum higher than tool_calls.
  • Timing split: subprocess_wait_ms is the wall-clock spent inside tool subprocesses; model/reasoning time is the invocation duration minus it, clamped at zero.
  • Context: workload_files/workload_lines (bounded change size), finding_count (findings in the structured output), and fallback_reason (why a failed resume forced a fresh session, one of transient/parse/exit/spawn/unsupported/other).

The count and timing definitions live in one authoritative place (internal/agent/invocationmetrics.go). Inspect the evidence with no-mistakes stats --agents (per-purpose aggregates, including a METRICS coverage count so a real zero is distinguishable from missing instrumentation) or no-mistakes stats --run <id> (one run’s invocations, the per-round-vs-cumulative token split, and parked time).

NO_MISTAKES_TELEMETRY

Disable telemetry collection.

Type0, false, or off to disable; anything else to leave enabled
Defaultunset

When set to a disabling value, telemetry stays off even if a runtime or embedded website ID is available.

Environment the daemon sees

When the daemon runs through a managed service (launchd, systemd user service, Task Scheduler), the macOS and Linux service definitions include a default PATH with common user and system binary directories. They also bake in any proxy variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY, ALL_PROXY) that were set when you installed or refreshed the service, so the daemon and the agents it spawns can reach the network through your proxy even when the login-shell probe is unavailable. Once baked in, the values are preserved across later service refreshes and restarts even when the proxy variables are not exported in that shell, so a routine daemon restart or a binary upgrade will not strip them; export the variables again only when you need to change or remove them. Both the upper- and lower-case spellings are forwarded exactly as you set them, because tooling is inconsistent about which it reads (curl, for example, honors only the lower-case http_proxy for plain-HTTP requests). Because a proxy URL can embed credentials (for example http://user:pass@host), the generated service file is restricted to owner-only 0600 permissions whenever proxy values are forwarded into it. When no proxy variables are set, the generated definition is unchanged and keeps the conventional 0644 mode. Windows Task Scheduler inherits your logon environment and needs no forwarding. At daemon startup, the daemon resolves environment once from your login shell on macOS and Linux ($SHELL -l -i -c 'env -0' for bash and zsh, run in its own session so an interactive shell never waits on a terminal), preserves your shell PATH order, and appends any missing well-known directories such as ~/.local/bin, ~/go/bin, ~/.cargo/bin, ~/bin, /opt/homebrew/bin, /usr/local/bin, /usr/bin, and /bin. When the login shell binary itself does not exist yet, which happens on macOS with nix-darwin when the daemon starts at login before /run/current-system has been recreated, startup waits for it with backoff for up to a minute before giving up. If login-shell resolution still fails or returns no entries, the daemon logs a warning and uses an augmented process-environment fallback that may omit version-manager directories such as nvm, fnm, or volta. Restart the daemon to pick up a login shell that appeared or changed after startup. On Windows it reuses the current process environment.

If your env vars aren’t set in your login shell’s rc files (.zprofile, .zshrc, .profile, .bash_profile, .bashrc, PowerShell profile), the daemon won’t see them. Put them somewhere a login shell will load, then restart the daemon to pick them up.