Skip to main content
Version: 0.2.0

C4 Component: Runtime (C3)

This diagram zooms into the Runtime container from the C2 Container Diagram to show its internal components — the interfaces, concrete implementations, and supporting types that make up Invowk's command execution layer.

note

The runtime package (internal/runtime/) is responsible for executing user-defined commands. It provides three interchangeable execution backends behind a common interface hierarchy, a registry for runtime dispatch, and a structured execution context that decouples I/O, environment, and TUI concerns.

Diagram

Diagram: architecture/c4-component-runtime

Interfaces

InterfaceMethodsRole
RuntimeName(), Execute(), Available(), Validate()Core execution contract. All runtimes implement this. Execute returns a *Result with both exit code and error — non-zero exit code without error is a normal process exit; error indicates infrastructure failure.
CapturingRuntimeExecuteCapture()Optional output capture capability. Returns a *Result with Output and ErrOutput fields populated. Does not embed Runtime.
InteractiveRuntimeSupportsInteractive(), PrepareInteractive()PTY attachment capability. Embeds Runtime. Returns a PreparedCommand with an exec.Cmd ready for PTY attachment and a cleanup function.
EnvBuilderBuild()Environment variable construction following a 10-level precedence hierarchy (host env at level 1 through --ivk-env-var CLI flags at level 10).

Implementations

ComponentTechnologyResponsibility
NativeRuntimeGoExecutes commands via the host shell (bash/sh on Unix, PowerShell on Windows). Fastest option. Configurable shell override. Implements Runtime, CapturingRuntime, and InteractiveRuntime.
VirtualRuntimeGo/mvdan-shEmbedded POSIX shell interpreter with optional u-root built-in utilities. No host shell dependency. Spawns a subprocess of itself for PTY-based interactive mode. Implements Runtime, CapturingRuntime, and InteractiveRuntime.
ContainerRuntimeGoExecutes commands inside Docker/Podman containers. Depends on container.Engine, provision.LayerProvisioner, sshserver.Server, and config.Config. Linux containers only. Implements Runtime, CapturingRuntime, and InteractiveRuntime.
DefaultEnvBuilderGoStandard 10-level precedence implementation: host env (filtered) → root/command/impl env files → root/command/impl env vars → ExtraEnv → runtime env files → runtime env vars.
MockEnvBuilderGoTest helper that returns a fixed environment map. Enables testing runtimes in isolation without real file system access or env loading.

Supporting Types

TypeRole
RegistryMap-based runtime dispatcher. Stores RuntimeType → Runtime mappings. Provides Get(), GetForContext(), Available(), and Execute() (which chains validate-then-execute).
RuntimeTypeString type identifying runtime variants: "native", "virtual", "container".
ExecutionContextPrimary data structure for command execution. Composed of IOContext, EnvContext, and TUIContext sub-types plus command metadata, selected runtime/implementation, and execution ID.
IOContextGroups I/O streams: Stdout, Stderr, Stdin. Factory functions DefaultIO() and CaptureIO() provide common configurations.
EnvContextGroups environment configuration: ExtraEnv (INVOWK_FLAG_*, INVOWK_ARG_*), RuntimeEnvVars (--ivk-env-var), RuntimeEnvFiles (--ivk-env-file), and inheritance overrides.
TUIContextGroups TUI server connection details: ServerURL and ServerToken.
ResultExecution result: ExitCode, Error, Output (captured stdout), ErrOutput (captured stderr).
PreparedCommandReturned by PrepareInteractive(): contains an exec.Cmd ready for PTY attachment and an optional Cleanup function.

External Dependencies

DependencyUsed ByPurpose
container.EngineContainerRuntimeUnified Docker/Podman container engine abstraction
provision.LayerProvisionerContainerRuntimeCreates ephemeral image layers with invowk binary and modules
sshserver.ServerContainerRuntimeToken-based SSH server for container-to-host callbacks
config.ConfigContainerRuntimeApplication configuration (container engine preference, etc.)
mvdan.cc/sh/v3VirtualRuntimeEmbedded POSIX shell interpreter
internal/urootVirtualRuntimeBuilt-in utilities for the virtual shell (cp, mv, cat, etc.)
pkg/invowkfileExecutionContextCommand and Invowkfile types, RuntimeMode, EnvInheritMode

Key Relationships

Interface Segregation

The runtime package uses interface segregation to let callers depend only on the capabilities they need:

  • Runtime is the base contract — any caller that just needs to run a command accepts Runtime.
  • CapturingRuntime is a standalone interface for output capture. Callers that need captured output can type-assert to it.
  • InteractiveRuntime embeds Runtime and adds PTY support. The helper function GetInteractiveRuntime() combines type assertion with SupportsInteractive() capability check.

All three concrete runtimes (Native, Virtual, Container) implement all three interfaces.

Registry Dispatch

The Registry decouples runtime selection from execution:

  1. CLI layer resolves the RuntimeType from command defaults or --ivk-runtime flag.
  2. Registry.GetForContext() looks up the matching Runtime.
  3. Registry.Execute() chains: get runtime → check availability → validate → execute.

ExecutionContext Composition

ExecutionContext uses composition of three focused sub-types:

  • IOContext — I/O streams, easily swapped between real and capture modes.
  • EnvContext — Environment variable configuration, including inheritance overrides from CLI flags.
  • TUIContext — TUI server connection details, zero-value means "not configured".

EnvBuilder 10-Level Precedence

LevelSource
1Host environment (filtered by inherit mode)
2-4Root/command/implementation-level env.files
5-7Root/command/implementation-level env.vars
8ExtraEnv (INVOWK_FLAG_*, INVOWK_ARG_*, ARGC, ARGn)
9--ivk-env-file flag
10--ivk-env-var flag (highest priority)

Design Rationale

Why Interface Segregation?

Callers depend only on the capabilities they actually use. A dependency resolver that just checks availability calls Runtime.Available(). The output capture system asserts CapturingRuntime only when capture is needed. The TUI system checks for InteractiveRuntime only for interactive commands.

Why Composition for ExecutionContext?

A flat struct with 15+ fields would be harder to test and harder to read. By grouping into IOContext, EnvContext, and TUIContext, each sub-type can be constructed and tested independently.

Why an EnvBuilder Interface?

MockEnvBuilder lets tests focus on runtime execution logic by providing a fixed environment map, eliminating flaky tests from file system state.

Why a Registry?

The Registry pattern lets the application wire all runtimes once at startup, and the execution pipeline simply asks for a runtime by type. Adding a new runtime means registering it — no changes to the execution pipeline.