Configuration

neondeck keeps mutable local state outside the repository. The app resolves runtime home from NEONDECK_HOME, then XDG_CONFIG_HOME/neondeck, then ~/.config/neondeck.

Runtime home

~/.config/neondeck/
  .env
  config.json
  mcp.json
  repos.json
  dashboard.json
  dashboard.schema.json
  SOUL.md
  skills/
    custom-skill/
      SKILL.md
  data/
    backups/
    neondeck.db
    flue.db

data/neondeck.db stores app state: watches, scheduled tasks, notifications, memories, workflow summaries, config history, and execution approvals, and ChatGPT OAuth credentials. data/flue.db stores Flue runtime state. On POSIX systems, Neondeck protects the data/ directory and the Neondeck database, including existing WAL and shared-memory sidecars, before accessing OAuth credentials. Neondeck app database migrations are shipped with the package and apply automatically before the app opens data/neondeck.db. Pre-migration backups are retained in data/backups/; Neondeck retains the latest two recognized backup sets. Use neondeck db backup to make a snapshot and neondeck db restore <backup-name> only after stopping Neondeck.

Secrets

Packaged and local app secrets live in runtime-home .env. Repo-root .env is supported as a checkout-only fallback when runtime-home .env does not define a value. Process environment variables still take precedence.

KILOCODE_API_KEY=...
KILOCODE_ORGANIZATION_ID=...
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
GITHUB_TOKEN=...
GITHUB_LOGIN=...

Trusted dashboard origins

Neondeck accepts dashboard API traffic from loopback hosts by default. To run the dashboard behind an authenticated reverse proxy, add each externally visible browser origin to server.trustedOrigins in config.json. Merge this block into the existing file; preserve its generated localApi.token and other settings:

{
  "version": 1,
  "server": {
    "trustedOrigins": ["https://example.exe.xyz"]
  }
}

Origins are exact scheme, host, and port matches. Wildcards, credentials, paths, query strings, and fragments are rejected. Non-loopback origins must use HTTPS. Restart Neondeck after changing this startup-time setting.

Models and providers

Agent model choices and provider registration live in config.json. Model strings must be provider-qualified and use a built-in or explicitly configured provider: kilocode, openai, anthropic, openai-codex, or a validated OpenAI-compatible provider id.

Choose models

{
  "models": {
    "displayAssistant": "openai-codex/gpt-5.6-sol",
    "displayAssistantThinkingLevel": "medium",
    "prReview": "openai/gpt-5.5",
    "prReviewThinkingLevel": "medium",
    "prReviewTimeoutMs": 1800000,
    "subagents": {
      "explore": "openai/gpt-5.6-terra",
      "exploreThinkingLevel": "medium"
    }
  }
}

Register providers

{
  "providers": {
    "openai": {
      "enabled": true,
      "apiKeyEnv": "OPENAI_API_KEY"
    },
    "openaiCodex": {
      "enabled": true
    },
    "openaiCompatible": [
      {
        "id": "openrouter",
        "enabled": true,
        "baseUrl": "https://openrouter.ai/api/v1",
        "apiKeyEnv": "OPENROUTER_API_KEY",
        "api": "openai-completions",
        "contextWindow": 200000,
        "maxTokens": 8192
      }
    ]
  }
}

Compatible endpoint rules

  • Credentials: provider config stores environment variable names, never raw secrets. apiKeyEnv is optional, so keyless loopback servers work too.
  • Limits: contextWindow and maxTokens are optional positive integers describing the selected service and model.
  • URLs: endpoints require HTTPS except for loopback HTTP. URL credentials, query strings, fragments, reserved ids, and duplicate ids are rejected.
  • Ownership: endpoint definitions may be changed through setup or the local access-controlled dashboard/API, never through a model-callable config action.

ChatGPT OAuth tokens live in the local app database and are managed with neondeck auth ... openai-codex.

Model roles and fallbacks

Setting Work it powers Default behavior
displayAssistant Chat and model-backed Autopilot owners Primary model
prReview Initial and follow-up read-only PR review Uses displayAssistant
utility Titles, labels, notifications, and compact summaries Uses displayAssistant
selfImprovement Reflection, memory curation, and retrospectives Uses utility, then displayAssistant
subagents.explore Focused read-only repository investigation for chat, PR review, and Autopilot Uses displayAssistant

Reasoning and runtime behavior

  • thinkingLevel accepts off, minimal, low, medium, high, or xhigh.
  • prReviewTimeoutMs defaults to 1,800,000 milliseconds and accepts values from 10,000 through 1,800,000. Initial and follow-up reviewers may delegate focused evidence gathering to explore; the parent reviewer still owns the final review or response.
  • Provider registration and standalone CLI ChatGPT login or logout require a server restart.

Explore subagent

explore is a shared Flue subagent available to the display assistant, initial and follow-up code reviewers, and active Autopilot owners. It runs in a fresh context and receives only the complete task briefing written by its parent. Its model and reasoning level are independently configurable through models.subagents.explore and models.subagents.exploreThinkingLevel.

When those settings are absent, Explore uses the display-assistant model and reasoning level. During neondeck init, setup offers an optional cheaper model for this role and recommends a fast profile such as OpenAI Luna with high reasoning or OpenAI Terra with medium reasoning.

