Reference

Platform support

What ota supports well on Linux, macOS, and Windows.

referenceautomation buildersintermediatestable2026-05-30

When to use this page

Use this page to understand where ota is first-class today and where the behavior is narrower.

This is the page that tells you what to expect before you assume a repo will behave the same everywhere.

  • Linux is the strongest supported target
  • macOS is also first-class
  • Windows is supported, but with narrower shell and portability guarantees
  • the support boundary matters most when a task body, healthcheck, or service command depends on shell syntax

Compatibility in practice

Compatibility is about whether the declared contract and the current runtime match well enough to trust the result.

  • a repo can be valid without being fully portable everywhere
  • a command can be supported on a platform without behaving identically to every other platform
  • ota should call out when behavior is narrower instead of pretending it is universal
  • the contract should tell you what will work before you run it

Platform support

  • Linux: first-class target
  • macOS: first-class target
  • Windows: supported, but not identical to Unix-like shells

What this means in practice

  • use Linux or macOS when you want the smoothest default path
  • use Windows when the repo contract explicitly handles its shell differences
  • split tasks into variants when one shell syntax cannot cover every platform cleanly
  • expect ota to warn when the declared behavior is narrower than the platform implies

What ota accepts

  • explicit platform declarations in the contract
  • task variants when different shells need different bodies
  • backend selection when the repo needs native, container, or remote execution
  • supported shell syntax for the declared platform
  • healthchecks and service commands that fit the current shell model

What ota rejects or warns about

  • shell syntax that only works on a different platform than the one declared
  • contract fields that imply portability ota does not actually provide
  • service or task commands that assume Bash-only behavior on Windows
  • hidden fallback behavior that would make the runtime look more capable than it is

Shell semantics

  • Unix-like systems use sh -lc
  • Windows uses cmd /C
  • there is no per-task shell selection in V1
  • if the command body assumes Bash behavior, that assumption must be explicit in the task or service design

Lifecycle semantics

