SNA

Core Launchers

Node and Electron launch surfaces for starting an SNA server.

Core Launchers

Launchers are for host applications that need to start SNA as part of their own lifecycle. They hide port selection, process handling, and Electron packaging details.

Import pathSurfacePurpose
@sna-sdk/core/nodestartSnaServer(options)Starts SNA from a Node host.
@sna-sdk/core/electronstartSnaServer(options)Starts SNA from an Electron host as a forked child process.
@sna-sdk/core/electronstartSnaServerInProcess(options)Runs SNA in the current Electron main process.
@sna-sdk/core/node, @sna-sdk/core/electronresolveClaudeCli, resolveCodexCli, resolveOpenCodeCliFinds runtime CLI installations for setup and diagnostics.

Launcher handles expose enough information to stop the server and pass the discovered URL to @sna-sdk/client or @sna-sdk/react.

Options

dbPath is required. Other fields are optional.

type SnaServerOptions = {
  port?: number;
  host?: string;
  appId?: string;
  authToken?: string;
  allowedOrigins?: string[];
  dbPath: string;
  cwd?: string;
  maxSessions?: number;
  permissionMode?: "acceptEdits" | "bypassPermissions" | "default";
  model?: string;
  permissionTimeoutMs?: number;
  nativeBinding?: string;
  env?: Record<string, string>;
  readyTimeout?: number;
  onLog?: (line: string) => void;
  dataDir?: string;
  logLevel?: "info" | "warn" | "error" | "silent";
  runtimePaths?: RuntimePaths;
  langfuse?: { publicKey: string; secretKey: string; baseUrl?: string };
};

type RuntimePaths = {
  claudeCode?: string;
  codex?: string;
  opencode?: string;
  grok?: string;
  cursor?: string;
};
FieldDefaultDescription
port3099API port passed to the server.
host127.0.0.1Bind host. Keep loopback unless the host app has a stronger transport boundary.
appIdsna-sdkOwner ID for this server instance. Sessions created through the server are tagged with this value.
authTokengeneratedBearer token required by protected HTTP routes and WebSocket upgrades.
allowedOrigins[]Browser Origin values allowed to call the server. Native clients that send no Origin are allowed.
dbPathrequiredAbsolute SQLite path. The launcher also derives cwd from this path when cwd is omitted.
cwddirname(dbPath)Working directory for the server process and runtime sessions.
maxSessions5Maximum concurrent agent sessions.
permissionModeSDK defaultDefault permission mode for new agent sessions.
modelSDK defaultDefault model for runtime calls.
permissionTimeoutMs00 leaves permission prompts under host control; positive values auto-deny after the timeout.
nativeBindingauto-detectedExplicit better-sqlite3 native binding path. Use this for custom Electron builds.
env{}Extra environment variables. Runtime command variables here override values generated from runtimePaths; launcher-owned values such as SNA_APP_ID, SNA_AUTH_TOKEN, SNA_HOST, and SNA_ALLOWED_ORIGINS remain controlled by launcher options.
readyTimeout15000Milliseconds to wait for the standalone process to print the ready line.
onLognoneReceives stdout/stderr lines from the child process or SDK logger lines in in-process mode.
dataDirderived from dbPathBase directory for image and asset storage.
logLevelinfoFilters in-process logger output. Forked server file logs still record all levels.
runtimePathsauto-detectExplicit runtime CLI commands or absolute paths. They map to SNA_CLAUDE_COMMAND, SNA_CODEX_COMMAND, SNA_OPENCODE_COMMAND, SNA_GROK_COMMAND, and SNA_CURSOR_COMMAND.
langfusenoneEnables tracing for sessions whose metadata opts in.

The launcher returns both authToken and connection: { baseUrl, authToken }. Pass connection to SnaClient or SnaProvider instead of persisting the token or reconstructing it from environment variables. The generated token is owned by the server handle; rotate it by starting the server with a new token.

