Productslamboot-tools › CLI Contract

CLI Contract

Universal flags, exit codes, and the JSON envelope that every tool shares.

COMMAND SHAPE

One shape, all tools

Every command in lamboot-tools 0.9.1 follows one structure. Learn one tool and you can predict the rest.

Command shape
<tool> [GLOBAL FLAGS] SUBCOMMAND [SUBCOMMAND FLAGS] [POSITIONAL ARGS]

Global flags come before the subcommand; subcommand-specific flags come after. The -- separator is honored for positional arguments that resemble flags. Short aliases exist only for the four highest-frequency flags: -h, -v, -q, -y.

UNIVERSAL FLAGS

Flags that behave identically across all tools

FlagBehavior
--help, -hShow terse help with an after-help summary of subcommands.
--versionPrint <tool> <version> (lamboot-tools <toolkit-version>).
--jsonEmit the unified JSON envelope (schema v1). All human output goes to stderr; color codes suppressed on stdout.
--json-schemaPrint the JSON Schema (draft 2020-12) this tool emits. Safe to pipe through jq.
--verbose, -vInformational output beyond the defaults.
--quiet, -qWarnings and errors only. Silent on info.
--no-colorDisable ANSI color. Auto-disabled on non-TTY stdout, when NO_COLOR is set, or when TERM=dumb.
--dry-runPrint planned actions and make no changes. Available on every mutating tool.
--yes, -yAnswer yes to interactive confirmations.
--forceSkip safety checks. Per-tool semantics are documented in each tool's help.
--autoNon-interactive automation. Implies --yes.
--offline DISKOperate on an unmounted disk or image. Supported by every tool except lamboot-migrate.
--suggest-next-commandPrint the recommended follow-up command based on the output.

Root and color notes

  • Read operations and informational paths (--help, --version, --json-schema) run unprivileged.
  • Mutating operations require root and refuse with exit code 7 when run without it.
  • Tools never self-escalate. --offline DISK always requires root.
  • Color is enabled on TTY stdout by default. --no-color strips ANSI codes from output only; textual content is byte-for-byte identical.
EXIT CODES

Eight exit codes, shared by every shell tool

The ten shell CLIs and the lamboot-toolkit dispatcher share one exit-code contract. The two bundled Rust binaries (lamboot-capcheck, lamboot-reader) use their own 0 through 3 contract.
CodeConstantMeaningUse in automation
0EXIT_OKSuccess. All requested operations completed.Continue the pipeline.
1EXIT_ERRORFatal error. Some state may have changed; see output.Log and alert.
2EXIT_PARTIALMixed success. Some operations succeeded; some failed.Log and re-run idempotent subcommands.
3EXIT_NOOPNothing to do. System already in the requested state.Idempotent-OK. Do not log as a failure.
4EXIT_UNSAFERefused due to a safety check. No state changed.Inspect output, then override or fix.
5EXIT_ABORTUser declined an interactive confirmation. No state changed.Expected when the user aborts.
6EXIT_NOT_APPLICABLEOperation does not apply to this system.Skip silently. Not a failure.
7EXIT_PREREQUISITERequired tool or capability is missing.Install the missing thing and retry.

Key insight: warnings alone do not fail. A diagnostic tool that emits only info and warning findings exits 0. A finding at severity error or critical exits 2.

Read-only vs. mutation: read-only runs report what was found (healthy exits 0, issues found exits 2). Mutation runs report what was done (all applied exits 0, some failed exits 2, refused exits 4).

Scripting against exit codes
sudo lamboot-migrate to-uefi
rc=$?
case $rc in
    0) echo "Migration complete" ;;
    3) echo "Already UEFI; nothing to do" ;;
    4) echo "Refused by a safety check. Inspect output and fix." ;;
    5) echo "Aborted by user" ;;
    *) echo "Migration failed with exit $rc" >&2; exit $rc ;;
esac
JSON ENVELOPE

The stable schema v1 envelope