Lifecycle tells you how ota treats execution reuse and isolation, not whether a repo is logically ready.

  • persistent reuses a named container when container execution is configured
  • ephemeral uses a fresh container when container execution is configured
  • ephemeral means fresh for this invocation, not reused
  • explicit task-execution options are capability requests, not hints; ota run and task-backed ota up refuse any accepted option that the selected task path cannot enforce
  • this fail-closed admission covers mode and lifecycle across both commands, plus command-specific options such as host-port, memory, and dependency overrides
  • native and remote task paths without a managed shared backend reject --ephemeral and --persistent instead of silently continuing with advisory lifecycle behavior
  • --sandbox-target oci_local requests provider-enforced sandbox application for a compatible explicit-platform ephemeral container lane; in agent mode Ota selects that target automatically when authoritative runtime controls require it and no other enforcing target is valid
  • --grant is separate from sandbox selection: prebound_file requires an exact signed grant ID, while Unix authority_broker resolves the one protected binding matching the contract and treats an optional --grant <authority-id> only as a non-secret label check. Broker dry-run reports requires_live_authorization without launcher contact; real governed execution consumes one exact lease after deterministic admission and before provisioning or work. Selected workflow instance, ordered prerequisite-instance closure, and ordinary ota up --ready-timeout change authority scope. Receipts bind runner-derived closure/effect/resource breadth without exposing raw resource values and retain a public verification binding without the live launcher descriptor. Proof scope includes every selected invocation by role and declaration order, lifecycle selected-service closure, target platform, host-port, memory, dependency selection, and normalized runtime readiness timeout. Runtime and lifecycle proof retain one transaction across that complete proof scope and cleanup, and their archives re-derive terminal authority
  • real granted execution creates a runner-owned crossing transaction before selected-lane mutation and archives its terminal outcome; dry-run verifies admission without creating a transaction or crossing record
  • refused task dry-run and workflow-refusal receipts carry typed prebound-file authority-source, selected authority/grant, stable reason, and execution-not-started evidence without minting crossing authority. When Ota derived the selected scope, they also carry scope and contract identities, boundary family, and classification for external issuance; workflow receipts keep their refusal boundary distinct through scope_boundary_family and scope_classification
  • the first local transaction carrier is runner_local_content_addressed: Ota locks and internally reconciles it, but does not claim independent authentication against same-user writes to .ota/state
  • the repository names only governance.crossing_authority.authority_id; it must not carry trust keys, signed bundle paths, monotonic sequence state, or caller-selectable trust overrides
  • a grant never bypasses --agent safety, and the first signed-file carrier refuses free-form task inputs rather than hashing or exposing potentially secret values
  • container.platform applies to variants, inputs, environment, service bindings, requirements, and every execution-backend container creation, not only sandbox execution; the current backend accepts Linux OCI targets only, and oci_local requires the explicit pin so the runner host platform cannot silently replace it
  • the first OCI adapter admits finite run, script, and command bodies only; typed prepare/action/Compose/launch/attach bodies, requirements, services, conditional checks, and authoritative lifecycle-proof closures refuse before execution
  • the stock OCI target enforces a read-only repository root, the exact declared mount set, existing writable carve-outs, protected-path write refusal, bounded external IP-network denial, terminal reinspection, and cleanup of Ota's exact boundary; unsupported targeted egress, inherited service networks, image-declared volumes, undeclared mounts, runtime-control sockets, native lanes, and unsafe path aliases refuse before setup
  • --native, --container, and --remote are backend shorthands for the mode-bearing repo commands, including ota run, ota up, ota doctor, and ota receipt
  • ota run --ephemeral still provisions only the missing or mismatched contract requirements in that fresh container; it does not reinstall everything from scratch
  • --ephemeral is the shorthand for --lifecycle ephemeral on ota run, ota up, ota doctor, and ota receipt
  • --persistent is the shorthand for --lifecycle persistent on ota run, ota up, ota doctor, and ota receipt
  • use ota clean when a container-backed task left stale runtime state behind; ota removes current and drifted repo-owned persistent containers plus dependency-isolation volumes
  • outside task-backed execution, service diagnosis may still describe lifecycle as advisory; explicit ota run and task-backed ota up lifecycle requests never silently degrade
  • ordinary ephemeral container execution and provider-enforced oci_local execution both use invocation-scoped boundaries; only the provider-enforced lane carries inspected policy-application and confirmed-cleanup evidence
  • dry-run never starts a provider boundary merely to probe runtime/tool availability; real OCI execution binds each provider-backed precondition probe to the exact admitted requirement-owning segment, records it as a cleanup-confirmed precondition_probe invocation, retains terminal evidence on a blocking refusal, and never lets it substitute for task-execution evidence

What users should expect

  • expect the best behavior today on Linux and macOS
  • expect Windows to work, but with more explicit shell syntax and fewer portability assumptions
  • expect support warnings when the contract uses behavior the current platform cannot honor cleanly
  • use task variants when one task name needs different bodies on different platforms
  • treat shell behavior as part of the contract, not an invisible compatibility layer
  • treat support policy as a boundary, not as a promise that every command will behave the same everywhere

When this matters

  • a repo has Windows users but shell bodies were written for Unix syntax
  • a task works locally but fails in CI because the runner shell differs
  • a service healthcheck uses shell features the current platform does not support
  • a maintainer needs to decide whether to split a task into variants

What this is not

  • not a promise of identical shell behavior on every OS
  • not per-task shell selection
  • not automatic shell portability translation
  • not isolated ephemeral workspace creation in V1
  • not a guarantee that any shell snippet copied between platforms will work unchanged

Relationship to other surfaces

  • contract defines the repo’s execution intent
  • shell-semantics defines the actual command invocation model
  • doctor should explain support and compatibility failures clearly and early
  • run and up should honor the platform boundary instead of hiding it
  • support policy tells you what platform boundaries ota expects you to respect