Preparing your workspace...

assistant-chat.tsx2.5k
utils.ts850
button.tsx720
Quick Actions
codmir
Docs
Discord
Get started
API
  • Introduction
  • Installation
  • Your First Project
  • Your First Agent

How the daemon process is started, monitored, and stopped by the desktop app and IDE extension

Lifecycle

The daemon is a long-running Node.js process managed by the clients that use it. Both the desktop app and IDE extension can start the daemon, and it persists independently of either.

Process management

PID and port files

The daemon writes two sentinel files on startup:

  • ~/.codmir/kernel.pid — the process ID
  • ~/.codmir/kernel.port — the port number (default 7700)

These files are the coordination mechanism. Any client can check if the daemon is running by:

  1. Reading the PID from ~/.codmir/kernel.pid
  2. Checking if that process is alive (kill(pid, 0) on Unix)
  3. If alive, reading the port from ~/.codmir/kernel.port and connecting

Startup sequence

When the daemon starts (startDaemon() in packages/kernel/src/daemon.ts):

  1. Check for existing instance — if kernel.pid exists and the process is alive, exit immediately
  2. Redirect stdout/stderr to ~/.codmir/logs/kernel.log
  3. Create MeshManager (wraps ProcessManager + mesh networking)
  4. Create KernelSocketServer and bind to port
  5. If ANTHROPIC_API_KEY is set, create OverseerLoop + WakeBridge
  6. Start the mesh manager and socket server
  7. Write PID and port files
  8. Start PlatformPresence (cloud heartbeat)
  9. Register SIGINT/SIGTERM handlers for graceful shutdown

Graceful shutdown

On SIGINT or SIGTERM:

  1. Stop PlatformPresence (deregister from cloud)
  2. Stop WakeBridge and OverseerLoop
  3. Stop MeshManager (cancel queued tasks, close peer connections)
  4. Close KernelSocketServer (drop all WebSocket connections)
  5. Remove kernel.pid and kernel.port files

Desktop app (Tauri)

The desktop app manages the daemon through Tauri's Rust backend.

ensure_daemon

The primary Tauri command. Called on app startup via invoke('ensure_daemon'):

// apps/desktop/src-tauri/src/daemon.rs
pub async fn do_start(app: &AppHandle) -> Result<DaemonState, String> {
    // 1. Check if already running via PID file
    // 2. Find the daemon binary (daemon-entry.js)
    // 3. Spawn as a detached process with Stdio::null()
    // 4. Poll PID file for up to 8 seconds (250ms intervals)
    // 5. Return DaemonState { status: Online, pid, port }
}

Binary lookup order:

  1. ~/.codmir/node_modules/@codmir/kernel/dist/daemon-entry.js
  2. /usr/local/lib/node_modules/@codmir/kernel/dist/daemon-entry.js
  3. which codmir-daemon (PATH lookup)

Background monitor

A Tokio task polls daemon status every 5 seconds:

pub fn start_daemon_monitor(app: AppHandle) {
    tokio::spawn(async move {
        loop {
            tokio::time::sleep(Duration::from_secs(5)).await;
            let new_status = check_daemon_status().await;
            if new_status != last_status {
                app.emit("daemon-status-changed", &state);
                update_tray(&app, &state);
            }
        }
    });
}

The monitor:

  • Updates the system tray icon (green dot = online, red = offline)
  • Emits a Tauri event so the React frontend can react to status changes
  • Auto-restarts the daemon if it crashes (configurable)

React integration

// apps/desktop/src/hooks/use-kernel.ts
export function useKernel() {
  useEffect(() => {
    useKernelStore.getState().ensureAndConnect();
    return () => useKernelStore.getState().disconnect();
  }, []);
}

// apps/desktop/src/stores/kernel-store.ts
ensureAndConnect: async () => {
  const info = await invoke<DaemonInfo>('ensure_daemon');
  get().connect(info.port);
}

Tauri commands

CommandDescription
ensure_daemonStart if not running, return connection info
daemon_statusReturn current DaemonState
daemon_startForce start (even if already running)
daemon_stopStop the daemon gracefully
daemon_restartStop then start
daemon_logsReturn last 200 lines of ~/.codmir/logs/kernel.log
daemon_installInstall the kernel package globally

IDE extension (VS Code)

The VS Code extension manages the daemon through its KernelClientService.

Connection flow

