Configuration is merged in this order, with later layers taking precedence:
- Built-in defaults.
- User config at
~/.config/noah-code/config.toml. - Project config at
.noah-code/config.toml. NOAH_CODE_*environment variables.- CLI flags.
The first TUI launch opens one guided provider → API key → model → reasoning flow before starting
the agent. Noah Code saves the selected model as the top-level model in
~/.config/noah-code/config.toml, making it the default for every repository, and stores provider
credentials separately in its private auth file. The classic --console frontend retains a
line-oriented model prompt for environments that cannot open the TUI.
An explicit noah --model MODEL on the first launch is saved without prompting. Project config,
NOAH_CODE_MODEL, and later --model flags still override the user default according to the
precedence above. Non-interactive commands such as noah run, doctor, and config show never
open the onboarding prompt.
Inside an interactive session, switch only the current session or replace the global default:
/model
/model openai/MODEL_NAME
/model --global anthropic/MODEL_NAME
Bare /model opens a guided TUI flow: search for a provider, enter its API key in a masked
field, enter the model ID, and select reasoning effort. Noah saves the credential in
~/.local/share/noah-code/auth.json, using the same provider-keyed record shape as OpenCode.
The containing directory uses mode 0700 and the file uses mode 0600. If the file cannot be
written, the key remains active only in the current Noah process and the TUI says so. Keys are
never written to Noah configuration or session metadata. XDG_DATA_HOME relocates the data root.
Model switches take effect between turns and are stored in the current session metadata, so a
resumed session continues with its most recently selected model. A session-only /model MODEL
does not change the user configuration or affect new sessions.
Enter /model for the common provider → API key → model flow. Run noah providers list or open
/providers in the TUI for advanced, secret-free setup. Noah supports
LiteLLM's provider routing and includes guided presets for OpenAI, Anthropic Claude, OpenRouter,
Google Gemini, Groq, Mistral, xAI, DeepSeek, Together AI, Perplexity, Azure OpenAI, Amazon
Bedrock, and local Ollama.
For scripting, export credentials before starting Noah and select a provider/model:
# OpenAI
export OPENAI_API_KEY="..."
noah providers add openai --model MODEL_NAME
# Anthropic Claude
export ANTHROPIC_API_KEY="..."
noah providers add anthropic --model MODEL_NAME
# OpenRouter
export OPENROUTER_API_KEY="..."
noah providers add openrouter --model PROVIDER/MODEL
# Google Gemini (GOOGLE_API_KEY is also accepted)
export GEMINI_API_KEY="..."
noah providers add gemini --model MODEL_NAMEUse --no-set-default to print a one-launch command without changing Noah's global default.
Inside Noah, /providers use openrouter PROVIDER/MODEL switches the current session and saves the
new global default.
For vLLM, LM Studio, a company gateway, or another OpenAI-compatible API, create a secret-free NOOA model alias:
export COMPANY_LLM_API_KEY="..."
noah providers add custom \
--alias company-llm \
--model MODEL_ID \
--base-url https://llm.example.com/v1 \
--api-key-env COMPANY_LLM_API_KEYThe alias is stored in ~/.config/nooa/llm_config.yaml with mode 0600. Only the environment
variable's name is stored. For an unauthenticated local endpoint, omit --api-key-env. You can
then use the alias anywhere a model is accepted: noah --model company-llm . or
/model company-llm.
Example user configuration:
model = "gpt-4o-mini"
reasoning_effort = "default" # default, none, minimal, low, medium, high, or xhigh
lightweight_model = "gpt-4o-mini"
mode = "build"
max_iterations = 40
cell_timeout = 120
command_timeout = 60
max_output_chars = 16000
[efficiency]
profile = "fast" # "fast", "balanced", or "deep"
strategy = "lean" # "standard" is the comparison fallback
deterministic_titles = true
lazy_mcp = false # true catalogs servers without attaching them at start
max_output_lines = 250
max_search_results = 100
max_file_results = 500
tool_output_retention_hours = 24
subagent_result_max_chars = 4000
max_concurrent_subagents = 3
[lsp]
enabled = true
timeout_seconds = 5
max_symbols = 300
# Trusted user config may override a language server command:
# servers.python = ["basedpyright-langserver", "--stdio"]
[processes]
max_jobs = 8
max_runtime_seconds = 86400 # 24 hours
max_buffer_chars = 64000
stop_grace_seconds = 2
[sampling]
# Omit values to use provider defaults.
# temperature = 0.2
# top_p = 0.95
# seed = 42
[budget]
# Persistent session-wide limits; omitted values are unlimited.
# max_tokens = 500000
# max_cost_usd = 25
# max_seconds = 28800
[checkpoints]
enabled = true
max_per_session = 50
capture_before_mutation = true
[reliability]
auto_resume_interrupted_runs = true
interaction_timeout_seconds = 86400
artifact_max_bytes = 2000000000
session_max_bytes = 5000000000
max_runtime_events = 20000
workspace_lease = true
[reliability.retries]
max_attempts = 5
base_delay_seconds = 0.5
max_delay_seconds = 20
jitter_ratio = 0.2
request_timeout_seconds = 180
fallback_models = []
[hooks]
# pre_tool = [{ match = "ws_*", command = "./scripts/pre-tool-check" }]
# post_tool = [{ match = "*", command = "./scripts/audit-tool" }]
[ui]
theme = "atom-one-dark" # atom-one-dark, noah-ocean, graphite, or high-contrast
frontend = "tui" # "tui" or "console"
markdown = true
show_reasoning = false
[summarization]
policy = "token_budget" # or "none"
trigger_ratio = 0.35
preserve_recent = 6
target_chars = 2500
[tracing]
enabled = true
# Local traces remain available without a collector.
jsonl_enabled = true
# jsonl_dir = "~/.local/share/noah-code/traces"
# Send traces, metrics, and structured logs over OTLP/HTTP.
# otlp_endpoint = "http://localhost:4318"
logs_enabled = true
metrics_enabled = true
# Sensitive agent content is excluded unless explicitly opted in.
capture_content = false
max_span_attributes = 256
max_attribute_length = 4096
metric_export_interval_millis = 30000
export_timeout_millis = 5000
[updates]
auto_install = false
interval_hours = 24Supported environment overrides include:
NOAH_CODE_MODELNOAH_CODE_LIGHTWEIGHT_MODELNOAH_CODE_REASONING_EFFORTNOAH_CODE_AUTONOAH_CODE_SESSION_DIRNOAH_CODE_MODENOAH_CODE_EFFICIENCYNOAH_CODE_UNSAFE_INPROCESSNOAH_CODE_AUTO_UPDATE
OpenTelemetry's standard environment variables are also honored. In particular,
OTEL_EXPORTER_OTLP_ENDPOINT enables remote traces, metrics, and logs without storing an endpoint
in Noah configuration. Signal-specific endpoint variables and OTEL_EXPORTER_OTLP_HEADERS are
supported by the OTLP exporters; keep authentication headers in the environment or a collector
secret store rather than TOML.
Repository-controlled configuration cannot weaken the host trust boundary. Project config is
ignored for auto_approve, budget, efficiency, enabled_skills, hooks, lsp, mcp,
permission_rules, processes, reliability, session_dir, tracing, updates, and
unsafe_inprocess_code_execution. Put those settings in trusted user config, the environment, or
an explicit CLI flag. Language-server overrides and hooks are user-only because they launch local
executables; reliability and budget settings are user-only so repository content cannot weaken
host limits.
A user-configured permission_rules array replaces the default rule array. Copy forward every
default you still want before adding overrides. Hard secret, destructive-shell, and plan-mode
gates remain enforced in code.
Inspect the resolved configuration from the CLI or inside an interactive session. /config
lists every nested path, while an optional path scopes the output. Values whose names look like
credentials are redacted.
Configuration is validated strictly: unknown keys (including typos such as theem under [ui])
and invalid values or TOML syntax fail every command with a one-line error naming the file and
field, never a traceback. Fix the named key or remove it; noah doctor reports the same error.
noah config show ./config
/config summarization
/config updates.auto_install
Model and provider configuration follows NOOA conventions, including its model registry,
environment variables, and configuration under ~/.config/nooa/. Provider strings not shown in
the guided list still pass through to LiteLLM, so additional supported services can be selected
with --model PROVIDER/MODEL.
Reasoning effort is passed through NOOA to LiteLLM only when it is not default. Supported values
are none, minimal, low, medium, high, and xhigh, but each provider/model may support
only a subset. Change the current session or the cross-repository default with:
/reasoning
/reasoning high
/reasoning --global low
For headless launches use --reasoning-effort high, or add
--reasoning-effort high to noah providers add ... when saving a global model default.
fast is the default: 16,000 characters and 250 lines per model-facing tool result. Configured MCP
servers from trusted user configuration attach at start so their tools are in the prompt; set
efficiency.lazy_mcp = true to catalog them instead. The agent runs until it finishes; if a
provider reports a context overflow, Noah compacts eligible history and retries that step once.
balanced raises the preview to 24,000
characters/400 lines. deep permits legacy-sized 80,000-character previews. max_iterations is a
safety rail (default 40), not an efficiency-profile cap. Switch without restarting:
/efficiency
/efficiency balanced
/efficiency deep
Oversized results are not discarded. During a durable session Noah writes the exact output to a
private, content-addressed artifact store, returns a bounded head/tail preview, and gives the agent
an output ID for focused line-range retrieval. Artifacts remain available when the session is
resumed and count toward reliability.artifact_max_bytes and session_max_bytes. A truncated file
preview is never returned as an editable Match anchor. tool_output_retention_hours applies to the
fallback cache used when workspace tools are embedded without a durable session runtime.
Set lightweight_model to route compaction to a faster or cheaper model. If it is omitted, that
route follows live /model switches. Compaction starts at 35% of the active main model's context
window by default, preserves the six newest events, and writes a coding checkpoint covering the
objective, decisions, files, validation, blockers, and next steps.
[budget] limits are cumulative across the parent agent, lightweight route, and custom-model
subagents. Token, cost, and elapsed-time counters survive process restarts. When a configured cap
is exceeded, the breach is sticky and later model calls fail before contacting the provider.
Git checkpoints are enabled by default and use rolling retention: once the configured maximum is
reached, Noah removes the oldest ref and continues capturing instead of silently stopping.
capture_before_mutation protects shell-driven changes; workspace-tool edits additionally use
durable pre-images and the persistent /undo journal.
[reliability.retries] controls transient model-call retries and ordered fallback models. Noah does
not retry authentication, invalid-request, content-policy, or context-window failures through this
route. [reliability] also controls crash-run resumption, interaction timeouts, session and artifact
quotas, event retention, and the exclusive checkout lease. See
Reliability and long-running sessions for the recovery sequence and operational
limits.
| Mode | Behavior |
|---|---|
build |
Routine workspace reads, edits, inspection, web research, skills, and delegation proceed automatically; higher-risk operations ask |
plan |
Reads are allowed; file edits and mutating shell commands are denied. self.plan.write may pin .noah-code/plan.md |
Switch modes with --mode, /mode build, or /mode plan. The agent can propose a switch with
self.plan.enter() / self.plan.exit_to_build() after writing a plan; the transition always asks
for confirmation and is never auto-approved, so in noah run --auto it is refused and the session
stays in plan mode. The active mode is stored with the session.
Permission rules are evaluated in order, and the last matching rule wins. The default policy:
- Allows ordinary reads.
- Denies likely secrets, including
.envvariants, private keys,.gitinternals, credential stores (.npmrc,.pypirc,.netrc,.pgpass,.kube/config,.docker/config.json,.aws/credentials), Java/JCEKS key stores, and session databases..env.exampleremains readable. The same denials apply to Git object syntax (git show HEAD:.env) and to patch-output Git commands with no path scope (git log -p, baregit show), which ask instead of being auto-approved. - Allows workspace edits, read-only in-workspace shell inspection, web reads, skills, and delegated tasks without interrupting the turn.
- Asks before arbitrary shell execution, external paths, MCP access, and remote mutations.
- Allows the question tool so the agent can pause for a structured choice.
- Denies
git push,git clean,git reset --hard, and mutatinggh pr(create,checkout,merge,close,ready,review). Push and PR mutations go through/pr/self.githubinstead. - Allows listing and viewing pull requests; asks before create, push, checkout, or comment.
- Keeps file tools inside the active workspace and asks before MCP access.
- Denies plan-mode mutations regardless of broader allow rules. Plan mode may still run read-only subagents. Its shell allowlist accepts only literal, unqualified read-only programs; test collection and interpreter execution are not treated as read-only because they can load repository code.
--auto changes routine ask decisions to allow but never overrides an explicit deny.
Elevated-risk commands such as file removal, downloads, and package installation still require
explicit approval: interactively they ask, and in non-interactive noah run --auto they are
rejected outright instead of being silently approved. Compound shell commands and mutating or
unrecognized Git commands cannot be silently auto-approved. Interpreters, eval/source commands, and
indirect execution wrappers, plus arguments hidden behind variable, command, ANSI-C, or brace
expansion, are denied under --auto; run without --auto when one of these commands needs
explicit approval.
The README executes the checked-in bootstrapper directly from GitHub. From a local clone, run the same installer with:
sh install.shInstalls created by the one-line command check PyPI at most once every 24 hours. If a release is
available, a new TUI session shows a temporary banner and keeps the version in the context rail.
Run noah update when ready. When an update is installed, Noah exits before starting the task so
it cannot mix old and new runtime modules. Rerun the command to continue on the new version.
# Check without changing the installation
noah update --check
# Update immediately
noah updateUpdates are notification-only by default. Enable unattended installation from trusted user configuration or the environment only when that behavior is desired:
[updates]
auto_install = true
interval_hours = 24export NOAH_CODE_AUTO_UPDATE=1