app.yaml reference
app.yaml tells Space CLI how to build one application from source when that
application is not a container. The build produces an OCI artifact and an
artifact manifest. A space.yaml project can then
refer to a named output from that manifest.
Build an OCI container from its Dockerfile with space build Dockerfile or
space build -f Dockerfile --project-root DIR. A Dockerfile is the app manifest
for that path and needs no app.yaml wrapper.
The space.app decoder refuses an undeclared member by name. A row carrying a
WIP badge names a member the decoder accepts and the rest of the product does
not act on yet.
Examples
Host executable
schema: space.appversion: "1"name: hellosource: .kind: hosttargets: - platform: host arch: arm64policy: network: none reproducible: trueoutputs: arm64: target: host-arm64 path: hello.sh kind: host-serviceUnikraft
schema: space.appversion: "1"name: unikraft-hellosource: .kind: unikraftkraftkit_image: kraftkit.sh/base@sha256:<digest>
targets: - platform: qemu arch: arm64
policy: network: replay reproducible: true
outputs: flight: target: qemu-arm64 path: .unikraft/build/app_qemu kind: unikernelMirageOS
schema: space.appversion: "1"name: mirage-hellosource: .kind: mirage
mirage_version: "4.9.0"ocaml_version: "5.4.1"opam_repo: https://github.com/ocaml/opam-repository.gitopam_repo_rev: "<full-git-commit>"
targets: - platform: hvt arch: arm64 image: ocaml/opam:debian-ocaml-5.4
policy: network: replay reproducible: true
outputs: flight: target: hvt-arm64 path: dist/http-fetch.hvt kind: unikernelCommon fields
| Field | Type | Required | Description |
|---|---|---|---|
schema | string | yes | Must be space.app. |
version | string | yes | Schema version. The current version is "1". |
name | string | yes | Application name written to the artifact manifest. |
source | path | yes | Source tree. Relative paths start at the manifest directory. |
kind | host, unikraft, or mirage | yes | Build path used to prepare the source. A container app builds from its Dockerfile and needs no app.yaml. |
targets[] | list | yes | Platform and CPU variants to build. |
policy | map | yes | Network and reproducibility settings for the build. |
cache[] | list | no | Named, content-addressed inputs available to an offline build. |
outputs | map | no (default: none) | Named outputs published in the artifact manifest. A build that declares none publishes none. |
output_budget_mb | integer | no (default: 8192) | Size in MiB of each writable output disk the builder VM writes this build's outputs to. Raise it when the outputs exceed 8 GiB. Must be positive. A build whose disks the output volume cannot hold is refused before the builder VM starts. |
requires | map | no | WIP Portable accelerator requirements. The accelerator requirements section lists their fields. |
Accelerator requirements
WIP requires.accelerators holds one requirement for
each logical role the workload uses, keyed by the role's name, such as
inference. The decoder takes these fields, and the shared matcher checks them
against a target's inventory. A workload cannot drive the device yet.
| Field | Type | Required | Description |
|---|---|---|---|
requires.accelerators | map | no (default: none) | WIP One requirement for each role. |
requires.accelerators.<name>.kind | gpu or fpga | yes | Accelerator kind, matched exactly. |
requires.accelerators.<name>.class | string | no (default: any class of the kind) | Namespaced class, matched exactly. |
requires.accelerators.<name>.count | integer | no (default: 1) | Distinct allocation units the role needs. A count below 1 matches no target. |
requires.accelerators.<name>.api | string | yes | Namespaced API name, matched exactly and case-sensitively. |
requires.accelerators.<name>.api_version | version | yes | One to three dotted non-negative integers, such as 12.4. A unit matches when its version has the same major number and is at least this one, so 12 accepts 12.4 and refuses 11.8 and 13.0. |
requires.accelerators.<name>.features | list of strings | no (default: none) | Namespaced features, each of which a unit must offer. |
requires.accelerators.<name>.memory_mib | integer | no | Usable memory in MiB each allocation unit must have, never summed across units. |
requires.accelerators.<name>.assignment | shared, exclusive, or shared-or-exclusive | no (default: shared-or-exclusive) | How a unit is assigned to the run. shared needs an isolation mechanism the target profile authorizes; exclusive gives the run sole use of the unit for the grant's lifetime. |
Target fields
| Field | Type | Required | Description |
|---|---|---|---|
targets[].platform | string | yes | The platform the backend configures for, such as hvt for a MirageOS target or qemu for an Unikraft one. It reaches the backend's own configure step as text, so it must be a plain identifier. |
targets[].arch | string | yes | CPU architecture: arm64 or amd64. Any other name is refused. |
targets[].options | list of strings | no | WIP Extra options for this target's backend. No backend takes them yet: a MirageOS target that sets any is refused, and the Unikraft and host backends ignore them. |
targets[].image | OCI reference | conditional (yes for kind: mirage) | Pinned toolchain image the build runs in for this variant. A MirageOS target without one is refused. |
outputs.<name>.target must equal the target id formed from its platform and
architecture, joined by a hyphen, such as hvt-arm64. One app build produces the outputs for one
target; use a separate manifest or a separate run for another architecture.
Host applications
kind: host packages an existing executable without starting a builder VM.
The output path must name an executable file under source. Host execution
covers local development and CI, and it is off by default. A host-isolated
service runs uncontained, so the runner refuses to launch one unless it was
started with --allow-host. The refusal names the service. The
substrates reference lists that flag with the other
backends.
Unikraft fields
| Field | Type | Required | Description |
|---|---|---|---|
kraftkit_image | OCI reference | no (default: kraftkit.sh/base:latest) | KraftKit build image. Pin it by digest: the default is a mutable tag, and a build that pins nothing is not the same build twice. |
MirageOS fields
| Field | Type | Required | Description |
|---|---|---|---|
mirage_version | string | yes | MirageOS release used to configure the application. It becomes an opam version selector, so it takes opam's version grammar and nothing else. |
ocaml_version | string | yes | OCaml compiler version. |
opam_repo | URL | yes | Opam repository used to solve dependencies. |
opam_repo_rev | Git commit | yes | The full forty-character commit id that pins the opam repository. A branch, a tag, HEAD and an abbreviation are all refused: a revision resolved against whatever the remote currently says is not a pin. |
Build policy
| Field | Type | Required | Description |
|---|---|---|---|
policy.network | allow, none, or replay | yes | Network access for the prepare and build phases. The table below gives each value. |
policy.reproducible | boolean | yes | Ask for the reproducibility check described below. |
| Value | Network | Use |
|---|---|---|
allow | on | Resolve and build mutable inputs. |
none | off | Build from inputs already present. |
replay | off | Rebuild from cache inputs. Today the builder treats it exactly as none; the two spellings are kept apart so a manifest can say which of the two it meant. |
Set policy.reproducible to true to ask for the reproducibility check. The
check builds the recipe twice, into two separate stores, and exports the OCI
layout only when the two results are identical. The builder runs it on a
Dockerfile build today; a MirageOS, Unikraft, or host build records the setting
in its cache key and does not run the check yet.
Each cache[] entry names a builder cache. The build attaches that cache as a
disk, filled from this computer's copy of it, so an offline build finds the
inputs an earlier build fetched. A MirageOS build attaches the opam, dune,
apt, and oci caches without being asked. The build record lists every cache
input and base image that went into the result.
| Field | Type | Required | Description |
|---|---|---|---|
cache[].name | string | yes | Builder cache to attach, such as opam or dune. It must be a plain identifier, and no two entries may share one. |
cache[].seed | string | yes | WIP A backend-defined operation that fills the cache. No backend reads it yet. |
Output fields
| Field | Type | Required | Description |
|---|---|---|---|
outputs.<name>.kind | string | yes | Artifact kind, such as unikernel or host-service. |
outputs.<name>.target | string | yes | Target id, such as hvt-arm64 or host-arm64. |
outputs.<name>.path | path | yes | Built file to package. It must be relative and stay inside the source tree; a path that escapes it is refused. |
Run the build from the application directory:
$ chmod +x hello.sh$ space build app.yaml -t ghcr.io/parsimoni-labs/hello:devBuilding an app from app.yaml✓ ghcr.io/parsimoni-labs/hello:dev built in ...Run it here: space run ghcr.io/parsimoni-labs/hello:devThe host build writes the executable manifest and layer to the local OCI store. MirageOS and Unikraft builds place their declared files under the configured builder output directory.