hostwright.yaml reference
The manifest filename is hostwright.yaml. A JSON Schema is checked in at
schemas/hostwright-yaml.schema.json in the core repository.
Current shape
Section titled “Current shape”version: 2project: api-localimagePolicy: allow-tags
volumes: database-data: provider: hostwright-local capacity: 2GiB accessMode: read-write-once reclaimPolicy: retain
services: api: image: ghcr.io/example/api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa replicas: 1 platform: os: linux architecture: arm64 resources: cpus: 2 memory: 1GiB user: 1000 group: 1000 workdir: /app entrypoint: ["/app/server"] command: ["serve"] init: true dependsOn: - service: database condition: ready ports: - "8080:8080" volumes: - type: volume source: database-data target: /var/lib/api env: APP_ENV: development secretEnv: API_TOKEN: keychain://hostwright.api/api-token labels: app.example.role: api probes: startup: exec: ["/app/server", "check"] interval: 2s failureThreshold: 30 readiness: http: port: 8080 path: /health interval: 10s liveness: tcp: port: 8080 interval: 20s restart: policy: on-failure update: strategy: rolling maxSurge: 1 maxUnavailable: 0 progressDeadline: 300s hooks: postStart: exec: ["/app/server", "warm"] preStop: exec: ["/app/server", "drain"] readOnlyRootFilesystem: true shmSize: 64MiBCanonical Hostwright manifests use this contract only. Do not add Kubernetes
apiVersion, kind, or metadata fields, or Compose fields such as build,
depends_on, deploy, or networks; the parser rejects them.
Version policy
Section titled “Version policy”version: 2 is required for execution. Versionless and explicit version: 1
manifests are legacy input and fail closed with migration guidance. Preview a
deterministic non-writing conversion:
hostwright migrate preview hostwright.yamlhostwright migrate preview hostwright.yaml --jsonThe preview inserts or replaces the version contract, converts legacy
health into an equivalent typed liveness probe, is idempotent for v2
input, and rejects future versions. Hostwright never silently downgrades or
rewrites a manifest during validate, plan, or a lifecycle command.
Project and image policy
Section titled “Project and image policy”| Field | Form | Rules |
|---|---|---|
version |
required scalar | Exactly 2. |
project |
scalar | Required, DNS-like name. |
imagePolicy |
optional scalar | allow-tags (default) or require-digest. See Image policy. |
Volumes
Section titled “Volumes”A top-level volumes: map declares named volumes that services mount by
name. Each declaration requires a supported provider (hostwright-local
today), a normalized capacity, an accessMode of read-write-once or
read-only-many, and a reclaimPolicy. Declaration labels cannot use the
reserved dev.hostwright. prefix.
volumes: database-data: provider: hostwright-local capacity: 2GiB accessMode: read-write-once reclaimPolicy: retainA service mounts a declared volume with a typed entry in its own volumes:
list. See Storage. Volume lifecycle (snapshots, backups, quota,
recovery) is managed with hostwright volume,
not through the manifest.
Service identity and scheduling
Section titled “Service identity and scheduling”| Field | Form | Rules |
|---|---|---|
image |
scalar | Required. No whitespace, must not begin with -; digest references use @sha256:<64 lowercase hex>. |
replicas |
integer | 1 to 256. Default 1. |
platform.os |
scalar | linux. |
platform.architecture |
scalar | arm64, or amd64 where Rosetta capability is available. |
resources.cpus |
scalar | Positive integer. |
resources.memory |
scalar | Normalized unit, e.g. 512MiB, 1GiB. |
user, group |
integer | Numeric UID/GID. |
workdir |
scalar | Normalized absolute container path. |
entrypoint, command |
inline array | Bounded non-empty tokens. |
init |
boolean | Runs an init process in the container. |
dependsOn |
list | Each entry names a declared service and a condition: started, ready, or completed. Self-reference and cycles are rejected. |
dependsOn is what makes a multi-service hostwright up converge in a safe
order: a service with condition: ready waits for its dependency’s readiness
probe before starting. See Lifecycle commands.
Environment, secrets, and labels
Section titled “Environment, secrets, and labels”| Field | Form | Rules |
|---|---|---|
env |
string map | Keys shell-safe ([A-Za-z_][A-Za-z0-9_]*). Plaintext credential-like keys are rejected; use secretEnv. |
secretEnv |
string map | Values are typed references (see below). A key cannot appear in both env and secretEnv. |
labels |
string map | Bounded; cannot use the reserved dev.hostwright. prefix. |
secretEnv accepts five reference schemes: keychain://<service>/<account>
for a Hostwright-owned Keychain item, env-file:///absolute/path#KEY and
local-file:///absolute/path for guarded local files, and
external://<provider>/<item> or plugin://<provider>/<item> for an
explicitly registered provider. A reference resolves once, at confirmed
execution, and never appears in plans, state, logs, or diagnostics. See
Secrets and Keychain.
Networking and storage
Section titled “Networking and storage”The executable manifest has no bind-address field: Hostwright-created port
publishes always bind to 127.0.0.1. Custom networks, DNS, and network
policy are not yet part of the executable manifest.
| Field | Form | Rules |
|---|---|---|
ports |
string list | "host:container", values 1–65535. Fixed localhost ports cannot collide, including across replicas. |
A service’s own volumes: list uses typed mounts, distinct from the
top-level volume declarations:
volumes: - type: volume source: database-data # must name a top-level volumes: declaration target: /var/lib/api - type: bind source: /Users/me/config target: /etc/api readOnly: true - type: tmpfs target: /tmp/scratch sizeLimit: 64MiBBind sources cannot expose host root, device files, path traversal,
symlinks, or unsafe ownership. volume-type sources must reference a
declared top-level volume.
Health, restart, and updates
Section titled “Health, restart, and updates”Typed probes replace the legacy scalar health field (still accepted and
converted automatically by migrate preview). Each probe stage (startup,
readiness, liveness) declares exactly one action:
probes: startup: exec: ["/app/server", "check"] interval: 2s failureThreshold: 30 readiness: http: port: 8080 path: /health interval: 10s liveness: tcp: port: 8080 interval: 20sexeccrosses the runtime provider’s process-control boundary; it never uses a host shell.httpandtcpprobes target only the container’s own loopback and a port the service declares;httpfollows at most three same-origin loopback redirects.
Startup gates readiness and liveness. Readiness gates dependent services and rollout promotion. Liveness drives the existing restart policy.
| Field | Form | Rules |
|---|---|---|
restart.policy |
scalar | no, on-failure, or unless-stopped. |
update.strategy |
scalar | rolling or recreate. |
update.maxSurge, update.maxUnavailable |
integer | Bounds for a rolling update; must be internally consistent. |
update.progressDeadline |
scalar | Seconds form; must be positive. |
hooks.postStart, hooks.preStop |
exec block | Run after start / before stop. Same bounded exec shape as probes. |
readOnlyRootFilesystem |
boolean | Mounts the container root filesystem read-only. |
shmSize |
scalar | Normalized unit for /dev/shm. |
rosetta |
boolean | Requires platform.architecture: amd64 plus virtualization support. |
Parser contract
Section titled “Parser contract”Hostwright pins Yams 6.2.2 inside HostwrightManifest and applies its own
strict, source-aware decoding contract. Input is limited to one UTF-8 YAML
document, 1 MiB, nesting depth 64, and 100,000 expanded nodes. Duplicate keys
are rejected at every level. Anchors, aliases, merge keys, custom tags,
multiple documents, ambiguous scalar coercion, unknown fields, and limit
violations fail with a stable line, column, and manifest-path diagnostic.
Canonical encoding uses fixed field order and lexically sorted maps; every
checked-in manifest satisfies parse → canonical encode → parse equality.
hostwright import-stack converts a smaller
stack-file subset into this shape: conversion only, with the normal
validator run on the output. Validation itself never contacts a registry or
Apple container.
Image policy
Section titled “Image policy”imagePolicy is a local string policy. The default allow-tags accepts
tag-based references such as ghcr.io/example/api:latest. require-digest
rejects mutable tag-only references and requires digest pinning:
imagePolicy: require-digest
services: api: image: ghcr.io/example/api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarequire-digest is also a prerequisite for imageTrust, imageSBOM,
imageVulnerability, and imageProvenance below. Lifecycle image resolution
stays local and offline: Hostwright does not contact a registry or pull
missing content as part of up, down, or the other lifecycle commands.
Pull images ahead of time with hostwright image pull.
imageTrust
Section titled “imageTrust”An optional offline image-signature trust policy on top of digest pinning:
imagePolicy: require-digestimageTrust: version: 1 threshold: 2 trustedRoot: /Users/me/.config/hostwright/sigstore-trusted-root.json authorities: - id: release-key type: keyed publicKey: /Users/me/.config/hostwright/release.pub - id: ci-oidc type: keyless issuer: https://token.actions.githubusercontent.com identity: https://github.com/example/repo/.github/workflows/release.yml@refs/heads/mainversion is locked to 1. threshold (1–8) cannot exceed the number of
declared authorities (1–8). Each authority is keyed (an absolute
publicKey path) or keyless (an HTTPS issuer, a non-empty identity,
and a Sigstore TrustedRoot JSON document). Optional notBefore, notAfter,
and revokedAt timestamps are RFC3339 and chronologically ordered.
Verification accepts only Sigstore bundle v0.3 message signatures through an
external cosign executable (see hostwright registry trust verify) and does not expose a way to
disable transparency or identity checks. The manifest text and exact trust
material are bound into lifecycle plan confirmation, so changing either
invalidates a pending confirmation.
imageSBOM
Section titled “imageSBOM”Declares which image-SBOM formats Hostwright may generate and whether evidence is required before a lifecycle mutation:
imagePolicy: require-digestimageSBOM: version: 1 requirement: required formats: - spdx-json - cyclonedx-jsonversion is locked to 1. requirement is optional or required.
formats holds one or both of spdx-json and cyclonedx-json. A required
policy re-verifies the SBOM evidence graph before execution and again during
recovery, so evidence cannot go stale between a dry run and a confirmed
apply.
imageVulnerability
Section titled “imageVulnerability”Declares an explainable, signed vulnerability-report policy:
imagePolicy: require-digestimageTrust: threshold: 1 authorities: - id: release type: keyed publicKey: /Users/me/.config/hostwright/release.pubimageVulnerability: version: 1 severityThreshold: high minimumVulnerabilityAgeSeconds: 86400 exploitability: known-exploited fixAvailability: fix-available maximumDatabaseAgeSeconds: 604800 staleAction: fail-closed unavailableAction: fail-closed exceptionApproval: required allowlist: - vulnerabilityID: CVE-2026-0001 packagePURL: pkg:swift/example@1.0.0 reason: Not reachable in this deployment expiresAt: "2026-12-01T00:00:00Z"Requires imagePolicy: require-digest plus imageTrust. Severity is low,
medium, high, or critical; staleAction and unavailableAction are
fail-open or fail-closed. Selectors are ANDed: a candidate finding must
clear severity, age, exploitability, and fix-availability all at once.
fail-open never overrides a known matching finding. Allowlist entries
require a reason and an RFC3339 expiresAt, and expire automatically.
exceptionApproval: required means an allowlist entry alone is not enough.
See hostwright registry vulnerability grant-exception.
imageProvenance
Section titled “imageProvenance”Declares which signed build provenance may authorize an image:
imagePolicy: require-digestimageProvenance: version: 1 requirement: required builderIDs: - urn:hostwright:builder:apple-container buildTypes: - https://hostwright.dev/build-types/apple-container/v1 signers: - id: release-builder publicKey: /Users/me/.config/hostwright/provenance.pub maximumAgeSeconds: 604800 requireReproducible: trueRequires imagePolicy: require-digest. requirement is optional or
required. Builder and build-type entries are unique, bounded, and
credential-free (HTTPS or URN only). maximumAgeSeconds is between 60 and
one year. A required policy checks the in-toto/SLSA statement’s subject,
builder, build type, timestamps, materials, and signer authority before
execution, and again during recovery.
Storage
Section titled “Storage”Named volumes declared at the top level and mounted with a type: volume
entry execute through the exact Hostwright-owned volume lifecycle: guarded
mounts, snapshots, verified backup/restore, quota and pressure accounting,
reclaim policy, and orphan recovery. See hostwright volume.
Untrusted manifests
Section titled “Untrusted manifests”Treat manifests from third parties as untrusted input. hostwright validate, hostwright plan, and every lifecycle command’s --dry-run are
non-mutating gates, but an accepted manifest can still describe images,
ports, environment values, paths, hooks, probes, and process arguments that
deserve review before a confirmed plan runs.
Never place plaintext credentials in a manifest. secretEnv stores a typed
reference, never a value; a confirmed lifecycle execution resolves it only
through the exact workload grant and configured provider, and state, events,
diagnostics, plans, and recovery records redact both the resolved value and
the reference itself.
