Skip to content

ADR-0001: Extension execution model

Status: Accepted — 2026-09-20. The project owner approved the common execution boundary and the process baseline, including the recorded compatibility limits and process cost limits below. This is an architecture decision, not a claim of a completed implementation; additional executors require separate decisions and validation.

The current terminal-ownership and help rules are refined by ADR-0002. The measurements and prototype results below remain historical evidence for this process baseline.

Clarification — 2026-09-22: ADR-0002–0004 govern cancellation reasons, outcome precedence and one bounded finalization period for both process mappings. A process-only command needs no IPC completion report; that difference does not remove supervision, mandatory finalization or the CLI exit mapping. This clarification does not expand the recorded test or runtime-support scope.

Refinement — 2026-09-24: ADR-0013 exposes the same command/execution boundary through CLI and MCP. ADR-0015 distinguishes the ordinary host-trusted process baseline from explicitly required, verified confinement. ADR-0014 and ADR-0016 add task authority and controlled effects at their own boundaries. None turns existing subprocesses into a sandbox or extends the historical measurements below.

Rukh provides the core of a named CLI. Teams publish extensions that add commands and subcommands without rebuilding that CLI. Extension authors need to choose their implementation language and release independently of the core.

The CLI must remain responsible for command routing, access checks and execution lifecycle. Native programs, scripts and future WebAssembly modules need a common integration boundary while retaining their own execution requirements.

We need an execution model that starts with separate processes and can later accommodate WASM/WASI or other runtimes without redesigning command routing and authorization.

  • Support native executables and scripts without requiring the core’s implementation language or a shared-library ABI.
  • Discover commands and display help without executing extension code or preparing its runtime.
  • Preserve terminal interaction, pipelines, exit codes and cancellation across supported operating systems.
  • Keep lifecycle and access checks under core control, with explicit limits on what those checks protect.
  • Allow extensions and the core to evolve through a versioned contract.
  • Add execution mechanisms without treating an operating-system process or IPC as a requirement for every extension.
OptionBenefitsCosts and limits
Separate processesLanguage-independent process boundary; independent releases; ordinary extension crashes do not share the core’s address spaceStartup and communication overhead; platform-specific process and terminal handling
Native libraries loaded into the coreDirect calls and low communication overheadShared ABI and memory; extension faults can affect the core; language and lifecycle coupling
Interpreters embedded in the coreDirect integration for supported languagesEach interpreter becomes a core dependency; same-process failures; additional languages require host work
WebAssembly with WASIA constrained execution model with explicit host importsToolchain, library and operating-system integration requirements need evaluation; arbitrary existing CLIs are not directly interchangeable

Separate processes best match the initial requirement for teams to supply executable code in their own language. They are the baseline implementation behind a common execution contract. HashiCorp go-plugin provides a precedent for subprocess plugins with versioned communication; it is not a selected dependency. Wasmtime’s security model informs future evaluation of WebAssembly execution.

Introduce a common ExtensionExecutor contract, with ProcessExecutor as the baseline implementation. These names describe architectural responsibilities, not an implemented API. Future executors may support WASM/WASI, embedded interpreters or other execution environments.

The core owns command routing, release selection, authorization and the invocation context. It selects an executor approved by the CLI owner against the extension’s declared artifact, platform, I/O and isolation requirements. Unsupported requirements cause an error before code starts; there is no silent fallback that weakens those requirements. An extension cannot install its own trusted executor or expand its permissions.

The common contract covers:

  • Checking execution support from static metadata, without running extension code.
  • Starting a selected command with its inputs, prepared environment and scoped access to core services.
  • Providing the supported input/output modes and reporting success, command failure, execution failure or cancellation.
  • Handling cancellation and deadlines, then releasing resources. Cancellation does not undo external effects; interrupted commands are not retried automatically.

Process IDs, operating-system exit details, IPC endpoints and WASM instance handles belong to the implementations. The core maps their outcomes to the public CLI result. An executor reports the features it supports, such as terminal interaction; this describes technical support, not granted permissions. Different executors do not automatically provide identical isolation, terminal behavior or fault containment.

RuntimeProvider remains a separate responsibility: it resolves or prepares a required environment, such as Node.js or Python. ExtensionExecutor executes the command using that environment when needed. A runtime manager adapter and an execution mechanism are not interchangeable.

Core operations retain the same authorization rules across executors. A process may reach them through IPC; a future WASM implementation may expose controlled host functions. The concrete binding, protocol and runtime engine require their own decisions.

