CLI
Documentation

Version Latest

Enterprise deployment

Roll the CLI out to an organisation with a managed settings file, a signed remote policy, the organisation policy from Govern, an audit log, OpenTelemetry export and network controls.

Administrators control the CLI through managed layers, which sit above every user and project setting:

Managed layerWhere it comes fromWorks today
Managed settings fileA file on each machineYes
Remote policyA policy document served over HTTPS, named by the managed fileYes
Organisation policyGovern, after the person signs inNot yet: the current Govern server does not serve the policy route. See Sign in to Govern
Governance policy bundleManaged settings, the shared governance folder or the repositoryYes. See Permissions and modes

Managed settings file

OSPath
Linux/etc/neuralinverse/managed-settings.json
macOS/Library/Application Support/NeuralInverse/managed-settings.json
Windows%ProgramData%\NeuralInverse\managed-settings.json

NEURALINVERSE_MANAGED_SETTINGS names another path.

The file uses the normal settings format. Every top-level key it sets replaces the user's value and is locked. For permissions, each field is locked on its own: the policy's deny and ask lists are combined with users' lists (users can add restrictions, never remove them), while other fields, such as allow, mode or disableFullAuto, replace the user's value. Set "allow": [] to forbid personal allow rules entirely.

{
  "providers": {
    "corp": { "kind": "openai", "baseUrl": "https://llm-gateway.example.com/v1", "apiKeyCommand": "corp-llm-token" }
  },
  "model": "corp/gpt-5",
  "permissions": {
    "disableFullAuto": true,
    "deny": ["read_file(.env)", "read_file(**/*.pem)", "run_shell(curl *)", "run_shell(wget *)", "web_fetch"]
  },
  "sandbox": { "enabled": true, "required": true, "allowNetwork": false },
  "audit": { "enabled": true },
  "hooks": { "tool.before": [{ "matcher": "run_shell", "command": "/opt/corp/ni-command-policy" }] }
}

neuralinverse config validate shows which keys a policy locks.

Settings honoured only in a managed layer include keystone.clientId, keystone.policyKeys, keystone.requireSignIn, governance.*, toolEnvironment, workspaceBindingPolicy, activity.required, activity.content, several checks.* keys and embed.*. Each is described on its own page.

Remote policy

Instead of distributing the whole policy to every machine, the managed file can point at a central policy document served over HTTPS:

// /etc/neuralinverse/managed-settings.json
{
  "remotePolicyUrl": "https://policy.example.com/neuralinverse/policy.json",
  "remotePolicyRequired": true,              // refuse to start without a policy (fail closed)
  "remotePolicyPublicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA…\n-----END PUBLIC KEY-----",
  "remotePolicyTokenCommand": "corp-sso-token --audience ni-policy"   // optional bearer token
}
  • The URL can also come from NEURALINVERSE_MANAGED_SETTINGS_URL when the managed file sets none; the managed file always wins. Only https:// is accepted (plain http:// only for loopback hosts, such as a local mirror). Redirects are not followed.
  • The document uses the normal settings format and is validated against the settings schema. It is applied at the managed layer, above the local managed file, with the same locking rules. It cannot set the remotePolicy* keys itself.
  • A bearer token is sent when NEURALINVERSE_MANAGED_SETTINGS_TOKEN is set or remotePolicyTokenCommand prints one.
  • The policy is fetched at start-up (5-second timeout, at most 1 MB) with If-None-Match, and the last good copy is kept in ~/.neuralinverse/managed/remote-policy.json. If the server cannot be reached, the cached copy is used with a warning; with remotePolicyRequired and no cached copy, the CLI exits with an error.
  • Long sessions refresh the policy every 60 minutes. When it changes, a notice is shown; new deny rules take effect at once, everything else at the next start.
  • Signatures. With remotePolicyPublicKey (an Ed25519 key as PEM, base64 SPKI DER, or base64 of the raw 32-byte key), the server must also publish <url>.sig: the base64 Ed25519 signature of the exact policy bytes. Unsigned or wrongly signed policies are rejected, and the cached copy is verified again on every start, so editing it locally cannot loosen the policy.

Sign a policy with OpenSSL:

openssl genpkey -algorithm ed25519 -out policy-signing.pem
openssl pkey -in policy-signing.pem -pubout -out policy-signing.pub   # goes into remotePolicyPublicKey
openssl pkeyutl -sign -inkey policy-signing.pem -rawin -in policy.json | base64 > policy.json.sig

Signed organisation policy

When a person signs in to Govern, the CLI fetches the organisation's policy from GET <api>/api/v1/policy/bundle, verifies it with the keys pinned in keystone.policyKeys, and applies it at the managed layer. The current Govern server does not serve this route yet, so this section describes the CLI's side of the contract. See Sign in to Govern.

Two forms are accepted, both signed with Ed25519 by a pinned key:

  1. The signed bundle envelope shared with the Editor's checks: { "payload": { "formatVersion": 1, "orgId", "version", "issuedAt", "expiresAt", "packs": [], "waivers": [], ...CLI fields }, "signature": { "algorithm": "Ed25519", "keyId", "value" } }, signed over the canonical JSON of the payload.
  2. A bare bundle with a detached signature over its exact bytes, in the X-Keystone-Signature: <keyId>.<base64> header or at <url>.sig.

The CLI fields:

