preflight

command module
v1.0.0-beta.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2026 License: ISC Imports: 1 Imported by: 0

README

Preflight

Configuration management CLI for Windows exhibit PCs in museum and gallery environments. Compiles to a single static binary with no runtime dependencies.

preflight apply playbooks/lobby.yml --target lobby

Overview

Preflight is a declarative, idempotent configuration management tool built for managing fleets of Windows PCs — kiosk displays, interactive exhibits, gallery hardware. It takes inspiration from Ansible but is designed around Windows-native primitives and a single redistributable binary that needs no Python, no agent, and no network access at runtime.

Key properties:

  • Single binary. Drop preflight.exe on any Windows machine and it runs. No installer, no runtime dependencies, no Python.
  • Idempotent by design. Every built-in module implements a Check() contract. The runner always checks current state before making any change. Running the same playbook twice is safe.
  • Dry-run first-class. --check mode calls Check() on every task and reports what would change — it never modifies the system. diff shows you the specific changes.
  • Offline-capable. The standard library ships embedded in the binary. Remote action refs are fetched once and cached. A staged bundle can be applied with no network access at all.
  • Structured output. --output json / --output jsonl for CI integration and log pipelines.

Concepts

Preflight has three layers: Modules → Actions → Playbooks.

Modules

The lowest layer. Go code compiled into the binary. Each module encapsulates a single primitive Windows operation — write a registry key, manage a service, copy a file, run a PowerShell snippet. Every module implements:

Check() → (needed bool, err)   // Is the system already in desired state?
Apply() → err                  // Make it so

The runner always calls Check first and skips Apply if nothing needs to change.

Built-in modules: registry, service, file, directory, package, shortcut, scheduled_task, user, windows_feature, environment, firewall_rule, powershell, shell, reboot, wait.

Actions

The middle layer. Parameterized, reusable bundles of tasks defined in YAML. Actions are the unit of sharing and versioning — similar to GitHub Actions or Ansible roles. An action takes typed inputs, runs a sequence of tasks (which can themselves call other actions), and optionally emits outputs.

Actions are resolved from a chain of sources:

1. Embedded stdlib     preflight/kiosk-mode
2. Local project       ./actions/myorg/display-config/
3. User cache          ~/.preflight/actions/myorg/name@v1.2/
4. Remote Git          github.com/myorg/actions/signage@v2.1

Remote sources are fetched once and pinned by exact Git SHA in preflight.lock.

Playbooks

The top layer. What you write per machine or per environment. A playbook declares what actions to run, with what variables, against which targets.

name: lobby-baseline
vars:
  resolution: "3840x2160"

tasks:
  - name: Apply kiosk mode
    uses: preflight/kiosk-mode
    with:
      autologin_user: exhibituser

  - name: Configure display
    uses: myorg/display-config
    with:
      resolution: "{{ vars.resolution }}"

Installation

macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/bluecadet/preflight/main/install.sh | sh

Installs to /usr/local/bin by default. Override with PREFLIGHT_INSTALL_DIR=/your/path.

Windows (PowerShell):

irm https://raw.githubusercontent.com/bluecadet/preflight/main/install.ps1 | iex

Installs to %LOCALAPPDATA%\preflight\ and adds it to your user PATH.

Or download a specific release manually from the releases page.

Build from source
# Windows (primary target)
GOOS=windows GOARCH=amd64 go build -o dist/preflight-windows-amd64.exe .
GOOS=windows GOARCH=arm64 go build -o dist/preflight-windows-arm64.exe .

# macOS / Linux
go build -o dist/preflight .

Or via Make:

make build-windows-amd64
make build-windows-arm64
make build-local

Usage

Apply a playbook
preflight apply playbooks/lobby.yml
preflight apply playbooks/lobby.yml --target lobby-pc-01
preflight apply playbooks/gallery.yml --target gallery --var content_root=D:\\content
Dry-run / check mode
preflight check playbooks/lobby.yml
preflight apply playbooks/lobby.yml --check
See what would change
preflight diff playbooks/lobby.yml --target lobby-pc-01
Plan without executing
preflight plan playbooks/lobby.yml

Resolves the full execution plan and prints it — no connection to targets required.

Validate configuration
preflight validate playbooks/lobby.yml

Parses and validates playbook, inventory, and action schemas without executing anything.

Gather facts
preflight facts                    # local machine
preflight facts lobby-pc-01        # remote target