Use runtimePaths when the host app has a setup screen or stores user-selected CLI locations. env is still available as the final escape hatch and overrides the values generated from runtimePaths. Use the typed launcher fields for server identity, auth, bind host, and origin policy.

Forked vs in-process

ModeUse it whenTradeoff
Forked startSnaServerYou want process isolation and a standalone child server.Requires packaged Electron apps to unpack @sna-sdk/core; readiness depends on the child process starting cleanly.
startSnaServerInProcessElectron fork() is unreliable because of asar paths, environment propagation, or orphaned children.Shares the caller's Node event loop; the host must call stop() during shutdown.

In-process mode does not spawn a default agent. The host starts sessions through HTTP, WebSocket, or the returned sessionManager.

Directly running the standalone server is a development/debugging path. Product integrations should prefer startSnaServer() or startSnaServerInProcess() so the token, owner identity, bind host, and shutdown lifecycle stay under the host app's control.

Function reference

startSnaServer(options) from @sna-sdk/core/node

Starts SNA from a plain Node host and returns a handle with connection information and shutdown behavior. Use this for CLIs, desktop hosts, and local development processes.

const handle = await startSnaServer({
  appId: "my-app",
  dbPath: "/Users/me/Library/Application Support/MyApp/sna.db",
  port: 3099,
  allowedOrigins: ["http://localhost:5173"],
  runtimePaths: {
    claudeCode: "/opt/homebrew/bin/claude",
  },
});

const client = new SnaClient(handle.connection);

handle.stop();

The Node launcher reuses the same standalone server implementation as Electron. It is useful when the host wants SNA as a sibling process instead of importing the HTTP app directly.

startSnaServer(options) from @sna-sdk/core/electron

Starts SNA from an Electron host. It accounts for packaged application constraints and should be preferred over the Node launcher inside Electron main processes.

In forked mode, packaged apps should unpack the SDK files that need to be executed by Node:

// electron-builder example
asarUnpack: ["node_modules/@sna-sdk/core/**"]

The launcher remaps .asar paths to .asar.unpacked, constructs NODE_PATH, resolves better-sqlite3, forwards logs, and waits until the server prints API server ready.

Returns:

type SnaServerHandle = {
  process: ChildProcess;
  port: number;
  host: string;
  appId: string;
  baseUrl: string;
  authToken: string;
  connection: { baseUrl: string; authToken: string };
  stop(): void;
};

startSnaServerInProcess(options)

Runs SNA in the current Electron process. Use it only when tighter lifecycle integration is more important than process isolation.

Returns:

type InProcessSnaServerHandle = {
  process: null;
  port: number;
  host: string;
  appId: string;
  baseUrl: string;
  authToken: string;
  connection: { baseUrl: string; authToken: string };
  sessionManager: SessionManager;
  httpServer: http.Server;
  initLangfuse(config: { publicKey: string; secretKey: string; baseUrl?: string }): Promise<void>;
  setTracerUser(userId?: string, userEmail?: string): void;
  stop(): Promise<void>;
};

stop() kills sessions, disposes runtimes, flushes tracing, clears the logger callback, and closes the HTTP server. Call it from the host shutdown path.

resolveClaudeCli(options?)

Finds a usable Claude Code CLI path. Use it during setup or diagnostics to explain why a runtime cannot start.

resolveCodexCli(options?)

Finds a usable Codex CLI path with the same env/cache/static/shell lookup order.

resolveOpenCodeCli(options?)

Finds a usable OpenCode CLI path with the same env/cache/static/shell lookup order.

validateClaudePath(path)

Checks whether a specific Claude CLI path is usable. Use it before saving user-provided CLI paths.

cacheClaudePath(path)

Stores a resolved Claude CLI path for later launches. Use it after a successful validation step.

parseCommandVOutput(output)

Parses shell command discovery output into a normalized path result. It is mainly useful for CLI resolution internals and diagnostics.

On this page