Enterprise

Configuration

Boot configuration, platform settings, and model providers.

Configuration follows one discipline: the instance should be governable from the admin UI at runtime, with as little reason to touch a server as possible. Neuralis configuration is split into four tiers with a clear ownership rule: .env holds only what the process needs to boot, the platform config store holds runtime settings an admin edits from the UI, long-lived secrets live exclusively in the encrypted credential store, and the Docker topology lives in a setup-generated docker-compose.yml (see deployment). For runtime values the precedence is: stored platform value, then environment fallback, then the built-in default.

Tier 1: .env — boot-critical only

The environment file is written by pnpm neuralis:setup and read at process start. It contains nothing a user would reasonably edit at runtime; changing it requires a restart. The important variables:

VariablePurpose
NEXTAUTH_SECRETSession encryption secret. Required; generated by setup.
NEXTAUTH_URLPublic app URL (defaults to http://localhost:3100).
NEURALIS_APP_PORT / MCP_HTTP_PORTApp and external-MCP ports (3100 / 3101).
NEURALIS_HOMEData root (in-container: /.neuralis).
NEURALIS_HOST_HOMEHost-side pair of the data root, required in Docker so file metadata carries host paths instead of container paths.
QDRANT_URLVector database endpoint.
QDRANT_API_KEYThe vector-database API key. Setup writes it: minted automatically for the compose-managed and binary modes (and preserved across re-runs), asked for when you point at an external instance — leave it empty only for an external, unauthenticated one. The compose-managed Qdrant ENFORCES the key on every request; the compose file hands it over by interpolation, so the key itself lives only in .env. It lives there rather than in the credential store because the vector client is constructed synchronously at boot, before any credential can be decrypted. The running platform sends it on every vector-database call — memory and sync, the admin panel's health probe, vector status page and vector reset — pnpm reset:vector, which lists and drops collections, and pnpm neuralis:qdrant-upgrade, which hands it to each upgrade step through a private file, never on a command line. The setup-time reachability probe deliberately does not send it: it shares its HTTP helper with the Ollama probe, so an unconditional key there would leak the key to a different service. It calls only the health and root endpoints, which Qdrant's own auth whitelist leaves unauthenticated. A key mismatch is not a boot failure: the platform falls back to in-memory vectors and logs a structured unauthorized line until the key matches.
NEURALIS_CODEX_LOOPBACKDocker only: on publishes the ChatGPT sign-in callback on the host's 127.0.0.1:1455, so a browser on the Docker host can finish the sign-in directly (off by default). Setup asks it on a Docker install; to change it later, re-run setup or edit .env and regenerate the compose. While the stack runs, Docker then holds that host port.
QDRANT_DASHBOARDSwitch for Qdrant's static dashboard UI on the loopback port (on / off, default on). Setting off and regenerating the compose disables the static UI entirely; the API is unaffected.
NEURALIS_BUILTINSOptional comma-separated list of full package ids to load instead of the discovered builtin set (slim deployments).
NEURALIS_BUILD_NPMRCOptional path to an .npmrc for a private package registry, on an install that builds its own image: scope lines only (@yourco:registry=<url> and its token line, never a default registry=). The image build mounts it as a build secret, never a layer; regenerating the compose refuses a missing file or a default registry line. See your own packages.

Tier 2: platform config store

Runtime platform settings live in a single JSON file in the platform zone (~/.neuralis/app/config/platform.json) behind a schema-validated store, and are edited from the admin widget's Config tab. Every key has a type, a default, and an editable flag; read-only keys surface in the UI as informational rows. Examples of what lives here:

  • the target embedding model id and vector dimension (the model the vectors are actually built with stays the active one until an owner rebuilds — see memory and sync),
  • custom LLM endpoints (your own hardware, or a keyed remote gateway) (Ollama-native or OpenAI-compatible),
  • custom embedding endpoints — any OpenAI-compatible /v1/embeddings server, edited in the admin Vector section; like the LLM endpoints, their keys live in the credential store, never in this file,
  • how many data checkpoints to keep (dataCheckpointKeep, see deployment),
  • runtime knobs such as the per-stream agent step cap and default temperature,
  • the chat composer clipboard caps — the maximum size of a pasted/dropped image and of pasted text (client-side paste guards only; they do not affect how much data a tool like fs_read returns),
  • platform limits such as the maximum number of projects.

Every key is declared by the package that consumes it (the manifest configSettings[] array — the host hardcodes no schema of its own), and the Config tab shows each key's declaring package, whether its current value comes from the default, an environment fallback, or an admin-set override, and any number bounds. The list can be filtered by that declaring package — one entry per package that declares keys, plus the host's own settings and the read-only environment rows. Only first-party packages can contribute keys: the host honors a configSettings[] declaration from a package it trusts by source (one of its own dependencies, whatever the npm scope), and skips one from a project-dropped or synced package, because an environment fallback would otherwise project an environment variable's value into the dashboard. Boot-critical environment values (ports, auth URLs, the vector database endpoint) appear in the same list as read-only rows served from an explicit host allowlist — secret values are never listed. Where a boot-critical variable is itself a secret, only its presence is reported: the vector database API key renders as (set) or (unset), never as the key.

Edits are validated against the declared schema (type + bounds), written atomically, and most take effect at runtime — no restart; keys that apply on restart or at client rebuild say so in their description. Many keys keep an environment fallback for first boot, but once a value is stored the stored value wins.

Tier 3: credential store

Anything secret — provider API keys, OAuth tokens, MCP API keys, skill-declared and custom secrets — belongs in the encrypted credential store, never in .env or platform config. The store is scope-aware (platform, user, project, agent) and managed from the admin Credentials tab. The full model is on the credentials page.

Model provider setup

LLM provider keys are credentials, not environment variables:

  1. During first-run setup, the wizard asks for provider keys interactively and writes them straight into the encrypted store. The runtime reads provider keys from the store only — there is no environment fallback, and the compose file carries no key entries (anything in a container's environment is readable via docker inspect).
  2. After that, manage keys from the admin Credentials tab. A key set at any scope is honored at stream time through most-specific-wins resolution (agent → project → user → global), so a project can carry its own provider key that overrides the platform default.
  3. Custom endpoints carry their own authentication: register an Ollama or OpenAI-compatible server in the admin Credentials tab and say whether it needs a key. One on your own hardware usually does not, and its models appear in the selector alongside the hosted providers. One that DOES — a remote gateway, say — stores its key like any other credential, resolved with the same scope cascade, and its models are offered only to callers for whom that key resolves. A row nobody has a key for is greyed out rather than failing mid-conversation, and the endpoints beside it are unaffected.

ChatGPT/Codex OAuth sign-in is the one special case: there is no key to paste, so it is connected through a sign-in flow from the Credentials tab (or a member's own Connect button in chat). The resulting login is stored like any other credential, at agent, project, user or global scope, and resolves through the same cascade. See providers and models for the provider catalog itself.

Tier 4: the generated docker-compose.yml

Docker deployments have one more setup-owned artifact: the compose file itself. pnpm neuralis:setup generates it next to .env with concrete machine-specific values (user/group IDs, ports, the Qdrant service or a direct QDRANT_URL depending on the chosen mode, an optional local Ollama service); pnpm neuralis:setup --compose-only regenerates it without re-asking anything. It is never hand-edited, and it never carries secrets — the single .env interpolation is NEXTAUTH_SECRET. Machine-local extra mounts live in a separate docker-compose.override.yml that survives regeneration. Details on the deployment page.

Logs: two planes, and the keys that level them

Neuralis writes logs to two places that are easy to confuse, and one level key (logLevel) behaves differently from all the others.

Docker / consoleFiles on disk
Wherecontainer stdout (docker logs)first-party package logs app/data/<package>/logs/*.jsonl, project-installed package logs projects/<id>/data/_installed/<slug>/logs/, per-agent run logs, plus the host's app/logs/audit.jsonl and app/logs/routes.jsonl
Lifetimeuntil the container restarts — nothing persistsrotated by size and age, survives restarts
Level keylogLevel (plus routeConsoleLogLevel for the route lines)packageLogLevel (+ packageLogLevelOverrides) and routeLogLevel
Takes effecton restartlive, on the next line written
Readable in-productnoyes — the admin Logs tab, and agent skills for the per-package files
  • packageLogLevel (default info) governs every package's own file log. packageLogLevelOverrides sets exceptions per package as <package>=<level> pairs (brain-core=debug,agent-core=warn), keyed on the pkg field of the log line; a malformed pair is ignored and that package follows the global.
  • routeLogLevel (default warn) governs the route log — one structured line per package-route dispatch: matched pattern, method, status, duration, caller, and which gate refused a call. It defaults to warn because several App surfaces poll every few seconds, so at info a single open shell tab writes tens of thousands of lines a day and the file rotates away within hours. Raise it for a debugging window, then put it back. At warn the file holds refusals, faults and every dispatch slower than routeSlowMs (default 1000 ms, marked slow: true whatever its status). The same lines are also written to container stdout through their own gate, routeConsoleLogLevel: empty (the default) follows routeLogLevel, so docker logs shows the same refusals, faults and slow requests; set it to warn with routeLogLevel=info to keep every request in the file and only the problems on stdout.
  • logLevel governs only container stdout. It changes nothing on disk.
  • The process monitor writes one perf.slow warning to container stdout when, over a sample window (perfSampleIntervalMs, default 30 s, applies on restart), the Neuralis process's CPU exceeds perfProcessCpuWarnPercent (default 80; 100 = one core, the Neuralis process only — not the container) or its longest single event-loop stall reaches perfEventLoopLagWarnMs (default 200 ms). Below the thresholds it writes nothing.

Because the file levels apply live, the working loop is: raise the level, reproduce the problem, read, put it back. Raising a level never recovers lines that were not written at the time.

Retention is what the rotation keys buy, not a time promise. Each plane has its OWN trio so neither is rotated on a budget set for the other: routeLogMaxBytes x routeLogMaxFiles x routeLogMaxAgeDays (25 MB x 5 x 90 days) for the route log, and packageLogMaxBytes / MaxFiles / MaxAgeDays for package logs. The audit log is the exception: auditLogMaxBytes (25 MB) only decides when audit.jsonl is renamed to a new generation — no generation is ever deleted.

What is hot and what needs a restart

ChangeTakes effect
Platform config edits (Config tab)At runtime, on the next read — except the keys whose own description says they apply on restart (or at client rebuild), which are read once as the service starts
packageLogLevel / packageLogLevelOverrides / routeLogLevel / routeConsoleLogLevel / routeSlowMs / perfSlowToolMsLive, on the next log line — no restart
logLevel (Docker/console)Process restart
Credential writes and deletionsImmediately — caches are invalidated on every store mutation, so a rotated key applies to the next stream
Role and feature grant changesImmediately, server-side on the next request
.env changes (ports, URLs, secrets, data root)Process restart
New or upgraded packagesContainer/image update for builtin-class packages; rescan for project packages

The configuration UI and the routes behind it are feature-gated (platform.config — a platform-tier feature with no default role grant); see administration for who can edit what.

On this page