Under --json, every tool emits the same top-level envelope. Additive changes do not bump schema_version; breaking changes bump it and the toolkit major version in lockstep. Pin on schema_version == "v1".
Envelope example
{
  "schema_version": "v1",
  "tool": "lamboot-diagnose",
  "version": "0.9.1",
  "toolkit_version": "0.9.1",
  "timestamp": "2026-06-23T14:37:22Z",
  "host": "laptop-01",
  "run_id": "2026-06-23T14-37-22-a1b2c3",
  "command": "lamboot-diagnose --json",
  "dry_run": false,
  "exit_code": 0,
  "summary": {
    "status": "warn",
    "findings_total": 24,
    "findings_by_severity": {"critical": 0, "error": 0, "warning": 1, "info": 23}
  },
  "findings": [ /* one object per observation */ ],
  "actions_taken": [ /* one object per mutation; empty for read-only tools */ ],
  "backup_dir": null
}

Top-level fields

FieldTypeDescription
schema_versionstringAlways "v1" in this toolkit major version.
toolstringExecutable name, e.g. lamboot-diagnose.
versionstringPer-tool semver. Each tool versions independently.
toolkit_versionstringThe lamboot-tools release bundling this tool.
timestampstringISO-8601 UTC with a Z suffix. No local time, no fractional seconds.
run_idstringUnique within a host across time. Correlates log lines, backup directories, and JSON from one invocation.
commandstringExact argv joined by single spaces. Passwords redacted to ***.
dry_runbooleantrue if --dry-run was passed.
exit_codeintegerThe code the process will return. Set at emit time, always accurate.
summaryobjectAggregate counts and an overall verdict.
findingsarrayObservations. One object per finding.
actions_takenarrayMutations performed. Empty for read-only tools.
backup_dirstring or nullPath to the backup directory when one was created.

summary.status values

ValueMeaning
passNo issues.
warnOnly warning-severity findings.
failCritical or error-severity findings present.
noopNothing to do.
errorThe tool itself hit an error.
unsafeRefused by a safety check.
abortUser declined a confirmation.

findings[] entry

Finding with remediation
{
  "id": "esp.free_space",
  "category": "esp",
  "severity": "warning",
  "status": "warn",
  "title": "ESP is 89% full",
  "message": "ESP at /boot/efi has 54 MB free of 512 MB (10.1% free)",
  "context": {
    "esp_mount": "/boot/efi",
    "free_bytes": 56623104,
    "total_bytes": 536870912,
    "percent_free": 10.1
  },
  "remediation": {
    "summary": "Remove stale kernel images to free space",
    "command": "sudo lamboot-esp clean --dry-run",
    "doc_url": "https://github.com/lamco-admin/lamboot-tools"
  }
}

Finding IDs are dotted-path stable identifiers of the form <category>.<specific>[.<subspecific>], for example esp.free_space, bootloader.grub.pe_format, vm.lamboot_state. IDs are semver-stable within the toolkit major version: adding new IDs is additive, renaming an existing ID is a major-version event.

severity answers "how bad is this if unaddressed?" (critical, error, warning, info). status answers "what did this check report?" (pass, warn, fail, skip). The two fields are orthogonal.

actions_taken[] entry (mutation tools only)

Action taken
{
  "action": "partition.create",
  "target": "/dev/sda2",
  "result": "ok",
  "reversible": true,
  "backup_ref": "/var/backups/lamboot-migrate-2026-06-23T14-37-22-a1b2c3/",
  "dry_run": false,
  "details": {
    "partition_number": 2,
    "size_mb": 512,
    "typecode": "EF00"
  }
}
CONSUMPTION GUARANTEES

Four properties that always hold

Valid JSON on stdout

Every tool's --json output passes jq -e . on stdout, regardless of exit code.

Valid JSON Schema

Every tool's --json-schema output is a valid JSON Schema (draft 2020-12) describing the envelope and that tool's context shape.

No log lines on stdout

No log lines go to stdout when --json is active. All human output goes to stderr.

No color codes on stdout

ANSI color codes are suppressed on stdout when --json is active.

Filter actionable findings
# Extract remediation commands for all warning-or-above findings
lamboot-diagnose --json | \
    jq -r '.findings[] | select(.severity != "info") | .remediation.command'

# Fail if any warning-or-above finding is present
count=$(lamboot-diagnose --json | jq '[.findings[] | select(.severity != "info")] | length')
if [[ "$count" -gt 0 ]]; then exit 1; fi