ProcessExecutor starts a new child process for each invocation: a native executable or a script in its prepared runtime. This per-invocation process lifecycle is specific to the baseline implementation.

FormInvocation contract
Process onlyA fixed executable and argument vector; ordinary stdin, stdout and stderr; process exit status. No core-service protocol is required.
Process with core servicesThe same process lifecycle plus a versioned local IPC session for command dispatch and calls to explicitly provided core services.

Under core control, the process executor supplies the working directory and permitted environment, launches the executable without shell interpretation, preserves command exit codes and supervises streams and descendants. The extension owns its command implementation.

Verified static metadata must be sufficient to build the command tree. Before starting code, the core checks release/executor compatibility, command authorization and required core operations. A process protocol handshake may still fail after startup; the child is then stopped without command dispatch. Its startup code may already have executed.

The baseline IPC channel is separate from captured command input/output and pipelines. ADR-0002 keeps terminal presentation and interactive input under core ownership; the extension does not receive the user’s raw terminal. ADR-0003 defines the wire format, version confirmation and session authentication for the managed process binding. IPC is not part of every executor’s required interface.

Access decisions may come from an explicit local policy or a Security Broker integration. Broker failure must not silently switch to local authorization. The core enforces its own dispatch and service boundaries. If an existing tool accepts unrestricted arguments and its internal subcommands cannot be reliably constrained, authorization covers the executable as a whole.

A child process is not a sandbox. It retains the operating-system authority granted at launch and may access files or networks directly. Command filtering and mediated credentials do not make arbitrary extension code safe or prevent it from bypassing its own dispatcher. ADR-0015 now defines the separate confinement contract; each concrete mapping still requires implementation and validation. An explicitly compatible local CLI or MCP profile may use the ordinary trusted baseline without claiming confinement. A profile requiring restrictions rejects a mapping that cannot establish them before startup.

A new executor must satisfy the common contract and document its artifact format, supported features, core-service binding and isolation guarantees. Reusing the same command contract does not make an existing native or scripted package automatically executable as WASM; a suitable artifact and compatible behavior are still required.

WASM may eventually run in an embedded engine or in a separate worker. That placement, host imports, resource limits and instance lifecycle need a separate ADR and validation. Naming WASM does not by itself establish a security boundary. Unsupported execution or isolation requirements must remain errors.

This decision covers installable command extensions. Built-in commands and owner-trusted core integrations are outside its scope. It defines the extension point and the process baseline, without selecting a core language, KCL schema, runtime manager, package source, RPC protocol or additional executor. Concrete interface signatures remain for implementation design.

  • Command routing and authorization can remain stable as execution mechanisms are added. Each executor still needs its own compatible artifacts, lifecycle handling and tests.
  • Keeping execution separate from runtime preparation allows the CLI owner to choose runtime managers independently of execution mechanisms.
  • The abstraction adds a compatibility check and translation between executor outcomes and CLI results; it must not hide differences in isolation or terminal support.
  • The process baseline incurs startup and IPC costs and requires process-tree supervision. Its ordinary crash containment and measured performance do not extend automatically to embedded runtimes.
  • Interactive extensions need documented terminal behavior. The Node.js prototype limitation below remains part of the baseline’s compatibility scope.

Completed: design review against the architecture draft, comparison of alternatives, startup/IPC measurements and the functional feasibility checks recorded below. The checks used temporary prototypes outside the repository.

Process baseline: startup and IPC measurements — 2026-09-20

Section titled “Process baseline: startup and IPC measurements — 2026-09-20”

A small Go host launched either a native Go worker or a Node.js script. The host was already running. The native worker used the same executable as the host; the script imported node:net without application dependencies. Both reported readiness and echoed received data. These were experimental implementations, not Rukh product code or a choice of core language.

  • Environment: Windows 11 Pro Insider Preview 10.0.26220, x64; Intel Core i7-14700KF, 16 visible logical CPUs, approximately 79.8 GiB RAM. Go 1.27.1, Node.js 24.17.0, installed through mise. Other applications remained running; background load and temperature were not controlled.
  • Channel: Windows named pipes, go-winio v0.6.2 and Node’s net module; four-byte length followed by UTF-8 JSON, one request in flight. This was a minimal echo protocol, not a negotiated Rukh API. Windows QueryPerformanceCounter measured elapsed time; consecutive clock reads had a p95 difference of 100 ns. Initial smoke results from a lower-resolution clock were excluded.
  • Sample: three rounds, with worker order reversed in round 2. Each round used 10 startup warmups followed by 150 measured invocations per worker, and 100 IPC warmups followed by 2,000 measured exchanges per worker and payload size. Totals: 900 fresh invocations and 24,000 established-connection round trips, with no reported errors or timeouts. Warmups were excluded; slow successful samples were included in the statistics. Caches were warmed, not evicted.

