AGENTS.md loaded about 33,000 tokens into every agent session, subagent and reviewer, mostly per-feature contracts, and the agent documentation around it had accumulated duplicates, dated logs and history of removed designs. This PR makes AGENTS.md a short set of rules that apply everywhere plus an index of area documents, and leaves AGENTS.md, dev-docs and the repository skills describing only the current state. Three commits, reviewable separately: 1. **Move (lossless).** Every per-area rule moved verbatim from AGENTS.md into its area document (existing ones, or six new ones: UI_CONVENTIONS, UI_SHELL, COMPOSER, TRANSCRIPT, SETTINGS, PROJECTS). A script split the old file into 208 units, required one destination for each and checked all 199 content units verbatim at their destination. AGENTS.md went from 134,000 to 16,000 characters. 2. **Trim.** A per-area ponytail review removed rules stated two or three times (keeping the more precise copy), dated validation logs, per-version qualification sections below the current minimum, abandoned designs and self-references; seven documents whose surviving rules live elsewhere were deleted. It also fixed statements the code contradicts (locale registration, the Mission play/recovery condition, plugin package updates, cleanup progress UI, timeline index persistence, automation restart/targets, stale pins and minimums). 3. **Current state only.** The new AGENTS.md rule: no history (removed features and their names, former behavior, provenance, dated logs) and no denials; correct a contradicted statement at its source. DEVELOPER_MODE.md is merged into BROWSER_AUTOMATION.md and every Developer Mode mention is gone (including the README and the browser help page in ten locales). Names that only made sense against older designs are renamed with every reference updated, MISSIONS_ENVIRONMENT_ADMISSION is folded into SESSION_ENVIRONMENT, and INDEX lists every document. The server npm README now states the real OpenCode minimum (2.1.2). AGENTS.md is now about 14,000 characters (roughly 3,500 tokens); AGENTS.md, dev-docs and the vendor README went from about 11,200 lines to under 4,700. Validation: every relative Markdown link and every cited dev-docs path resolves; no reference to a renamed or deleted document remains; `npm run test:help` passes for all ten locales. Review: three independent GPT-6.1 Sol (high) reviews by area found binding rules lost in the trim and current-state passes; 25 were restored as present-tense facts or prohibitions and four contradicting statements corrected, each checked against code. A Fable 5.1 gatekeeper review of the whole PR passed; its follow-up routes session recovery from the index, points to dev-docs/INDEX.md and documents the family identity read.
19 KiB
CodeNomad Server
CodeNomad Server connects the desktop and browser UI to your projects and one shared OpenCode V2 background service. It provides workspace access, Git operations, authentication, and live event delivery. OpenCode owns the sessions, messages, and agent execution; closing CodeNomad leaves the shared OpenCode service running.
Features & Capabilities
🌍 Deployment Freedom
- Remote Access: Host CodeNomad on a powerful workstation and access it from your lightweight laptop.
- Code Anywhere: Tunnel in via VPN or SSH to code securely from coffee shops or while traveling.
- Multi-Device: The responsive web client works on tablets and iPads, turning any screen into a dev terminal.
- Always-On: Run as a background service so your sessions are always ready when you connect.
⚡️ Workspace Power
- Multiple projects: Work across project tabs and sessions over the same OpenCode service.
- Long conversations: Bounded transcript loading, full-history search, and timeline navigation.
- Deep Task Awareness: Monitor background tasks and child sessions without losing your flow.
- Command Palette: A single, global palette to jump tabs, launch tools, and fire shortcuts.
Prerequisites
- Git: A prerequisite in the backend process's
PATHfor repository identity, Git operations and worktree management. Without Git, directory-only conversations in the explicitly opened physical folder remain available so the user can ask the agent for installation help. Owned session prompts/custom commands update the nativecodenomad.git-availabilityinstruction; it reports the backend platform and is removed when Git becomes available. Git/worktree operations still fail explicitly with503/git_required. Install from https://git-scm.com/downloads and restart the backend after changingPATH. A Windows per-user installation is sufficient; Git inside WSL alone is insufficient for a Windows backend. - OpenCode: install
opencode2.1.2 or later (minimum and recommended version). The npm package is@opencode/cli; see the installation guide. CodeNomad prefers the CLI onPATHand also supports an explicitly selected executable. It uses the official service lifecycle and validates authenticated daemon metadata and API compatibility; custom and prerelease versions undergo the same contract validation. Seeruntime-support.tsfor the current requirements. - OpenCode data: The global daemon owns its platform-default storage, database, and service registration. Configured startup environment applies only when CodeNomad starts a missing daemon; an existing daemon is unchanged.
- Windows to WSL: A configured WSL UNC binary uses Linux
service status,service start, andservice get password; Windows must have WSL localhost forwarding enabled to reach its loopback service. - Node.js and npm: Use Node.js 24 LTS for the standalone server; source and release builds use
.node-version(currently 24.20.0). Desktop distributions bundle Node/npm. - A workspace folder on disk you want to serve.
- Optional: a Chromium-based browser if you want
--launchto open the UI automatically.
Usage
Run via npx (Recommended)
You can run CodeNomad directly without installing it:
npx @neuralnomads/codenomad --password <your-password> --launch
Authentication required: The server requires a password. Pass it via
--password, theCODENOMAD_SERVER_PASSWORDenvironment variable, or create anauth.jsonfile (see Authentication below).
To list all CLI options:
npx @neuralnomads/codenomad --help
On startup, CodeNomad prints two URLs:
Local Connection URL : ...(used by desktop shells)Remote Connection URL : ...(used by browsers/other machines when remote access is enabled)
Install Globally
Or install it globally to use the codenomad command:
npm install -g @neuralnomads/codenomad
codenomad --password <your-password> --launch
Install Locally (per-project)
If you prefer to install CodeNomad into a project and run the local binary:
npm install @neuralnomads/codenomad
npx codenomad --password <your-password> --launch
(npx codenomad ... will use ./node_modules/.bin/codenomad when present.)
Common Flags
You can configure the server using flags or environment variables:
| Flag | Env Variable | Description |
|---|---|---|
--https <enabled> |
CLI_HTTPS |
Enable HTTPS listener (default true) |
--http <enabled> |
CLI_HTTP |
Enable HTTP listener (default false) |
--https-port <number> |
CLI_HTTPS_PORT |
HTTPS port (default 9898, use 0 for auto) |
--http-port <number> |
CLI_HTTP_PORT |
HTTP port (default 9899, use 0 for auto) |
--tls-key <path> |
CLI_TLS_KEY |
TLS private key (PEM). Requires --tls-cert. |
--tls-cert <path> |
CLI_TLS_CERT |
TLS certificate (PEM). Requires --tls-key. |
--tls-ca <path> |
CLI_TLS_CA |
Optional CA chain/bundle (PEM) |
--tlsSANs <list> |
CLI_TLS_SANS |
Additional TLS SANs (comma-separated) |
--host <addr> |
CLI_HOST |
Interface to bind (default 127.0.0.1) |
--workspace-root <path> |
CLI_WORKSPACE_ROOT |
Restricts the root path where new workspaces can be opened. Git worktrees are created in .codenomad/worktrees inside the project folder. |
--unrestricted-root |
CLI_UNRESTRICTED_ROOT |
Allow full-filesystem browsing (strict boolean, default off) |
--config <path> |
CLI_CONFIG |
Config file location |
--launch |
CLI_LAUNCH |
Open the UI in a Chromium-based browser (strict boolean, default off) |
--log-level <level> |
CLI_LOG_LEVEL |
Logging level (trace, debug, info, warn, error) |
--log-destination <path> |
CLI_LOG_DESTINATION |
Log destination file (defaults to stdout) |
--username <username> |
CODENOMAD_SERVER_USERNAME |
Username for CodeNomad's internal auth (default codenomad) |
--password <password> |
CODENOMAD_SERVER_PASSWORD |
Password for CodeNomad's internal auth |
--generate-token |
CODENOMAD_GENERATE_TOKEN |
Emit a one-time local bootstrap token for desktop flows (strict boolean, default off) |
--dangerously-skip-auth |
CODENOMAD_SKIP_AUTH |
Disable CodeNomad's internal auth (strict boolean, default off; use only behind a trusted perimeter) |
--ui-dir <path> |
CLI_UI_DIR |
Directory containing the built UI bundle |
--ui-dev-server <url> |
CLI_UI_DEV_SERVER |
Proxy UI requests to a running dev server (requires --https=false --http=true) |
--ui-no-update |
CLI_UI_NO_UPDATE |
Disable remote UI updates (strict boolean, default off) |
--ui-auto-update <enabled> |
CLI_UI_AUTO_UPDATE |
Enable remote UI updates (true) |
--ui-manifest-url <url> |
CLI_UI_MANIFEST_URL |
Remote UI manifest URL |
The five strict boolean environment variables above enable their switch only with 1, true, yes, or on, ignoring case and surrounding whitespace. 0, false, no, off, empty/whitespace-only values, and unset variables leave the switch off. Any other value refuses startup with an error naming the variable and accepted values (even when its CLI flag is present). Passing the argument-less CLI flag always enables the switch over a valid off environment value. Other value-taking options are unchanged.
In particular, CODENOMAD_SKIP_AUTH=false or =0 keeps authentication enabled: provide a password or use the local bootstrap setup. A merely defined variable no longer disables authentication.
Dev Releases (Advanced)
If you want the latest bleeding-edge builds (published as GitHub pre-releases), use the dev package:
npx @neuralnomads/codenomad-dev --password <your-password> --launch
Choose which releases are offered in Settings → Info → Updates → Update feed (server.updateFeed in the CodeNomad config): Stable releases or Previews and stable releases. Until you choose, builds labelled -dev-* (including legacy -dev-v2-*) start on the preview feed and other builds on the stable feed. The feed only selects offered releases: changing it never switches windows, drafts, desktop data or the backend. Signed Tauri Windows installer (NSIS), macOS app and Linux AppImage installations install the release selected by the same feed after an explicit confirmation; other installations, including Debian/RPM packages, open the release page.
| Env Variable | Description |
|---|---|
CODENOMAD_GITHUB_REPO |
GitHub repo used for preview release checks (default NeuralNomadsAI/CodeNomad) |
CODENOMAD_UPDATE_CHANNEL no longer affects update checks. Desktop hosts accept it only as a deprecated alias of CODENOMAD_PROFILE (see dev-docs/DESKTOP_DATA_PROFILES.md). A desktop host sets CODENOMAD_DESKTOP_PROFILE on the backend it launches for a non-default data profile; the server only displays it in Settings → Info.
HTTP vs HTTPS
- Default:
--https=true --http=false(HTTPS only). - To run plain HTTP only (useful for development):
codenomad --https=false --http=true
- To run both HTTPS (for remote) and HTTP loopback (for desktop):
codenomad --https=true --http=true
Remote Access Binding Rules
- When remote access is enabled (bind host is non-loopback, e.g.
--host 0.0.0.0):- HTTP listens on
127.0.0.1only. - HTTPS listens on
--host(LAN/all interfaces).
- HTTP listens on
- When remote access is disabled (bind host is loopback, e.g.
--host 127.0.0.1):- Both HTTP and HTTPS listen on
127.0.0.1.
- Both HTTP and HTTPS listen on
Remote Control
Remote Control reaches CodeNomad from anywhere without an inbound port, VPN or LAN binding. Turn it on in Settings → Remote Access, then scan the one-time pairing link (valid for five minutes) on the other device. It is independent of --host direct access; neither falls back to the other.
- Transport: CodeNomad claims a random route on the device's shared OpenTunnel identity (the default profile, also used by
opencode pair --remote). The relay forwards ciphertext only; the TLS certificate and its private key stay on this machine. The relay can still observe hostnames, connection timing and sizes.CODENOMAD_REMOTE_CONTROL_APIselects a self-hosted OpenTunnel API. - Authorization: Every remote request must carry a paired device's credential, even with
--dangerously-skip-auth; password login and the token bootstrap are not available remotely. Device credentials are stored hashed in~/.config/codenomad/remote-control-devices.json, expire after 30 days without use and can be revoked from Settings, which also ends that device's open streams. - Surface: Remote Control management, desktop window, SideCar, preview and automation endpoints stay host-only, and remote requests never receive loopback privileges.
- Lifetime: Remote Control stops with the backend. Closing the last desktop window quits the app and its backend, Remote Control included; an enabled Remote Control resumes at the next launch.
Self-Signed Certificates
If --https=true and you do not provide --tls-key/--tls-cert, CodeNomad generates a local certificate automatically under your config directory:
~/.config/codenomad/tls/ca-cert.pem~/.config/codenomad/tls/server-cert.pem
Certificates are valid for about 30 days and rotate automatically on startup when needed. You can add extra SANs via:
codenomad --tlsSANs "localhost,127.0.0.1,my-hostname,192.168.1.10"
Generated CA and server certificates use positive, nonzero serial numbers. On startup,
CodeNomad replaces an older generated server certificate with a negative or zero
serial while keeping its valid CA, so existing reverse-proxy trust remains usable.
If the generated CA itself has an invalid serial, has expired or must be replaced,
CodeNomad reissues the server certificate too and logs a warning: import the new
ca-cert.pem into clients/reverse proxies that trusted the old CA. Restart CodeNomad
after installing a version containing this fix to repair existing generated credentials.
Explicitly supplied certificates are not rewritten; their renewal remains your responsibility.
Browser warning: Self-signed certificates trigger a "Your connection is not private" warning in browsers on first visit. This is expected and safe for local development (127.0.0.1 / localhost):
- Chrome/Brave/Edge: Click Advanced → Proceed to 127.0.0.1 (unsafe)
- Firefox: Click Advanced → Accept the Risk and Continue
- Alternative: For local-only development without the warning, run with
--https=false --http=trueNote: Only accept self-signed certificates for localhost/127.0.0.1 that you control. For remote hosts, use proper TLS certificates.
Authentication
- Default behavior: CodeNomad requires a login (username/password) and stores a session cookie in the browser.
--dangerously-skip-auth/CODENOMAD_SKIP_AUTH=truedisables the login prompt and treats all requests as authenticated. Use this only when access is already protected by another layer (SSO proxy, VPN, Coder workspace auth, etc.). If you bind to0.0.0.0while skipping auth, anyone who can reach the port can access the API.
Setting a password
Practical setup options:
- Runtime password (every start): Use
--password <your-password>or setCODENOMAD_SERVER_PASSWORD=<your-password>environment variable - Persistent password (UI setup): Launch with
--generate-token, complete the local bootstrap flow in your browser, then set a password through the UI settings
The --password flag and CODENOMAD_SERVER_PASSWORD env var are runtime credentials — they must be provided on every server start and are not persisted to disk.
Advanced: auth.json internals
The auth.json file (~/.config/codenomad/auth.json) is automatically created and managed by CodeNomad when you set a password through the UI. You generally don't need to edit this file manually. For reference, it uses the following scrypt-based schema:
{
"version": 1,
"username": "codenomad",
"password": {
"algorithm": "scrypt",
"saltBase64": "<base64-salt>",
"hashBase64": "<base64-hash>",
"keyLength": 64,
"params": {
"N": 16384,
"r": 8,
"p": 1,
"maxmem": 33554432
}
},
"userProvided": true,
"updatedAt": "2026-05-18T12:00:00.000Z"
}
Manual creation of this file is not recommended unless you have a helper to generate a valid scrypt PasswordHashRecord.
Progressive Web App (PWA)
When running as a server CodeNomad can also be installed as a PWA from any supported browser, giving you a native app experience just like the Electron installation but executing on the remote server instead.
- Open the CodeNomad UI in a Chromium-based browser (Chrome, Edge, Brave, etc.).
- Click the install icon in the address bar, or use the browser menu → "Install CodeNomad".
- The app will open in a standalone window and appear in your OS app list.
TLS requirement Browsers require a secure (
https://) connection for PWA installation. If you host CodeNomad on a remote machine, use HTTPS. Self-signed certificates generally won't work unless they are explicitly trusted by the device/browser (e.g., via a custom CA).
Data Storage
- Stable server configuration:
~/.config/codenomad/config.yaml - Mutable server state:
~/.config/codenomad/state.yaml - Legacy migration input:
~/.config/codenomad/config.jsonis migrated to the YAML files above. - CodeNomad instance data:
~/.config/codenomad/instances/ - Remote Control route and paired devices:
~/.config/codenomad/remote-control-devices.json(credential hashes only) - OpenCode V2 sessions, messages, and service registration: OpenCode's platform-default global locations.
- Desktop restore state:
~/.codenomad/client-state/v2/
CodeNomad owns no private OpenCode port, database, service registration, or daemon PID. Allowed profile environment variables and the current NODE_EXTRA_CA_CERTS are passed when starting a missing daemon; connecting to an existing daemon does not change its process environment. Separately, CodeNomad applies the profile's complete execution-host environment to the native session before each prompt, custom command, or session shell request, including removal of previously configured values. Legacy OPENCODE_DB and XDG_STATE_HOME ownership variables are ignored. See Session Environment. WSL lifecycle commands run inside Linux and never inspect or signal Linux PIDs from Windows.
Closing a project tab deletes its logical CodeNomad workspace record without evicting the native Location or stopping sessions. Separate occurrences of the same folder are independent. The workspace.stopped broadcast closes any other window's tab displaying that record, with a notification only in that other window. Whole-window close, quit and renderer reload do not delete records; backend shutdown clears only CodeNomad's in-memory connection state and never stops the global service. Reopening the project finds its native sessions again.
Event Delivery
CodeNomad holds one shared OpenCode V2 client.event.subscribe() stream. It routes native location-scoped events to logical workspaces and multiplexes them with CodeNomad events over GET /api/events for browser EventSource clients.
The stream is volatile and has no replay guarantee. After reconnecting, clients must refetch authoritative sessions and pending permission and Form requests; file and config consumers must also refetch after filesystem.changed and config.updated invalidations.
Provider Plan Usage
The Status panel automatically displays quota information for the provider used by the active session. CodeNomad reads the credential each integration currently selects through OpenCode's native credential API, never OpenCode's legacy auth.json. Claude usage follows the login the session uses: anthropic sessions use OpenCode's Anthropic OAuth login, while the claude-code provider registered by opencode-claude uses the Claude Code login on the OpenCode daemon's host (including WSL distros), resolved with that host's environment and CodeNomad profile variables in Claude Code's own order: CLAUDE_CODE_OAUTH_TOKEN, then the stored login (macOS Keychain for a local daemon, otherwise .credentials.json under CLAUDE_SECURESTORAGE_CONFIG_DIR, CLAUDE_CONFIG_DIR or ~/.claude). CodeNomad never modifies these credentials or returns provider secrets through its API. Expired externally owned OAuth sessions must be refreshed by their owner (OpenCode, Claude Code or Codex CLI); CodeNomad does not exchange their refresh tokens, so refresh-token rotation cannot be lost.
Some optional usage integrations require credentials that OpenCode does not expose. They can be enabled without UI configuration through these environment variables:
- Cursor access token:
CURSOR_ACCESS_TOKENorCURSOR_TOKEN - Ollama Cloud:
OLLAMA_CLOUD_COOKIE - OpenCode Go:
OPENCODE_GO_WORKSPACE_IDandOPENCODE_GO_AUTH_COOKIE - ZenMux platform API key (distinct from its inference key):
ZENMUX_PLATFORM_API_KEY - exe.dev usage token:
EXE_DEV_USAGE_TOKEN