ngn configuration reference¶
This page describes the current source-checkout ngn harness, which can differ
from an installed release or another coding assistant. See
release availability and installation and the installed
release notes before assuming every current field or behavior is available.
Quick example¶
Configuration is JSON. Keep general settings in config.json and named connections
in providers.json. profiles and the optional providers file reference are
the nested settings in the general config:
{
"providers": "./providers.json",
"model": "gpt-6-luna",
"agent": "assistant",
"theme": "terminal",
"animations": true,
"max_subagent_depth": 2,
"profiles": {
"audit": {
"mode": "reviewer",
"instructions": "Prioritize regressions and missing tests. Do not edit files."
}
}
}
Unknown fields, wrong JSON types, invalid values, and literal api_key fields
are errors rather than silently ignored options.
The checkout includes an example pair at examples/harness/config.json and
examples/harness/providers.json. To try it
without provider calls, run from the repository root:
Precedence¶
The order is lowest to highest priority:
- Built-in defaults.
- The active named connection in
$XDG_CONFIG_HOME/ngn/providers.json, when present. NGN_*environment defaults, includingNGN_PROVIDER_IDfor a named selection.- Global/user JSON.
- Trusted project JSON.
- An explicitly selected JSON file.
- Explicit CLI flags.
Environment variables are not the highest-priority override. For example,
NGN_MODEL=environment-model loses to "model": "file-model" in global JSON;
--model cli-model overrides that top-level setting. A flag omitted from the
command line leaves the loaded value intact.
Files are layered, not exclusive: --config does not suppress the global file
or a project file allowed by --trust-project. Re-selecting the global file with
--config puts it at explicit-file priority, after the trusted project file.
An explicit file must exist; absent global and project files are fine.
Each loaded file must be valid JSON with valid field types. CLI flags are applied after configuration loading and validation, so a CLI override is not a way to repair a malformed or invalid lower-priority configuration. Likewise, invalid environment conversions can fail before a file replaces the value.
Profile model selection happens at runtime after loading the top-level
configuration; see profiles before combining a profile-specific
model with --model.
Shared named provider connections¶
ngn and ngn serve share $XDG_CONFIG_HOME/ngn/providers.json (default
~/.config/ngn/providers.json) for global connections. Use Settings → Global →
Provider connections to edit them and choose the default. Settings → Workspace →
Provider connections manages connections and a selection for this workspace;
its JSON lives at $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/providers.json.
The workspace list includes global connections, which can be selected without
changing the global default. Use global default clears a workspace selection.
The TUI /provider menu also offers both scopes. Without an active connection,
ngn serve starts for setup, but chat waits until one is selected. You can edit either JSON while
ngn is stopped. A revision prevents an older UI from overwriting another
process's edits. A separate connection can be selected for a trusted agent profile using
profiles.NAME.provider: CONNECTION_NAME.
Provider connections contain kind, authentication source, endpoint where needed,
HTTP API and environment variable names. Chat models are chosen separately:
the model field in global $XDG_CONFIG_HOME/ngn/config.json or workspace
$XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/config.json provides shared
defaults for the CLI, TUI and web UI. Trusted JSON model selections, agent-profile models,
NGN_MODEL and explicit --model overrides retain their precedence. Switching
connections does not change the chat model. Voice duplex preferences are edited
separately in GPT-Live → Voice settings: Global supplies defaults, and the
workspace may override individual fields. Use "api_key_env": "OPENAI_API_KEY" or
"api_key_env": "${OPENAI_API_KEY}"; ngn resolves the variable when it makes a
request. It never writes the key value to this file. Set environment variables
in the environment of the ngn process (including the ngn serve process),
not in the browser. For Microsoft Entra ID, "auth": "entra" uses the optional
azure-identity package's DefaultAzureCredential chain; its SDK credential
owns token caching and refresh. OpenAI supports auth: chatgpt for ngn's device
login and "auth": "codex" for the library's local Codex discovery (CODEX_HOME or
~/.codex). These existing OAuth/Codex files are not copied into the provider
registry. "auth": "auto" prefers an existing ngn ChatGPT login or local Codex
configuration before an OpenAI API-key environment variable. Auto auth is
available only for OpenAI; Codex auth uses only local Codex discovery. OpenAI
uses a fixed API host; use openai_compatible for custom endpoints.
Web voice uses the normal provider registry
and defaults; an explicit Voice connection can select a different Live-capable
provider. The browser sends audio only to ngn, which owns provider authentication
and connections. ChatGPT/Codex login uses the server's native Codex media/control
path, while API-key and Entra connections use supported Live WebSockets. Subscription
tokens are never exposed to the browser or used as public Live API keys.
Example:
{
"version": 2,
"revision": "0000000000000000000000000000000000000000000000000000000000000000",
"active": "work",
"providers": {
"work": {
"kind": "foundry",
"base_url": "https://resource.openai.azure.com/openai/v1",
"api": "responses",
"auth": "entra",
"request_timeout": 120.0
}
}
}
request_timeout is the total deadline, in seconds, for each ngn-issued model
HTTP request on this named connection, including connection setup and streaming
response reads. It accepts positive finite integers or decimals; omission keeps
the existing 120-second default. Booleans, numeric strings, zero, negative
values, and nonfinite values are rejected. There is no additional upper cap.
The same setting applies to API-key, ChatGPT/Codex, and Foundry/Entra model
requests, including agents and native children selecting this connection.
Editing an active connection replaces its HTTP client so the new deadline takes
effect on subsequent requests. Global and workspace connection scope works as
for its other fields. Model catalogs retain their separate cap of
min(request_timeout, 30) seconds.
This field does not change shell limits, whole-run or child deadlines, Live
session duration, Live backend waiting limits, or authentication SDK deadlines.
Existing retries remain unchanged; retries and backoff can make a generation
last longer than one request deadline. There is no top-level Harness JSON field
or NGN_REQUEST_TIMEOUT environment alias. Use the named connection's JSON,
web provider editor, or TUI /provider editor.
The UI generates a fresh revision on save and writes indented JSON. Workspace JSON uses the same schema:
its active may reference a global connection, and an empty active inherits
the global default. Other provider types (openai,
openai_compatible, openrouter, anthropic, gemini, litellm, both Azure
routes) have their own authentication/API/endpoint requirements; the UI shows
the allowed fields for the selected type. A provider catalog is a best-effort
list of model IDs, not a guarantee of access. Providers without a catalog or a
compatible /models endpoint require manual model entry. Saving a named
connection does not perform network authentication.
To place the registry elsewhere, set "providers": "./providers.json" in
config.json. A reference in the user config selects the global provider file;
a reference in a trusted project or explicit config selects the workspace file.
Use "providers": {"global": "./shared.json", "workspace": "./team.json"}
to set either scope explicitly. Paths are relative to the containing config
file. Omitted references use the default providers.json paths. An explicit
reference to a missing file starts with an empty registry; the UI creates that
file on save. The references must name distinct .json files. The full resolved
provider definitions are visible as config.providers during CLI startup.
Voice defaults are stored in $XDG_CONFIG_HOME/ngn/voice.db, and workspace
overrides in the workspace session database. Provider connections contain no
chat or Voice model and no API-key values.
Create a named connection in the web UI or TUI and set its environment variable; the provider editor does not offer an API-key-value field.
File locations and trust¶
| Item | Location or selection | Behavior |
|---|---|---|
| Global/user JSON | $XDG_CONFIG_HOME/ngn/config.json; defaults to ~/.config/ngn/config.json when XDG_CONFIG_HOME is unset or empty |
Automatically trusted and loaded if present. |
| Global provider registry | $XDG_CONFIG_HOME/ngn/providers.json; defaults to ~/.config/ngn/providers.json |
Shared across workspaces by web, TUI, and headless ngn; no API key values. |
| Workspace provider registry | $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/providers.json |
Workspace-only connections and selection; inherits global connections and default. |
| Workspace chat-model preference | $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/config.json |
The workspace model field overrides the global model preference; no credentials. |
| Project JSON | <workspace>/.ngn/config.json |
Ignored with a diagnostic unless --trust-project is supplied or that exact file is selected explicitly. |
| Explicit JSON | --config /any/path/settings.json |
Any filename/location ending in .json is accepted. Selecting it explicitly trusts it, including endpoint and plugin settings. |
| Workspace | --workspace PATH or -C PATH; defaults to the shell's current directory |
Must already be a directory. Determines project configuration, file-tool boundaries, and session scope. |
| State root | data_dir; default $XDG_DATA_HOME/ngn or ~/.local/share/ngn when XDG_DATA_HOME is unset or empty |
Stores workspace-scoped session databases. |
| OpenAI login store | $XDG_DATA_HOME/ngn/auth/openai.json or ~/.local/share/ngn/auth/openai.json |
Separate credential store, not a general config setting or a session database. Changing data_dir does not move it. Never include it in a config example or bug report. |
| Provider login store | $XDG_DATA_HOME/ngn/auth/login.json or ~/.local/share/ngn/auth/login.json |
Login credentials beside the ChatGPT store; this does not select a named provider. ngn logout removes it. Never include it in a config example or bug report. |
The workspace's .ngn/config.json is the project file; ngn does not search every
ancestor directory for more configuration layers. The trust decision is made
from CLI arguments before project configuration is read. There is no
trust_project = true JSON escape hatch or NGN_TRUST_PROJECT shortcut.
ngn --workspace /path/to/project --trust-project
ngn --workspace /path/to/project --config /path/to/project/.ngn/config.json
ngn --workspace /path/to/project --config /another/location/review.json
The second command explicitly trusts the selected project file without needing
--trust-project. The third trusts only the explicitly selected file in addition
to global configuration; it does not implicitly trust a different workspace
project file.
Review trusted configuration and its referenced Python code. It can redirect requests and select executable plugins. There is no safe-settings subset loaded from an untrusted project file: the entire file is skipped.
Relative paths¶
--workspace,--config, and file paths passed with--pluginresolve from the shell's current working directory, not from each other.-Cselects a workspace; it does not change how another CLI path is resolved.data_dir, provider references, and.pyplugin paths written in JSON resolve from the directory containing that JSON file. In.ngn/config.json,"data_dir": "state"means.ngn/state, andplugins: ["../extension.py:setup"]refers toextension.pyat the workspace root.NGN_DATA_DIRis a path relative to the shell's working directory unless absolute. It is the full state root; ngn does not append another/ngnto it.- Supported path values expand
~and resolve to absolute paths. Use absolute XDG environment paths. General$VARIABLEor${VARIABLE}interpolation in JSON strings is not supported. - An installed-module plugin such as
my_package.extension:setupis imported from the selected Python environment, not resolved as a filesystem path.
Complete top-level schema¶
Defaults below are built-ins, before environment, JSON, CLI, profile activation, or authentication-route selection. String choices are case-sensitive.
Model and named provider selection¶
| Field | JSON type | Default | Accepted values and meaning |
|---|---|---|---|
provider_id |
string | "" |
Optional named connection from providers.json; otherwise the registry's active connection is used. |
providers |
string or object | omitted | Relative .json file reference for this scope, or {"global": "path.json", "workspace": "path.json"}. Omitted scopes use their default providers.json. The web UI saves the selected file. |
model |
string | "gpt-6-astra" |
Nonempty after trimming whitespace; a model ID supported by the endpoint/account, or an Azure deployment name. No model-catalog validation occurs at startup. |
Connection details—kind, api, base_url, api_key_env, auth, and
api_version—belong only to entries under providers.json → providers.NAME.
They are rejected as top-level config.json fields. The resolved connection is
available by name at CLI startup: config.provider is the selected name and
config.providers maps names to provider definitions.
At the CLI's serve(config, ...) breakpoint, inspect config.provider
and config.providers[config.provider] for its kind, auth, api, base_url,
api_key_env, and api_version. The api value selects the provider's HTTP
protocol, such as messages, responses, or chat_completions; allowed values
depend on the provider kind and authentication mode. There is no connection
wrapper or provider_registry field, and no flat config.auth or API fields. General
settings such as config.model, config.workspace, and limits remain separate.
The profile's key_env property resolves the environment-variable name;
loading configuration does not read its secret value.
| Provider string | Aliases | Default endpoint/behavior |
|---|---|---|
openai_compatible |
openai |
https://api.openai.com/v1; API-key requests use the OpenAI-compatible protocol. |
openrouter |
None | https://openrouter.ai/api/v1; choose a model exposed by OpenRouter. |
anthropic |
None | https://api.anthropic.com/v1; native Messages protocol with api: auto. |
gemini_native |
gemini, google |
https://generativelanguage.googleapis.com/v1beta; native Gemini protocol with api: auto. |
azure_openai_compatible |
azure |
Explicit Azure resource endpoint and nonempty api_version required. |
azure_openai_compatible_v1 |
None | Explicit Azure endpoint required; use the endpoint form expected by that route, rather than assuming the versioned Azure configuration is interchangeable. |
foundry |
None | Azure AI Foundry OpenAI-compatible v1; explicit API prefix; API key or Microsoft Entra ID. |
litellm |
None | Explicit base_url required. This is a connection to your gateway, not an embedded proxy or a LiteLLM SDK dependency. |
api: auto retains provider-specific routing: Chat Completions for
OpenAI-compatible, OpenRouter, LiteLLM, and Azure; Messages for Anthropic; native
Gemini for gemini_native. The three explicit API choices refer to /chat/completions, /responses, and
/messages respectively under the configured API base, where that provider/endpoint
supports the route. Native Gemini routing is not one of those OpenAI/Anthropic-style
paths. OpenAI-compatible, OpenRouter, and LiteLLM support selecting the explicit
contracts, subject to server/model support. Anthropic accepts only auto or
messages; Azure accepts only auto or chat_completions; native Gemini requires
auto. OpenAI-compatible messages requires an explicit compatible base_url
rather than the default OpenAI endpoint.
auth: auto prefers a saved ChatGPT login for the default OpenAI provider
endpoint only when api: auto; otherwise it uses api_key_env. "api-key" forces API-key selection.
"chatgpt" requires an ngn login and uses the dedicated Codex Responses route,
not a general-purpose OpenAI API credential. It requires provider: openai
or "openai_compatible" without base_url and with api: auto. Explicitly
combining auth: chatgpt with an api override is rejected, even for
api: responses. When explicitly selecting an API-key protocol, set
auth: api-key alongside api instead of relying on automatic login
selection. Saved OAuth credentials are never sent to a custom endpoint,
including LiteLLM. Do not use api to attempt to
redirect a ChatGPT login to a gateway or to the ordinary paid API.
The key's value is read from the variable named by the active connection
when a live request needs it. Put the variable name in providers.json, not
config.json; no API-key value belongs in either file. ngn
does not automatically load .env files or borrow another coding assistant's
credentials.
Agent and tool limits¶
| Field | JSON type | Default | Accepted values and meaning |
|---|---|---|---|
agent |
string | "assistant" |
The built-in "assistant" or an explicitly configured profile name. |
read_only |
boolean | false |
Enforce read-only operation for the assistant and child agents. |
shell_timeout |
integer or float | 60.0 |
Seconds, strictly greater than 0 and at most 600. Booleans are not numbers here. |
max_output |
integer | 32768 |
1024 through 1048576, inclusive; bounds tool output in bytes. |
max_file_bytes |
integer | 262144 |
1024 through 4194304, inclusive; file-size limit in bytes for guarded file operations. |
max_tool_rounds |
integer | 30 |
1 through 1000, inclusive; maximum model/tool rounds in the agent loop. This is not the subagent count. |
max_subagent_depth |
integer | 2 |
0 through 8, inclusive. Root depth is 0, child depth 1, grandchild depth 2. 0 disables delegation; the default allows children and grandchildren, not great-grandchildren. |
demo |
boolean | false |
Offline scripted provider, no workspace mutations or shell execution, and no plugin imports. Local demo conversations are still saved. |
Integer fields reject floats and booleans even if numerically equivalent. Subagent concurrency, per-run job counts, timeouts, and result-size budgets are additional safeguards, not extra JSON fields. See subagents.
The model receives the active shell timeout, output cap, and whole-file read
limit in the native tool descriptions. These descriptions refresh when settings
change and when a child harness starts. A shell call may omit timeout or use
0 for the configured default; a positive value cannot exceed shell_timeout.
Reading fewer lines does not bypass max_file_bytes: guarded reads take a
snapshot of the whole file. Invalid arguments and limit failures return tool
errors for the model to correct; they do not automatically relax limits or retry
the operation.
Terminal behavior¶
| Field | JSON type | Default | Accepted values and meaning |
|---|---|---|---|
theme |
string | "terminal" |
"terminal", "graphite", "ocean", or "ember". |
theme_background |
string | "auto" |
"auto", "terminal", or "theme". Automatic preset behavior, terminal-native background, or the theme's background. Separate from palette selection. |
animations |
boolean | true |
Enable motion; false disables the busy animation, animated scrolling, and cursor blinking. TEXTUAL_ANIMATIONS=none also disables them; brief button feedback remains. |
submit_mode |
string | "queue" |
"queue" or "interrupt": queue new prompts during work, or cancel/await current work before sending the new prompt. |
tab_action |
string | "agent" |
"agent", "complete", or "focus": cycle profiles, complete slash commands, or navigate widgets. |
These are ngn settings, not arbitrary Textual theme names or CSS tables. Preset
colors can evolve; there is no user-defined palette table in this schema.
With theme_background: auto, terminal uses native terminal surfaces and
the other presets use painted surfaces. "terminal" retains native surfaces for
any preset, with preset-specific ANSI accents. "theme" paints the background;
the terminal preset then uses a dark graphite-based surface fallback.
Browser GPT-Live preferences are configured in GPT-Live → Voice settings at either scope. See GPT-Live setup.
Storage and extensions¶
| Field | JSON type | Default | Accepted values and meaning |
|---|---|---|---|
data_dir |
string path | $XDG_DATA_HOME/ngn or ~/.local/share/ngn |
Full session-state root; converted to an absolute Path. JSON-relative paths use the file's directory. |
mcp_servers |
mapping | {} |
Named stdio MCP servers (command, args, optional env and cwd). Higher-priority maps replace lower-priority maps. |
plugins |
list of strings | [] |
Ordered "path.py:setup" or "installed.module:setup" references. Converted to a tuple internally. Each higher-priority JSON list replaces the previous list. |
profiles |
mapping of profile mappings | No custom profiles | Entries under profiles.NAME, merged by name with whole-profile replacement. Built-in profiles remain available. |
workspace (Path), trust_project (bool, default False), and diagnostics
(tuple[str, ...], initially empty) also exist on the Python HarnessConfig
object. They are runtime/loader inputs or outputs, not accepted JSON fields.
Select workspace and trust through the CLI; diagnostics are generated by loading.
Environment defaults¶
Every configurable top-level scalar field in the tables above has an
NGN_<UPPERCASE_FIELD_NAME> environment default. This includes
NGN_API, NGN_MAX_SUBAGENT_DEPTH, and NGN_THEME_BACKGROUND as well as
provider, model, limits, and state storage.
NGN_THEME=ocean NGN_ANIMATIONS=false ngn --demo
NGN_SUBMIT_MODE=interrupt NGN_MAX_SUBAGENT_DEPTH=1 ngn
NGN_PROVIDER_ID=team-openai ngn --model gpt-6-luna
JSON can override every one of these defaults. Booleans accept case-insensitive
true/false or 1/0 in the environment. Integer fields are converted with
integer parsing; shell_timeout uses floating-point parsing. Normal value/range
validation still applies.
There is no NGN_PLUGINS list or NGN_PROFILES mapping parser, no per-profile
environment-variable convention, and no NGN_CONFIG, NGN_WORKSPACE, or
NGN_TRUST_PROJECT configuration mechanism. Use JSON, --config, --workspace,
--trust-project, and repeatable --plugin flags as appropriate. Unknown
NGN_* environment names are not a substitute for supported fields.
Agent profiles¶
assistant is the only built-in agent and is reserved. Other agents come from
JSON profiles or discovered Markdown and TOML definitions.
Use read_only: true (or NGN_READ_ONLY=true) to constrain the assistant and its
children to read-only operation. This startup restriction cannot be relaxed by
switching profiles or saving web preferences. Select assistant in JSON and
NGN_AGENT; use read_only for its read-only mode. Other names must refer to
explicitly configured custom profiles.
| Profile | Mode | Purpose |
|---|---|---|
assistant |
build |
Default interactive agent and default delegation target. Guarded changes and shell require approval; child agents inherit their parent's permission ceiling. |
Custom profile names match [A-Za-z0-9_-]{1,128}. Each profile accepts these fields:
| Profile field | Default | Meaning |
|---|---|---|
mode |
"build" |
"build" for normal guarded operation or "reviewer" for read-only operation. These are permission modes, not additional built-in agents. |
instructions |
"" |
Trusted profile instructions, appended to the harness context. They do not grant extra permissions. |
model |
"" |
Optional model override on profile activation. Empty keeps the current/top-level model. |
provider |
"" |
Optional named connection ID in providers.json. Empty follows the active connection. The profile's model, if set, overrides the shared chat-model choice. |
description |
"" |
Description shown to the model and agent picker. |
tools |
omitted | Tool-name array; omission inherits, [] permits no tools. |
denied_tools |
[] |
Additional tool-name denials. |
allowed_agents |
omitted | Delegation target-name array; [] disables delegation. |
user_invocable |
true |
Boolean controlling user selection. |
subagent_available |
true |
Boolean controlling delegation eligibility. |
{
"agent": "audit",
"profiles": {
"audit": {
"mode": "reviewer",
"instructions": "Report bugs with file references and explain missing tests."
},
"implementation": {
"mode": "build",
"instructions": "Make the smallest correct change and verify it."
}
}
}
Activate with --agent audit or /agent audit; Tab normally cycles profiles.
A nonempty profile model is applied when the harness activates that profile,
after top-level model loading, so it can also supersede the value supplied by
--model. Leave a profile's model empty if you want top-level model selection
to govern it. In-process /model changes are runtime state, not edits to JSON.
Merge and replacement rules¶
Profiles merge by name, not recursively by field. Different names from
different files remain available, but a later profiles.audit entry constructs a
new profile and replaces the earlier audit completely. Omitted fields in that
replacement return to the profile defaults.
For example, if global JSON defines audit with mode: reviewer and a model,
then a trusted project file containing only:
replaces it with a build-mode profile and an empty model override. It does
not inherit mode: reviewer. Repeat the intended mode when redefining a
read-only profile. An empty "profiles": {} mapping does not clear previously defined
profiles, and no profile deletion syntax is provided.
Delegation defaults to agent, rather than silently forcing every child into
reviewer. Child authority can never exceed the parent's effective permissions:
a reviewer parent cannot create a build-capable child by selecting agent,
build, or a custom profile. Depth limits count the root as zero. Profile
instructions cannot bypass approvals, depth limits, or the permission ceiling.
MCP servers and live reload¶
Both ngn serve and the TUI load MCP tools through the same harness. Configure
stdio servers in the user config or an explicitly trusted project config:
{
"mcp_servers": {
"docs": {
"command": "python",
"args": ["/opt/my-mcp-server.py"],
"cwd": ".."
}
}
}
cwd resolves relative to the configuration file; omitted cwd uses the workspace.
command and args are executed directly, without a shell. Servers inherit the
ngn process environment; optional env entries override it. Keep secret values
in the deployment environment instead of committing them to configuration.
Remote HTTP/SSE transports are not supported by this stdio loader.
The harness rereads the trusted configuration sources, tool selections, discovered
skills and applicable instructions after every completed model response, even
when files are unchanged and even for a final answer. It reloads Python extension
source and reuses healthy MCP connections whose declaration, direct script source,
and effective process environment are unchanged. A server's tools/list_changed
notification triggers fresh discovery on the same connection at the next boundary.
Added, changed and removed definitions become
available without restarting the UI or losing conversation history. User-message
boundaries also refresh. A changed server can briefly have overlapping old and new
instances while calls finish. The tool-batch boundary ensures that a tool's own
configuration/source edits are visible to the immediately following model request.
Tool calls returned by a response execute against that response's original implementation and schema. Its old MCP process stays alive until those calls and approvals finish; current permissions still apply. Saved tool allowances match captured source/configuration/schema identities, so unchanged reloads preserve permissions and changed implementations require approval again. Changes to project instructions invalidate old file-edit snapshots. Configuration parsing and Python extension errors retain the last working generation and produce a warning. MCP startup and discovery failures are isolated per server: other healthy tools, including newly added servers, stay available. An unavailable server is skipped at later response, tool, and message boundaries until its configuration, direct script source, or effective environment changes, or an operator explicitly retries. Environment values enter only a private digest; diagnostics never expose them. New configuration owners and parallel chat runtimes inherit known failures so saving settings or opening a chat does not silently retry a failed process. Cancellation/shutdown closes owned processes. Offline demo starts no MCP servers, and read-only profiles cannot launch custom server subprocesses. Native child agents do not inherit parent MCP servers.
A newly created <workspace>/.ngn/config.json does not become trusted just because
it was written from the web chat. Without --trust-project or an explicit selection
of that file, the running harness ignores it. Server logs report the trusted reload
locations; Tools → MCP discovery and ngn doctor show the ignored source.
An operator can merge the specifically approved MCP declaration into an
already trusted user/host configuration, preserving its other settings. Preserve
relative path semantics when moving a declaration: an explicit absolute cwd is
useful. Restart with --trust-project only when trusting the entire project
configuration is intended.
Reload locations preserve their logical paths. For example, a process started with
--config /etc/ngn/config.json follows a replacement Kubernetes ConfigMap ..data
symlink on its next reload instead of retaining an obsolete timestamped target.
Relative provider files, data directories, Python plugin paths, and MCP working
directories in an explicit symlink config retain their target-directory meaning.
Each extension reload resolves that logical source again and reads the same target
used as its relative-path base. Startup-only settings still require their normal
restart or settings action.
ngn serve logs an MCP discovery summary at startup and on every reload, including
unchanged settings and zero configured servers. The summary includes configured
and connected server counts, registered/available tool counts, logical ngn config
paths and their read/trust status, and the effective MCP source. ngn doctor and
the web Tools → MCP discovery section show the same metadata. When a reload
fails as a whole, the counts and source list describe the retained working generation
and the failure is reported separately. A partially available generation lists each
unavailable server with its safe category and recovery guidance. Retry in
Tools → MCP discovery attempts that server again; during active work it queues
the attempt for the next safe boundary. Healthy connections remain open, and
settings stay available while a retry is waiting. An attempt can still fail; its
warning remains visible until recovery or removal from the trusted configuration.
A higher-priority "mcp_servers": {} still clears
earlier declarations; the effective source makes that precedence visible. Warnings distinguish configuration parsing, MCP
startup and tool discovery failures. Tool-call authorization failures are separate
from registration. Diagnostics do not echo server arguments, environment values,
request/response bodies or credentials.
Startup provider/configuration details are logged once when the harness initializes
and remain available through ngn doctor. They are not repeated in chat or Live
delegation responses. New resource reload failures still produce an actionable
conversation warning; their current status remains visible in MCP discovery.
Workspace .vscode/mcp.json and opencode.json files produce an ignored-config
server log and appear in MCP discovery when present. ngn checks their existence
without reading or importing their contents. Add only the approved mcp_servers
declarations to a trusted ngn config;
foreign files do not grant project trust. An explicitly selected --config path
still follows ngn's configuration schema, regardless of its filename.
Verify activation in the running web Tools catalog and a subsequent model
request. A successful connection from a separate MCPManager or CLI process only
proves that separate process can discover the server. Tool discovery also does not
prove that the server is authorized to call GitHub or another external service.
Reloading never expands trusted paths or replaces provider/login, session storage, permission ceilings, task ownership, or host approval handlers. Newly created files are discovered only at the trusted paths selected at startup. Changes to those host settings still use their existing settings/restart mechanisms.
Plugins¶
The setup name must be a Python identifier. Installed module names must be
dot-separated identifiers. References are checked when loading configuration;
Python import and setup happen when the harness initializes and at each model
response boundary, not during JSON parsing. A setup function may be synchronous or asynchronous and may return an
AgentPlugin or None.
JSON plugin lists replace the previous list, including "plugins": [] to clear
lower-priority entries. Repeated --plugin flags append to the final JSON
list in CLI order. There is no automatic deduplication of plugin references;
listing one twice can invoke setup twice and can fail on duplicate registrations.
ngn --config /path/to/settings.json --plugin /path/to/extension.py:setup
ngn --plugin examples/harness/commands.py:setup
Plugins are fully trusted Python code, not a sandbox or just prompt text. They
can access the network and filesystem during import/setup. ngn doctor normally
initializes them too, so it is not a safe way to inspect unknown Python code.
ngn doctor --demo skips plugin imports but still validates configuration and
initializes local demo state. Subagents do not re-import parent plugin files.
See Python extensions for the actual hook and
command interfaces.
Provider recipes¶
These snippets are provider entries under providers.json → providers.NAME;
the active name belongs in that file's active field. Set the chat model in
config.json or the model selector. Put API-key values in your shell or secret
manager, not in these files, URLs, or command arguments.
OpenAI API key¶
For eligible ChatGPT/Codex access instead, leave base_url unset, use
"auth": "auto" or "chatgpt", and follow device login.
That route can select a different account-supported default model.
OpenRouter¶
{
"kind": "openrouter",
"api": "chat_completions",
"auth": "api-key",
"api_key_env": "OPENROUTER_API_KEY"
}
The default endpoint is https://openrouter.ai/api/v1; no base_url override is
needed. Set the referenced key in your launcher environment. Choose a model your
OpenRouter account exposes and check that it supports tools. An explicit
environment-file launch through uv is shown in the
installation guide.
Anthropic¶
Native Gemini¶
LiteLLM gateway¶
{
"kind": "litellm",
"base_url": "http://127.0.0.1:4000/v1",
"api": "chat_completions",
"auth": "api-key",
"api_key_env": "LITELLM_API_KEY"
}
Configure the base URL and choose a model alias for your running server; the example
does not start a gateway or guarantee a route exists. For a server exposing
Responses or Messages, choose responses or messages and the corresponding
API base. The library also exposes legacy Completions, but ngn rejects that
text-only route during harness setup. Gateway authentication uses the
referenced LITELLM_API_KEY, never an ngn ChatGPT OAuth token. Legacy Completions
remains text-only, not a promise of coding-tool support.
LiteLLM 1.100.0 has a legacy streaming usage-frame serialization issue. On that one provider/route combination, ngn does not request the optional extra usage frame; it still parses usage if the gateway supplies it. Error envelopes and incomplete streams remain errors, not successful responses.
Another OpenAI-compatible endpoint¶
{
"kind": "openai_compatible",
"base_url": "http://127.0.0.1:8000/v1",
"auth": "api-key",
"api_key_env": "LOCAL_MODEL_API_KEY"
}
Use the endpoint's required key in that environment variable. The harness still expects a nonempty referenced key for a live API-key request; do not assume it detects anonymous local servers automatically.
Inspect and diagnose¶
ngn doctor --demo provides local configuration diagnostics without model calls
or Python plugin imports. /context shows effective runtime context, and
/plugins shows configured/loaded extensions. A real ngn doctor loads trusted
plugins, which can perform arbitrary I/O. These commands can create local state;
they are not side-effect-free JSON parsers.
If a setting is unexpected, check the global file, the workspace selected with
-C, whether project trust was granted, the explicit file, and CLI flags in that
order. Also check profile-specific models and runtime /model or /agent
changes. Do not attach credential files, session databases, .env contents, or
literal API keys to a diagnostic report.