// apps/vscode-extension/src/extension/services/KernelClientService.ts
async connect() {
  const port = await this._discoverPort();
  if (!port) {
    await this._ensureDaemon();
    port = await this._discoverPort();
  }
  this._connectWs(port);
}

Port discovery

_discoverPort(): number | null {
  // 1. Read ~/.codmir/kernel.port
  // 2. Read ~/.codmir/kernel.pid
  // 3. Verify process is alive: process.kill(pid, 0)
  // 4. Return port if alive, null otherwise
}

Daemon startup from extension

If the daemon isn't running, the extension forks it:

_ensureDaemon() {
  const entry = this._findDaemonEntry();
  const child = fork(entry, ['start', '--foreground'], {
    detached: true,
    stdio: 'ignore',
    env: { ...process.env, KERNEL_PORT: '7700' },
  });
  child.unref();
  // Poll PID file for up to 5 seconds
}

Binary lookup:

  1. ~/.codmir/node_modules/@codmir/kernel/dist/daemon-entry.js
  2. require.resolve('@codmir/kernel/daemon-entry')
  3. Relative paths from the extension's installation directory

Webview bridge

The extension acts as a bridge between the webview UI and the daemon:

Webview (React)  ←→  Extension Host  ←→  Daemon (WebSocket)
     postMessage        MessageBridge       KernelClientService
  • Webview sends START_KERNEL_SESSION → extension calls kernelClient.startSession()
  • Daemon sends events → extension forwards via bridge.send({ type: 'KERNEL_SESSION_EVENT', payload }) → webview receives via onDidReceiveMessage

Platform compatibility

macOS / Linux

  • Process spawning: fork() / detached: true / child.unref()
  • Signal handling: SIGTERM for graceful stop, SIGKILL as fallback
  • PID files: ~/.codmir/kernel.pid
  • Works identically on both platforms

Windows (planned)

  • Process spawning: Same detached: true + unref() pattern works
  • Signal handling: No SIGTERM — use taskkill /PID {pid} instead
  • PID files: %USERPROFILE%\.codmir\kernel.pid
  • WebSocket: Works identically

Environment variables

VariableDefaultDescription
KERNEL_PORT7700Port for the WebSocket server
ANTHROPIC_API_KEY—Required for agent sessions and overseer
CODMIR_PROXY_URL—Route API calls through Codmir's proxy
NODE_ENV—Set to production for log reduction

Troubleshooting

Daemon won't start

# Check if already running
cat ~/.codmir/kernel.pid && kill -0 $(cat ~/.codmir/kernel.pid)

# Check logs
tail -50 ~/.codmir/logs/kernel.log

# Remove stale PID file if process is dead
rm ~/.codmir/kernel.pid ~/.codmir/kernel.port

Port conflict

If port 7700 is in use:

KERNEL_PORT=7701 codmir daemon start

Connection issues

Both clients look for the daemon at ws://127.0.0.1:{port}. If a firewall blocks localhost WebSocket connections, the daemon won't be reachable.

The daemon binds to 0.0.0.0 (all interfaces) to support mesh networking, but clients always connect via 127.0.0.1.

Open in ChatGPTOpen in ClaudeConnect MCP on CursorConnect MCP on WindsurfEdit on GitHub
codmir
Products
Desktop AppMobile AppIDE ExtensionAI AgentsOverseerVoice ModeCortex ProtocolHive MindGame Dev (Godat)PricingDownload
Solutions
AI Code AgentsCode ReviewError TrackingSelf-HealingDeploy TrackingAI Tracking SDKWorkflow AutomationTesting FrameworkTime TrackingVoice Control
Platform
ProjectsTickets & BoardsAgent MarketplaceIntegrationsTemplatesApp HubAdmin PanelAnalytics
Integrations
GitHubSlackDiscordVercelSentryRailwayLinearAll Integrations
Developers
DocumentationAPI ReferenceSDKCLIDeveloper PortalMarketplaceMCP ConnectorsWebhooksExamplesnpm Packages
Resources
BlogChangelogCommunityTutorialsGet StartedSupportStatusFeedback
Company
AboutCareersContact SalesKickstarterReferral Program
Legal
Terms of UsePrivacy PolicyEULATrademarkSecurity
Download on the App StoreGet it on Google Play

Codmir™ 2026. All rights reserved.

Free & open source SDK — Apache 2.0 License