{
  "orgId": "org_123",
  "orgName": "Acme",                       // optional, shown in the model picker and header
  "version": 42,                           // never lower than a version already accepted
  "issuedAt": "2026-10-01T00:00:00Z",
  "expiresAt": "2026-11-01T00:00:00Z",
  "offlineGraceHours": 72,
  "keyId": "ks-2026",                      // bare form only
  "settings": { "permissions": { "deny": ["run_shell(curl *)"] }, "sandbox": { "enabled": true } },
  "models": {
    "allowed": ["keystone/claude-sonnet-5-5", "keystone/gpt-5", "ollama/*"],
    "personalModels": false,
    "gateway": { "baseUrl": "https://models.acme.example/v1", "kind": "openai" }
  },
  "content": {
    "skills":   { "release": "---\nname: release\ndescription: ...\n---\n..." },
    "agents":   { "security-reviewer": "---\nname: security-reviewer\n---\n..." },
    "commands": { "ship": "---\ndescription: Ship a release\n---\n..." },
    "rules":    { "deny": ["web_fetch(domain:pastebin.com)"], "ask": [], "allow": ["read_file"] }
  }
}

A bundle is rejected as a whole when its signature does not verify with a pinned key, it has expired, its orgId differs from the signed-in account's organisation, or its version is older than one already accepted (or the same version with different content).

PartEffect
settingsApplied at the managed layer like managed settings: they win over user and project settings and lock the keys they set. keystone and the remotePolicy* keys are ignored
models.allowedFilters the model picker and neuralinverse models; any other model is refused in /model, --model, routing and the fallback chain. Entries are provider/model refs, bare model ids or globs with *
models.gatewayAdds the provider keystone (OpenAI- or Anthropic-shaped), called with the person's Govern access token. OpenAI shape: requests go to <baseUrl>/chat/completions; Anthropic shape: <baseUrl>/v1/messages
models.personalModels: falseDisables every user-configured provider
contentWritten to a managed, read-only folder (~/.neuralinverse/managed/keystone/content) and loaded with organisation scope: organisation skills, agent profiles and commands win over project and user files of the same name. Organisation deny and ask rules are added at the managed layer; organisation allow rules are added to the user's allow list

Offline behaviour and the locked state are described in Sign in to Govern.

Audit log

With "audit": { "enabled": true } (or NEURALINVERSE_AUDIT_LOG=<path>), every session writes an append-only JSON Lines trail, by default ~/.neuralinverse/audit/<UTC date>.jsonl with mode 0600. audit.path sets another location.

EventContent
session_start, session_endUser, host, working folder, version, model, mode
permissionTool, target (path, command or URL), decision, and whether a rule, the mode, a hook or the user decided
toolTool, summary, duration, success, and a SHA-256 of the full input. Commands, paths, URLs and queries are recorded; file contents are not
mode_change, rule_added, hook_blocked, mcp_connectAs named
checks_gateEach checks gate run: fingerprints, rule ids and severities, never code or messages
keystone_policyChanges of the organisation policy state between online, cached and locked

Ship these files to your SIEM with your usual log forwarder. The audit log is local and does not depend on Govern; the activity record is a separate feature that sends events to Govern.

OpenTelemetry

Telemetry is off by default and has no built-in destination. To send traces, metrics and logs to your own collector (OTLP over HTTP with JSON encoding):

{
  "telemetry": {
    "enabled": true,
    "endpoint": "https://otel-collector.example.com:4318",
    "headers": { "Authorization": "Bearer ${OTEL_TOKEN}" },
    "resourceAttributes": { "team": "payments" },
    "includePrompts": false
  }
}

NEURALINVERSE_TELEMETRY=1 turns it on, and OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, the per-signal _ENDPOINT and _HEADERS variables, OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME and OTEL_SDK_DISABLED=true are honoured.

SignalContent
Tracesni.prompt per prompt, with ni.llm.turn (model, tokens, stop reason) and ni.tool (tool, duration, outcome) children
Metricsni.session.count, ni.prompt.count, ni.tokens, ni.cost.usd, ni.tool.calls, ni.tool.duration, ni.permission.denials, ni.retries, ni.compactions, ni.lines.changed
LogsPrompt submitted (text only with includePrompts, secrets redacted), permission decisions, errors

Tool inputs and outputs are never exported.

Network and credentials

  • Route all model traffic through an internal gateway with a custom openai, anthropic or bedrock provider and baseUrl, and lock providers and model.
  • Keep keys out of files with apiKeyCommand (any secrets-manager command) or apiKeyEnv.
  • HTTPS_PROXY, NO_PROXY and NODE_EXTRA_CA_CERTS are honoured.
  • Nothing is sent anywhere except the model providers, MCP servers, your Govern deployment when people sign in, and an OpenTelemetry collector if you configure one. The only other request is the daily npm registry version check, off with NEURALINVERSE_DISABLE_UPDATE_CHECK=1 and in CI.
  • For a self-hosted or air-gapped Govern deployment, set keystone.baseUrl in managed settings. To install without internet access, mirror the release assets and set NI_DOWNLOAD_BASE (Install and update).
  1. A managed settings file with disableFullAuto, deny rules for secrets, and audit.enabled.
  2. sandbox.enabled and sandbox.required on Linux and macOS workstations.
  3. A tool.before hook for organisation-specific command policy.
  4. A shared .neuralinverse/ folder in each repository with AGENTS.md, commands, and allow rules for the project's own test and lint commands.
  5. If you use Govern, keystone.baseUrl and, when people must be signed in, keystone.requireSignIn.

For Govern's own deployment, see Deploy and operate.

Last reviewed 17 October 2026View as Markdown

Enterprise deployment · Docs · Neural Inverse