Parent agents use one Explore task for narrow or dependent research. When several investigations are independent, they may launch up to three distinct Explore tasks in one tool-call batch; Flue runs that batch concurrently, and the parent reconciles the returned evidence before it acts or answers.

PR reviewer prompts

Human review has two independently configurable full system prompts: initial-review for each fresh bounded review submission and follow-up-reviewer for the durable read-only reviewer chat. Inspect, replace, or reset either prompt in the dashboard's Runtime panel, through typed config tools, or through the local access-controlled API.

{
  "prReview": {
    "prompts": {
      "initial-review": "Complete replacement prompt…",
      "follow-up-reviewer": "{{reviewContextDeliveryGuidance}}\n{{workspaceToolGuidance}}"
    }
  }
}

Overrides are non-empty strings up to 40,000 characters. The initial prompt receives PR facts separately as untrusted JSON and has no template tokens. The follow-up prompt supports {{workspaceToolGuidance}} and {{reviewContextDeliveryGuidance}}. These expand to stable policy explaining the workspace tools and per-question signal delivery; mutable review facts are delivered separately in the review-context signal. Initial changes apply to the next review run; follow-up changes apply to the next turn, including an existing reviewer conversation. There is no prompt versioning or stale-session state.

Set a prompt to null through neondeck_config_update_pr_review_prompt or POST /api/pr-review/prompts to restore its built-in default. The override replaces the whole shipped prompt, including its safety and review guidance. That footgun is intentional. The read-only workspace tools, structured result schema, and GitHub delivery boundary are still selected and enforced by the server rather than by prompt text.

Autopilot owner prompts

Full owner system prompts are configurable independently for prepare-only, autofix-with-approval, and autofix-push-when-safe. Use the prompt editor in the dashboard's Runtime panel, ask Neon through the typed config actions, or use the local access-controlled API. The editor shows the effective prompt and can reset an override to the built-in default. notify-only has no template because it never starts an owner mutation turn.

{
  "autopilot": {
    "prompts": {
      "prepare-only": "Complete replacement prompt using {{mode}} and {{workspaceInstructions}}",
      "autofix-with-approval": "Complete replacement prompt…",
      "autofix-push-when-safe": "Complete replacement prompt…"
    }
  }
}

Overrides are non-empty strings up to 20,000 characters. Supported substitutions are {{source}}, {{mode}}, {{status}}, {{capabilities}}, and {{workspaceInstructions}}. Existing owners use a saved change on their next turn; no prompt versioning or stale-owner state is created. Set a mode's prompt to null through neondeck_config_update_autopilot_prompt or POST /api/autopilot/prompts to restore the default.

The override replaces the entire shipped owner prompt. This is a powerful local footgun by design. Prompt text still cannot expand the tools supplied for the current turn or bypass server-side delivery guards; those capabilities are derived from current watch state and enforced outside the model prompt.

Learning

Learning config controls memory writes, skill patch policy, PR retrospective thresholds, review policy, and bounded memory curation. Memory uses active user, local, and project scopes; active sessions are marked stale after memory or skill changes rather than being silently rewritten.

{
  "learning": {
    "enabled": true,
    "memoryWriteMode": "auto",
    "skillWriteMode": "auto",
    "memoryCurationEnabled": true,
    "memoryCurationMode": "review",
    "memoryCurationTurnInterval": 200,
    "prRetrospectiveThreshold": 5,
    "maxPrBatchItems": 8,
    "memoryMaxActiveItems": 200
  }
}

Repositories

repos.json maps repo ids to local git checkouts and GitHub repository metadata. A local checkout is required for local git status and repo diagnostics. GitHub API features use GITHUB_TOKEN and do not expose the token to the browser.

Work in progress: exe.dev settings describe an experimental approved-command backend for an existing VM. They do not select a remote workspace for chat, schedules, reviews, managed worktrees, or Autopilot. Keep local execution as the default unless you are deliberately testing that limited command path.

Dashboard

dashboard.json selects theme, layout regions, plugin ids, and plugin config. The checked-in dashboard config and schema are copied into new runtime homes as defaults.

{
  "notifications": {
    "toasts": {
      "enabled": true,
      "soundEnabled": true,
      "minimumLevel": "ready",
      "readyDurationMs": 3600000,
      "maxVisible": 3
    }
  }
}

Ready notifications remain visible for one hour by default. Set readyDurationMs to 0 to keep them visible until dismissed, or choose a nonzero duration from 1 second through 24 hours. Attention and urgent notifications do not auto-timeout. Newly created notifications play a brief chime by default when they meet the toast policy. Set soundEnabled to false to mute it. A browser may require one click or keypress before it allows audio.

Schedules and watches

Scheduled tasks, their execution history, and PR watches are persisted in SQLite so they survive restart.

Watched-PR Autopilot reads shared guardrails from config.json plus any repository override in repos.json. Configure repository policy through the typed Autopilot policy action rather than editing metadata in chat. The Autopilot guide explains the mode-specific delivery ceiling and semantic autonomous judgment. Effective guardrails.requiredChecks values are optional hints for the continuing owner; they are not an autonomous delivery prerequisite. The same setting remains enforceable in separate `/fix-ci`, Kilo, and prepared-diff verification flows.

SOUL and skills

SOUL.md defines the agent name, emoji, and vibe. User runtime skills live under skills/, plus any external skill roots listed in config.json. Start a new session or restart the server after changing SOUL, model choices, memory, or skills so prompt context reloads deliberately.