Definitions
Core terms
This section defines the core terms used throughout this documentation. These terms describe how Courier jobs are structured, how work is delegated, and how intent is expressed and executed.
A Courier job is an ordered collection of steps that together express a complete operational process or business outcome. A job defines what must be accomplished and the sequence in which it should occur, without exposing how individual actions are executed.
Example: A job that assesses security compliance across a set of systems, remediates approved findings, and creates an auditable change record aligned with ITIL practices to document the actions taken and their outcomes.
A job step is a single unit of work within a Courier job. Each step represents one discrete intent and delegates execution to exactly one interpreter.
Example: A step that runs a compliance scan using the InSpec interpreter.
An interpreter is a contract-defined capability that performs exactly one job step by translating structured intent into concrete execution against an underlying engine. Interpreters constrain what’s allowed and how work is performed to provide consistency, safety, and predictability.
Example: The InSpec interpreter accepts a declarative scan request and translates it into one or more
inspecCLI invocations.
An execution engine is the system that performs the actual work on behalf of an interpreter. Engines may be command-line tools, APIs, SDKs, operating system facilities, or embedded runtimes. Engines are intentionally hidden from users.
Example: The
inspeccommand-line tool invoked by the InSpec interpreter.
A contract is the stable, user-facing definition of how intent is expressed to an interpreter and how results are returned. Contracts define allowed inputs, validation rules, and output structure.
Example: The documented command schema for the InSpec interpreter’s
scanoperation.
Intent is a declarative expression of the outcome a user wants to achieve, independent of the specific steps required to achieve it.
Example: “Run the CIS Linux baseline against this node” rather than specifying command flags or execution steps.
An artifact is a structured or referenced output produced by an interpreter that’s intended for downstream consumption or inspection.
Example: A JSON compliance report generated by an InSpec scan.
The job context is a structured key–value store maintained by the Courier Runner that carries inputs into job steps and propagates selected outputs forward to subsequent steps.
Example: A compliance score produced by one step and used as a condition for a later remediation step.
Interpreter vs engine
Understanding the distinction between a Courier interpreter and an execution engine is critical for anyone using or extending Courier. Confusing these two concepts leads directly to unstable contracts, poor user experience, and unsafe behavior.
An execution engine is responsible for doing work. It exposes low-level controls, flags, APIs, and operational details that are optimized for flexibility and power, not for stability or safety. Engines are expected to change over time as features evolve, interfaces shift, and underlying platforms are updated.
An interpreter, by contrast, exists to deliberately not expose those details. Its purpose is to provide a stable, constrained, and user-oriented contract that expresses intent rather than mechanism. Interpreters translate intent into one or more concrete operations against an engine while enforcing consistency, validation, and safety.
This distinction matters because an interpreter contract is a promise. Users bind automation, workflows, and expectations to that contract. If an interpreter merely mirrors the surface area of its underlying engine, the contract inherits the engine’s complexity, volatility, and risk.
For this reason, interpreter commands must not be designed as thin wrappers around engine interfaces. They shouldn’t expose raw flags, pass through arbitrary arguments, or require users to understand engine-specific behavior. Instead, interpreter commands must be shaped to reflect what the user is trying to achieve, not how the engine happens to do it today.
By separating interpreters from engines in this way, Courier achieves several goals simultaneously:
- Users interact with stable, intention-driven capabilities rather than volatile tools
- Complex or multi-step engine behavior can be safely encapsulated behind a single intent
- Security boundaries are enforced through constraint rather than convention
- Engine evolution can occur without breaking user workflows
In Courier, users select interpreters, not engines. Engines are implementation details. Interpreters are the contract.
Interpreter contract
An interpreter contract defines the stable, user-facing agreement between a Courier interpreter and its users. It specifies how intent is expressed, what behavior can be relied upon, and how results are returned. For users, the contract represents a set of guarantees. For implementers, it represents a set of obligations.
An interpreter contract is more than a schema. It captures the meaning of inputs, the constraints under which an interpreter operates, and the observable behavior a user can depend on over time. Users compose Courier jobs against these contracts, not against the details of any underlying execution engine.
A well-designed interpreter contract:
- Expresses intent in terms meaningful to the user or business process
- Constrains what actions are allowed to ensure safety and consistency
- Abstracts and absorbs complexity from underlying engines
- Produces predictable, structured output suitable for downstream consumption
Interpreter contracts are intentionally long-lived. Once published, a contract should evolve conservatively. Additive changes that preserve existing behavior are preferred. Changes that alter meaning, behavior, or guarantees must be introduced through new commands or versions rather than by repurposing existing fields.
Interpreter contracts also clearly distinguish between:
- Interpreter-defined fields, which are part of the user-facing contract and must be documented, validated, and supported
- Runner-defined fields, which are supplied by the Courier Runner to manage execution context and lifecycle and aren’t specific to any interpreter
Users rely on interpreter contracts as promises. Breaking those promises undermines trust in Courier jobs and workflows. Designing and maintaining interpreter contracts therefore requires the same discipline as designing public APIs.