Ignition
Contributing

Architecture

Module overview, data flow, and key design decisions.

Data flow

CLI args
  → parse targets
  → resolve inventory (load .ts file via dynamic import)
  → load recipe (.ts file via dynamic import)
  → for each host (bounded parallelism):
      create SystemSSHConnection (ssh/scp via Bun.spawn)
      → ping (verify connectivity)
      → probe host facts (OS, pkg manager, init system, arch)
      → create ExecutionContext (one per host per run)
      → execute recipe(ctx)
      → accumulate results into RunSummary
  → format output
  → exit code (0 = success, 1 = failures)

Module overview

src/core/

The engine.

FilePurpose
types.tsAll core interfaces and types
resource.tsexecuteResource() — the check-then-apply lifecycle engine
context.tsExecutionContextImpl — per-host execution state
runner.tsrunRecipe() — multi-host orchestration
errors.tsTagged error hierarchy
cache.tsCheck result caching (memory and file)
facts.tsHost platform detection
registry.tsResource registry and schema generation
serialize.tsDeterministic JSON and field redaction

src/ssh/

SSH transport.

FilePurpose
types.tsTransport interface, capabilities, config types
connection.tsSystemSSHConnection — shells out to ssh/scp via Bun.spawn

src/resources/

Built-in resources.

FilePurpose
index.tscreateResources() factory
exec.tsImperative command execution
file.tsDeclarative file management (SHA-256 comparison)
apt.tsDeclarative package management
service.tsSystemd service management (mixed)
directory.tsDeclarative directory management

src/recipe/ and src/inventory/

FilePurpose
recipe/loader.tsDynamic import of recipe .ts files
recipe/types.tsRecipeFunction, RecipeMeta
inventory/loader.tsDynamic import of inventory files, target resolution
inventory/types.tsInventory, Host, HostGroup, ResolvedHost

src/output/

Output and observability.

FilePurpose
events.tsEventBus, event types, NdjsonStream
reporter.tsPrettyReporter (spinners, colors) and QuietReporter
formats.tsJsonFormatter and MinimalFormatter
log_sink.tsFileLogSink for NDJSON audit logs
spinner.tsTerminal spinner
stderr.tsStderr writer for PrettyReporter

src/dashboard/

Web dashboard.

FilePurpose
server.tsDashboardServer — HTTP + WebSocket, run history
client.tsDashboardClient — CLI-side WebSocket producer
dev-server.tsMock event server for UI development
assets.tsEmbedded dashboard HTML (generated)
ui/React SPA (Vite + vite-plugin-singlefile)

src/cli/ and src/lib/

CLI and utilities. The CLI uses the incur framework.

FilePurpose
cli.tsCLI entry point
cli/index.tsRoot Cli.create() + command registration
cli/logger.tsLogger middleware for human-readable stderr output
cli/commands/run.tsrun command (includes --check for dry-runs)
cli/commands/init.tsinit command
cli/commands/inventory.tsinventory command
cli/commands/dashboard.tsdashboard command
cli/commands/schema.tsschema command group
lib/types.tsShared CLI option types
lib/parsers.tsDashboard address parser
lib/config.tsConfig file loading and validation
lib/errors.tsUser-facing error rendering utilities

Key design decisions

No state tracking

Every run queries actual server state via SSH. No state files, lock files, or drift databases. This simplifies the mental model — there's nothing to get out of sync. The trade-off is SSH round-trips for every check, mitigated by the optional TTL-based cache.

System SSH over native libraries

Ignition shells out to the system ssh and scp binaries instead of using a native SSH library. This avoids native dependencies, leverages the user's existing SSH config and agent, and requires no additional setup. The trade-off is subprocess overhead per command, mitigated by SSH multiplexing (ControlMaster).

TypeScript recipes via dynamic import

Recipes are full TypeScript files loaded with import(). This gives recipes the full power of the language — loops, conditionals, helper functions, npm packages — with type safety and IDE support. The trade-off is no sandboxing (recipes have full system access).

Capability-driven transport

The Transport interface defines four capabilities (exec, transfer, fetch, ping). Resources declare which capabilities they need. This allows future alternative transports (Docker exec, local exec) without rewriting resources.

EventBus architecture

The dashboard and NDJSON log sinks consume a shared lifecycle event stream when an EventBus is attached. Terminal reporters are still called directly by the runner. This keeps telemetry optional without forcing the TTY output path through the event bus.

Extension points

Extension pointInterfaceDescription
TransportTransportAdd new connection types (Docker, local, native SSH)
ResourcesResourceDefinitionAdd custom resources
RegistryResourceRegistryManage resource collections
Event consumersEventBus.on()Custom monitoring, alerting, logging
Output formatsReporterCustom output rendering

On this page