Returns a JSON object of system facts (OS version, disk layout, environment) used in when: conditions.

Manage actions
preflight action list
preflight action info preflight/kiosk-mode
preflight action fetch github.com/myorg/actions/signage@v2.1
Inspect state
preflight state show

Prints the result of the last apply from state/provision.json.


Project Layout

project/
├── preflight.yml          # project-level config and vars
├── inventory.yml          # target machine definitions
├── preflight.lock         # pinned remote action SHAs
├── playbooks/
│   ├── lobby.yml
│   ├── gallery.yml
│   └── base.yml           # shared baseline, imported by others
├── actions/               # local custom actions
│   └── myorg/
│       └── display-config/
│           └── action.yml
└── vars/
    ├── prod.yml
    └── staging.yml
preflight.yml
project: natural-history-museum
environment: production

vars:
  content_root: "C:\\Exhibits\\content"
  app_root: "C:\\Exhibits\\app"
  fileserver: "\\\\nas01\\exhibits"
inventory.yml
groups:
  all:
    vars:
      timezone: "America/New_York"

  lobby:
    vars:
      resolution: "3840x2160"
    hosts:
      - name: lobby-pc-01
        address: 192.168.1.10
        transport: winrm
      - name: lobby-pc-02
        address: 192.168.1.11
        transport: winrm

  gallery-2:
    vars:
      resolution: "1920x1080"
    hosts:
      - name: gallery2-pc-01
        address: 192.168.1.20
        transport: winrm
action.yml
name: myorg/display-config
version: 1.0.0
description: Configure display resolution and orientation

inputs:
  resolution:
    type: string
    required: true
    description: "Target resolution, e.g. 1920x1080"
  orientation:
    type: string
    default: landscape

tasks:
  - name: Set resolution via PowerShell
    powershell:
      script: |
        Set-DisplayResolution -Width {{ vars.resolution | split('x')[0] }} -Height {{ vars.resolution | split('x')[1] }}
    when: "{{ facts.os.version >= '10' }}"

Variable Precedence

Later entries win:

Built-in defaults
  ← preflight.yml
    ← inventory group vars
      ← inventory host vars
        ← playbook vars
          ← --var CLI flags

Templates use Jinja2-like syntax: {{ vars.foo }}, {{ facts.os.version }}.


Execution Pipeline

Preflight processes every playbook through four explicit phases:

Phase What it does
Plan Parse playbook, resolve action refs, expand into a flat task DAG, validate inputs. Pure computation — no I/O against targets.
Fetch Download remote action refs not in cache. Network-heavy; runs once for N targets.
Stage Assemble a self-contained bundle (ZIP) that can be pushed to air-gapped targets.
Apply Execute the task graph. For each task: Check(), skip if already correct, Apply() if change needed, record result.

Run only up to a specific phase with --phase plan|fetch|stage|apply.


Global Flags

Flag Description
-t, --target Target host(s) or group(s) from inventory
-e, --var key=value Override a variable
--tags tag1,tag2 Only run tasks with these tags
--skip-tags tag1 Skip tasks with these tags
--check Dry-run mode — check without applying
--diff Show file diffs
-v, --verbose Verbose output
--output text|json|jsonl Output format
--concurrency N Max parallel targets (0 = unlimited)
--timeout duration Execution timeout
--phase Run only up to this phase

Standard Library

The preflight/ namespace is embedded in the binary and always available without fetching.

Action Description
preflight/autologin Set up automatic login

Plugin System

Preflight supports external plugins that extend the module library. Plugins are executables (any language) that speak JSON-RPC over stdin/stdout. They are discovered from:

  1. Alongside the binary
  2. ~/.preflight/plugins/
  3. ./plugins/ in the project directory

The pkg/plugin/sdk package provides a Go SDK for plugin authors that handles protocol boilerplate.


Building & Testing

go test ./...                       # all tests
go test ./internal/runner/...       # specific package
go vet ./...                        # vet

License

ISC

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
template
Package template provides Jinja-like template evaluation for preflight action and playbook YAML values.
Package template provides Jinja-like template evaluation for preflight action and playbook YAML values.
pkg
plugin/sdk
Package sdk provides the plugin author interface and helpers for implementing preflight plugins as standalone executables speaking JSON-RPC over stdin/stdout.
Package sdk provides the plugin author interface and helpers for implementing preflight plugins as standalone executables speaking JSON-RPC over stdin/stdout.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL