17 KiB
status, system, created, owners
| status | system | created | owners | |
|---|---|---|---|---|
| draft | cli | 2026-06-15 |
|
Native Kandev CLI Requirements
Overview
Users start Kandev from the terminal through kandev, but the current launcher is a Node.js/TypeScript program even when Kandev is installed from Homebrew or run from a local production build. This adds a runtime dependency and extra moving parts to the most basic startup path. Users should keep the same kandev command while the launcher becomes a native executable.
Requirements
REQ-CLI-NATIVE-KANDEV-CLI-001: Native Kandev CLI
Intent: Users start Kandev from the terminal through kandev, but the current launcher is a Node.js/TypeScript program even when Kandev is installed from Homebrew or run from a local production build. This adds a runtime dependency and extra moving parts to the most basic startup path. Users should keep the same kandev command while the launcher becomes a native executable.
Acceptance criteria
- AC-CLI-NATIVE-KANDEV-CLI-001.1:
kandevremains the public command for Homebrew, npm/npx, global npm installs, service units, and local development. - AC-CLI-NATIVE-KANDEV-CLI-001.2: Homebrew and release bundle installs provide a native
bin/kandevexecutable that can launch Kandev without executing the TypeScript CLI bundle. - AC-CLI-NATIVE-KANDEV-CLI-001.3: The native
kandevexecutable supports the public launcher commands users need after the web runtime merge: default run,run,start,service,--help,--version,--port,--backend-port,--verbose,--debug, and--headless. - AC-CLI-NATIVE-KANDEV-CLI-001.4: Native
devmode is supported for source checkouts through the Go launcher (kandev dev/--dev); installed release entrypoints do not expose it — it requires the repository checkout thatmake devprovides. - AC-CLI-NATIVE-KANDEV-CLI-001.5: The native launcher starts the backend as a supervised child process by re-executing the same
bin/kandevbinary in a hidden backend mode. - AC-CLI-NATIVE-KANDEV-CLI-001.6: The hidden backend mode is not a public command and is not shown in normal help output.
- AC-CLI-NATIVE-KANDEV-CLI-001.7: Backend restarts restart only the backend child process; the launcher/supervisor remains alive unless the shutdown policy requires the whole app to exit.
- AC-CLI-NATIVE-KANDEV-CLI-001.8: npm/npx continues to expose the
kandevcommand through thekandevnpm package. The npm package may use a minimal JavaScript shim, but that shim only resolves the platform runtime package and execs its nativebin/kandev.
Migrated source detail
Why
Users start Kandev from the terminal through kandev, but the current launcher is a Node.js/TypeScript program even when Kandev is installed from Homebrew or run from a local production build. This adds a runtime dependency and extra moving parts to the most basic startup path. Users should keep the same kandev command while the launcher becomes a native executable.
What
kandevremains the public command for Homebrew, npm/npx, global npm installs, service units, and local development.- Homebrew and release bundle installs provide a native
bin/kandevexecutable that can launch Kandev without executing the TypeScript CLI bundle. - The native
kandevexecutable supports the public launcher commands users need after the web runtime merge: default run,run,start,service,--help,--version,--port,--backend-port,--verbose,--debug, and--headless. - Native
devmode is supported for source checkouts through the Go launcher (kandev dev/--dev); installed release entrypoints do not expose it — it requires the repository checkout thatmake devprovides. - The native launcher starts the backend as a supervised child process by re-executing the same
bin/kandevbinary in a hidden backend mode. - The hidden backend mode is not a public command and is not shown in normal help output.
- Backend restarts restart only the backend child process; the launcher/supervisor remains alive unless the shutdown policy requires the whole app to exit.
- npm/npx continues to expose the
kandevcommand through thekandevnpm package. The npm package may use a minimal JavaScript shim, but that shim only resolves the platform runtime package and execs its nativebin/kandev. make startlaunches through the nativeapps/backend/bin/kandev startpath after building local artifacts.- Existing service installs continue to be managed by
kandev service ...; newly generated service units execute the publickandevlauncher path. - Reinstalling or updating a system service preserves the service account recorded by the existing
root-controlled Kandev unit or plist. The account changes only when the operator supplies
--run-as <user>explicitly. - A first system install invoked from a root login does not silently select root. It requires an
explicit
--run-as <user>; selecting root is allowed only as the explicit--run-as rootchoice. A first install throughsudomay continue to select the non-root invoking user. - Before rewriting or restarting a system service, the installer verifies that the configured Kandev home is owned by the selected service account. A mismatch fails with recovery guidance; the installer does not recursively change ownership.
- Service units preserve install-time Node/npm/npx bin directories when they are discoverable, so agents can still invoke npm-managed CLIs under service managers that do not source shell profiles.
- Startup output continues to show the URL users should open, the MCP URL, database path when applicable, and log level when applicable.
- Production
runandstartdo not execute a Node.js web runtime; the Go backend serves the embedded Vite SPA assets.
API surface
Public CLI
kandev [--port <port>] [--verbose] [--debug] [--headless]
kandev run [--port <port>] [--verbose] [--debug] [--headless]
kandev start [--port <port>] [--verbose] [--debug] [--headless]
kandev service install [--system] [--run-as <user>] [--port <port>] [--home-dir <path>] [--no-boot-start]
kandev service uninstall|start|stop|restart|status|logs|config [--system]
kandev --version
kandev --help
Port flags and environment variables:
--portand--backend-portselect the public backend port.KANDEV_PORTandKANDEV_BACKEND_PORTprovide backend port defaults.- There is no production web port flag or environment variable. The Go backend serves the embedded SPA on the backend port.
--runtime-versionis not supported by the native launcher and is rejected as a usage error.
Logging/debug flags:
--verboseshows backend info logs.--debugshows debug logs and enables current debug environment behavior.--headlessskips browser opening and prints the ready URL.
System-service identity flags:
--run-as <user>is valid only forkandev service install --system.- On reinstall, omitting
--run-aspreserves the account in the existing Kandev-managed root-controlled unit or plist, even when the update command is run from a different login. - On a first install, omitting
--run-asselects a non-rootSUDO_USERwhen available. A root login without that context must choose explicitly, including when the desired account is root. - The service-owned
service/install.jsonmetadata records the effective account but is not trusted by a privileged installer as authority for choosing a system account.
Hidden backend mode
The native launcher has a private backend mode:
kandev __backend [backend flags]
This mode starts the backend server and is invoked only by the launcher/supervisor. It is intentionally hidden from public help output. Existing backend diagnostic flags may remain available in this mode for tests and direct diagnostics.
Runtime bundle
Release bundles expose this layout:
kandev/
├── bin/kandev
├── bin/agentctl
├── bin/agentctl-linux-amd64
├── bin/agentctl-darwin-arm64
└── bin/agentctl-darwin-amd64
bin/kandev is both the public launcher and the hidden backend-mode executable.
Supervisor manifest
When backend restart supervision is enabled, the launch manifest records the same executable plus hidden backend argv:
{
"version": 1,
"backend_executable": "/absolute/path/to/bin/kandev",
"argv": ["__backend"],
"cwd": "/absolute/path/to/bin",
"env": {
"KANDEV_SERVER_PORT": "38429",
"KANDEV_AGENT_STANDALONE_PORT": "39429",
"KANDEV_RESTART_ADAPTER": "supervisor"
},
"home_dir": "/absolute/path/to/home",
"port": 38429,
"mode": "run",
"created_at": "timestamp"
}
State machine
Launcher process lifecycle:
idle: command parsed but no children started.backend-starting: launcher has spawnedkandev __backendand is waiting for backend health.ready: backend is healthy and serving API, WebSocket, and SPA routes; browser may be opened unless headless.restarting-backend: supervisor received a restart request and is replacing only the backend child.shutting-down: launcher is terminating child processes after signal, service stop, or child exit policy.failed: startup failed and the launcher exits non-zero after surfacing the actionable error.
Transitions:
idle->backend-starting: user runskandev,kandev run,kandev start, or service unit starts.backend-starting->failed: backend exits or health timeout expires.backend-starting->ready: backend health endpoint reports ready.ready->restarting-backend: backend restart adapter requests restart.restarting-backend->ready: replacement backend becomes healthy.- any active state ->
shutting-down: signal, service stop, or non-restartable child exit.
Failure modes
- If a required backend artifact is missing in
startmode,kandev startexits non-zero with a message that tells the user to runmake build. - If the runtime bundle is missing required files in
runmode,kandev runexits non-zero and names the missing artifact. - If the backend does not become healthy before timeout, the launcher exits non-zero and includes captured backend output when running in quiet mode.
- If the backend child exits unexpectedly after startup, the launcher either restarts it through the supervisor path when the exit is restart-controlled or shuts down the app with the backend exit code.
- If npm/npx optional runtime resolution fails, the JavaScript shim exits non-zero with an actionable message explaining that the platform runtime package is missing.
- If a privileged reinstall finds an existing Kandev-managed system unit or plist, it fails before rewriting or restarting the service when that root-controlled definition is malformed or its account cannot be resolved.
- If a first system install is run from a root login without
--run-as, it exits with usage guidance instead of silently creating a root service. - If the configured system Kandev home owner does not match the selected service account, install exits before changing the service definition and explains how to preserve the existing account or deliberately reconcile ownership. It does not add a global Git trust exception or chown the data tree automatically.
Persistence guarantees
- Service installation metadata remains durable across restarts and upgrades.
- The effective system-service account remains stable across package upgrades and repeated
service install --systemcalls unless an operator explicitly changes it with--run-as. - For an existing system service, the root-controlled unit or plist is authoritative for service identity. Service-owned metadata may corroborate that identity but cannot authorize a privileged identity change.
- Supervisor manifest/control files remain under the configured Kandev home directory and are overwritten on each launch with the current executable path and backend argv.
- The launcher does not persist child process state beyond the existing supervisor manifest. After a launcher restart, it starts a fresh backend child.
- User data, database files, task worktrees, and backend-managed persistence remain owned by existing backend persistence behavior; the launcher migration does not change their retention.
Scenarios
- GIVEN Kandev is installed from Homebrew, WHEN the user runs
kandev --help, THEN the help output describes the public launcher commands and does not show__backend. - GIVEN Kandev is installed from Homebrew, WHEN the user runs
kandev --version, THEN the command prints the installed Kandev version without executing the Node CLI bundle. - GIVEN a valid release bundle, WHEN the user runs
kandev --headless, THEN the native launcher startskandev __backend, waits for the backend to serve API and SPA routes, and prints the backend URL. - GIVEN a local checkout with built backend and web artifacts, WHEN the user runs
make start, THEN the Makefile launches throughapps/backend/bin/kandev startand does not invoke the TypeScript CLI launcher (kandev devlikewise goes through the same Go binary). - GIVEN the backend has requested a restart through the restart adapter, WHEN the launcher receives the restart request, THEN only the
kandev __backendchild process is replaced. - GIVEN the user presses Ctrl-C while Kandev is running, WHEN the launcher handles the signal, THEN it terminates the backend child process before exiting.
- GIVEN a new service install on Linux or macOS, WHEN the user runs
kandev service install, THEN the generated service unit executes the publickandevlauncher path. - GIVEN a system service already runs as
brewuser, WHEN root later runskandev service install --systemduring an upgrade without--run-as, THEN the generated definition still runs asbrewuser. - GIVEN an existing system service, WHEN an operator intentionally supplies
--run-as another-user, THEN Kandev validates that account and the configured home owner before changing the service definition. - GIVEN no existing system service and no non-root
SUDO_USER, WHEN root runskandev service install --systemwithout--run-as, THEN installation fails and asks for an explicit account rather than defaulting to root. - GIVEN a selected service account whose UID differs from the configured system home owner, WHEN installation runs, THEN it fails before rewriting or restarting the service and does not recursively chown the home.
- GIVEN service-owned install metadata names a more privileged account than the existing root-controlled service definition, WHEN a privileged reinstall runs, THEN the metadata cannot change the selected account.
- GIVEN a native user-service install on Linux or macOS, WHEN the service starts, THEN its environment and durable install metadata identify it as a kandev-managed service and the guarded System-page updater can launch the native self-update helper.
- GIVEN Node is managed by nvm/fnm/asdf/volta/mise and
node,npm, ornpxresolve from that manager at install time, WHEN the user runskandev service install, THEN the generated service environment includes the detected Node tool bin directory inPATH. - GIVEN a user runs
npx kandev@latest --help, WHEN npm has installed the platform runtime package, THEN the npm shim execs the runtime package's nativebin/kandevand the user sees the same public help output.
Out of scope
- Making
npxitself Node-free. - Introducing
kandevctlas a user-facing command. - Requiring a separate shipped backend binary such as
kandev-backend. - Changing task, workflow, agent, or integration behavior after the backend has started.
- Automatically changing ownership, managing ACLs, or adding broad Git
safe.directoryentries when a system-service identity mismatch is detected. - Managing Windows services or inferring service identity from an untrusted service-owned metadata file.
Repair plan
See the system-service identity guardrails plan.
Decision
System-service account continuity and ownership validation follow ADR-2026-07-31-system-service-user-continuity.
Open questions
- Should hidden backend mode be selected by the
__backendargv token or byKANDEV_INTERNAL_ROLE=backend? Recommendation:__backend. - Should
--runtime-version <tag>be ported in a later native launcher implementation or remain deprecated? - Should one release ship both the old Node CLI bundle and the native
bin/kandevfor compatibility while Homebrew/npm packaging switches over?