Values are milliseconds, median / p95, calculated from pooled samples across the three rounds. The p95 uses the nearest-rank method and is not a maximum. Each worker has 450 startup/lifecycle samples and 6,000 IPC samples per payload size.

MeasurementNative GoNode.js
Host preparation before process start0.129 / 0.2540.132 / 0.279
Process start → ready message21.36 / 30.1162.50 / 83.09
Preparation + start + one 64-byte echo + exit + cleanup39.87 / 51.2985.32 / 114.10
Established IPC, 64-byte data, request → response0.0403 / 0.08310.0405 / 0.0925
Established IPC, 4,096-byte data, request → response0.0574 / 0.13060.0663 / 0.1771

Startup includes process/runtime initialization, IPC connection and parsing the ready message; it excludes host preparation. IPC includes serialization, transfer and parsing on both sides, but excludes deadline bookkeeping and final response comparison. Every response was checked. Payload size refers to the data field: full request/response frames were 102/104 bytes or 4,134/4,136 bytes. Build, tool lookup and mise execution were outside the timers.

Results varied between rounds: startup medians ranged from 18.78 to 23.37 ms native and 54.95 to 71.44 ms Node; full-invocation medians ranged from 27.16 to 44.49 ms and 71.79 to 103.79 ms. The cause was not established. Maximum observed full invocations were 57.30/125.06 ms for native/Node; maximum IPC round trips across both sizes were 1.439/1.185 ms. These observations are not guaranteed upper bounds.

Interpretation and limits: in this prototype, starting and terminating a process cost much more than a short IPC exchange. This supports reusing the command’s process for its sequence of core-service calls; it does not establish a need for persistent workers. Real host startup, descriptor/integrity and access checks, broker calls, runtime/dependency installation, real extension work, cold caches, concurrent invocations, terminal behavior and sandboxing were not measured. Acceptable product limits remain open.

The runner independently checked sample counts and median/p95 against raw values. Windows build, go vet and Node syntax validation passed. This ADR retains the historical performance summary; measurement code, binaries and raw timing samples have been removed. The statistics cannot be independently recalculated from this repository.

Process baseline: functional checks — 2026-09-20

Section titled “Process baseline: functional checks — 2026-09-20”

Go 1.27.1 and Node.js 24.17.0 prototypes passed 56/56 common checks on each system: Windows 10.0.26220 and Ubuntu Base 24.04.5 in WSL2, both x64.

AreaConfirmed
Discovery and accessStatic help without execution; incompatibility and denied required access stopped preparation and launch.
Invocation and IPCArguments, binary streams, EOF and exit codes survived direct and IPC-assisted calls; control messages used a separate channel.
Failures and cleanupCrash, lost IPC and timeout ended the invocation without a retry; process-tree cleanup and Ctrl-C were also exercised.
PolicyLocal rules and a test HTTP broker used the same checks; broker failure did not widen access.

Separate terminal/tree checks passed 15/18 on Windows and 4/4 on Ubuntu. The three Windows failures describe one limitation: Node’s ordinary line-input mode did not update the window size; readline did. Terminal mode handling therefore needs an explicit compatibility contract.

These results support the process baseline only. Runtime preparation and corporate identity were simulated; authenticated IPC and production security remain unverified. The common executor abstraction, WASM and other future implementations have not been exercised by these checks.

The accepted decision separates the long-term execution contract from the tested process baseline. The recorded terminal limitation is part of the baseline’s initial compatibility scope. Each additional executor requires its own decision and validation.

For a comparable workstation and the same minimal warmed scenario, the accepted p95 regression limits are: 100 ms from process start to readiness, 150 ms for one complete fresh invocation, and 1 ms for a 64-byte or 4 KiB IPC round trip. The recorded Windows results meet them for both workers. These limits apply to the minimal scenario; they are not guarantees for real commands, runtime installation or other machines.

Concrete interaction and IPC contracts, runtime preparation, company authorization and future executors remain separate decisions. Acceptance does not validate untested implementations or extend the recorded evidence beyond the process baseline.

Diagram

Drag to move · + / − to zoom · 0 to fit · Esc to closeDrag to move and pinch to zoom

100%