# Windows launcher and native ownership boundary

## Implemented bounded launcher

ServiceHost remains an npm-hosted TypeScript/Node runtime, not a Windows Service or .NET wrapper. No native dependency/binary has been added. Native executable launches use Node/libuv quoting. Windows command search is explicit, limited to exe/com/cmd/bat, with bare names searched only in absolute PATH directories (not the current directory). Case-insensitive PATH selection follows Node's lexicographically first environment-key behavior. Native and batch children still inherit direct stdout/stderr file descriptors, not a proxy logging pipe.

Batch execution uses the system cmd, disables AutoRun and initial delayed expansion, and emits one outer-quoted command with an inner-quoted script path. Arguments are unquoted tokens from a tiny allowlist. It deliberately rejects all general cmd escaping cases, expansion syntax, parameter separators (including equals/comma), empty values, Unicode arguments, and whitespace arguments. The script path can contain spaces but cannot contain cmd metacharacters. Scripts are trusted code: they may enable expansion, invoke another shell, or reinterpret arguments/environment themselves. This adapter is not a sandbox or a universal npm argument encoder.

All argument checks occur after credential expansion. Rejection does not include the command or argument value. Resolution/validation failure creates no running child, removes the new lease, and never truncates old logs. Successful batch launch persists `scope: batch-wrapper-only`; stop retains the lease and reports external reconciliation required. Existing direct native children retain direct-child-only semantics.

Native execution tests must still run on Windows before broader claims: spaces in executable/script paths, empty/quoted/trailing-backslash native arguments, cmd and bat scripts, npm-style `%*` forwarding, PATH precedence, output file handle inheritance, nonzero exits, parent-console closure and nested job hosts. Supplied tests cover a subset; planner tests on Linux cannot prove cmd semantics or client terminal behavior.

## Why not taskkill or process groups?

- `taskkill /T` or enumerating parent PIDs is not retained, race-free descendant ownership. A process can exit, reparent or race enumeration; saved PIDs are never a permission to terminate.
- Node's Windows `child.kill` terminates the direct process, not a graceful tree shutdown. Stopping cmd can leave its actual application alive.
- Linux now launches a dedicated JS anchor as session/group leader. Its live IPC capability requests `kill(0)` inside the anchor: group membership itself pins the identity, avoiding runtime-side numeric PGID reuse races. It remains alive after target exit, ignores TERM, and releases only after bounded group-drain observations. Anchor death revokes authority; no saved PGID fallback. Escaping descendants and non-atomic procfs scans still prevent whole-tree claims.
- A pidfd establishes one process identity, not an entire tree. A delegated cgroup v2 scope, with verified containment and permissions, is a stronger Linux tree boundary but is not portable/unconditionally available. No cgroup mutation is implemented here.
- Listening ports are observations only. An empty port does not prove descendants ended; an occupied port only blocks release/relaunch. No process is killed by port.

Linux fixtures now exercise portless children/grandchildren, natural parent exit, group stop/restart, TERM resistance, anchor loss, runtime death/disconnect, escaped descendants, and historical/direct-file logs. Shutdown requires Linux ownership release. Escaped descendants remain outside this boundary.

## Concrete proposed Windows helper (implementation blocked locally)

A narrow signed/reproducibly built native helper could own one anonymous Job Object per launch while Node continues to own config, credentials, HTTP API, panel and logs:

1. Create Job Object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, no breakaway permission; retain its sole controlling handle in the helper, not the application. Associate an I/O completion port for membership/exit notifications.
2. Receive a versioned, bounded launch request via inherited private handles, not a TCP control port or globally guessed pipe name. Inherit only explicit request/control and log handles using `STARTUPINFOEX` handle allowlisting. Never serialize secret arguments into diagnostic logs.
3. Create the target process **suspended**, with exact executable and separately encoded native argv/environment/cwd. Assign to job before resuming. If assignment fails (host/nested-job restrictions included), terminate the still-suspended owned process, close handles and report failure. Never silently launch outside the job.
4. Acknowledge successful assignment/resume with an opaque launch capability and diagnostic PID. Runtime leases transition to launched only on acknowledgement. Loss/timeout of acknowledgement keeps uncertainty fenced; no blind replay.
5. Define explicit stop policy: cooperative termination is application-specific; forceful `TerminateJobObject` requires a reviewed product policy. Report completion only when job active-process count is zero, not merely when cmd exits. Runtime/helper control-channel loss must close the job or terminate it under the chosen crash policy; inherited job handles must not keep it alive accidentally.
6. After crash, do not reattach or terminate via saved PID. With kill-on-close policy, recovery still needs independent confirmation of job drain and lease reconciliation. Test helper death, runtime death, nested host jobs, descendants racing stop, attempted breakaway, launch failure at each step, and direct-file logs before claiming containment.

### Source/build/distribution strategy for the next native iteration

Keep a small C++ Win32 helper under `native/windows/`, with protocol specification and CMake/MSVC build scripts committed alongside it; no downloaded-on-install binary or PowerShell-generated executable. Build per-architecture artifacts in native Windows CI using pinned toolchain/SDK inputs. Test suspended assignment, explicit inherited-handle allowlist, job drain, control EOF, helper/runtime death and nested jobs before producing versioned hashes/provenance. The helper and JS protocol versions must match exactly. Prefer optional OS/CPU-specific npm artifact packages (win32-x64 first, arm64 only after native validation); keep JS runtime/config canonical. Missing, mismatched or unvalidated artifacts must reject **job-owned** launch without fallback. Do not install a compiler as an npm install side effect. Signing/release trust and supported Windows versions remain decisions, not completed infrastructure.

Control contract: private inherited request/response handles; bounded length-prefixed versioned messages `launch`, `stop`, `release`; replies `assigned`, `drained`, `failed`. Job handle must be non-inheritable and held only by helper. Target receives only stdin and direct log handles (not control/job handles), with Unicode environment/cwd and validated argv encoding. No PID-based recovery API. Decide whether explicit stop is forceful `TerminateJobObject` or application-specific graceful request followed by timeout; do not represent Node SIGTERM as Windows graceful tree shutdown. Acknowledgement loss retains lease even if kill-on-close likely drained the job. There is no shipped implementation of this protocol yet.

Blockers: choose supported Windows versions/architectures, review native code/build/signing and npm distribution/update trust, settle graceful-vs-forceful stop and crash policy, validate handle inheritance and nested jobs on native Windows. The lifecycle investigation is authorized, but this checkout has no Windows host, Windows SDK/MSVC (`cl.exe`), clang-cl, MinGW cross compiler, Wine or CMake available (PATH probes on Linux). No Windows executable can be built or natively tested here. Therefore this iteration supplies the design, not untested native code or an invented executable; the existing restricted/direct-child Windows adapter remains unchanged. A Job Object would address ownership, not arbitrary cmd quoting; complex batch inputs should continue to fail closed or use native/JS entrypoints.
