Recipe format reference

This is the normative schema for the two TOML files at the heart of a pekit recipe: pekit.toml (the recipe proper) and package.pekit.toml (what to package). It enumerates every section, every key, its type, whether it is required, and its meaning. Behaviour that a key merely triggers is cross-linked to the concept pages; this page is the field-by-field contract.

Two strictness rules hold throughout and are called out per-section below:

  • Unknown keys are hard errors. Every table the loader owns has a fixed set of accepted keys. Any other key — including a camelCase spelling of a known snake_case key, such as outDir — is rejected with an unknown_key diagnostic. There is no lenient/forward-compatible mode.
  • Types are checked on decode. A key declared here as a string must be a string, a table must be a table, and so on; a mismatch is an invalid_type error.

pekit.toml #

Worked example #

out_dir = "dist"

[env]
CFLAGS = "-O2"
PREFIX = "/usr"

[wrap]
command = ["pesb", "run", "--", "{{command}}"]

[source.git]
url = "https://github.com/example/tool.git"
ref = "v{{version}}"
versions = ">=1.0"
tag_regex = "^v(?P<version>[0-9.]+)$"

[build]
command = "make PREFIX=$PREFIX"

[build.docs]
command = ["make", "docs"]
needs = ["main"]

[build.linked]
command = "make link"
dependencies.pesb-dev.libc = ">=2.38"

[test]
command = "make check"

[install]
command = "make install DESTDIR=$PEKIT_OUT"

[clean]
command = "make clean"

Top-level keys #

The recipe file accepts exactly these top-level keys. Anything else is an unknown recipe key error.

KeyTypeRequiredMeaning
out_dirstringnoManaged output directory strictly below the recipe root. Relative paths resolve from the recipe root; an absolute path is accepted only when it remains below that root. Default "out".
envtablenoEnvironment variables exported to every target command. See [env].
wraptablenoCommand wrapper applied to every target. See [wrap].
sourcetablenoWhere the recipe's source tree comes from. See [source].
inputtablenoAdditional authenticated upstream inputs, keyed by name. See [input.<name>].
delegatebool or tablenoBorrow build/env/wrap/package definitions from the source tree. See [delegate].
source_packagetablenoCorresponding-source package emission control. See [source_package].
tagsarray of stringsnoLabels for selecting this recipe as a workspace member with --tag and --exclude-tag. Each tag is a selector-style name; duplicates are rejected. Only the member recipe's own tags count: a delegated source's tags are ignored. See Workspaces.
buildtablenoBuild target(s). See [build] / [test] / [install] / [clean].
testtablenoTest target(s).
installtablenoInstall target(s).
cleantablenoClean target(s).
gentablenoSource-generating target(s). See [gen].

[env] #

A flat table of environment-variable name → value. Every entry is exported into each target command's environment (layered on top of the PEKIT_* contract; see Anatomy of a recipe).

KeyTypeRequiredMeaning
<NAME>string—Value for variable NAME.

Each NAME must match ^[A-Za-z_][A-Za-z0-9_]*$; anything else is an invalid_env error. Values must be strings. Declaration order in the file is preserved, so a later entry may reference an earlier one via shell expansion in the target command.

[wrap] #

Wraps every target command in another command (for example, running it inside a sandbox launcher).

KeyTypeRequiredMeaning
commandstring or array of stringsnoThe wrapper. Must contain the placeholder {{command}} exactly once.

No other key is permitted under [wrap]. The {{command}} rules mirror a target command:

  • String form: the whole string is run through a shell; it must contain the substring {{command}} exactly once.
  • Array form: {{command}} must appear as a complete argument (not a substring of a larger argument) exactly once, and never as the first element (element 0 is the wrapper program itself).

An omitted command yields an empty wrap (no wrapping).

[source] #

Selects and configures the source tree. It has four sub-tables. At most one reproducible source (git, url, or pypi) may be present — declaring more than one is a mixed_source error. A local source may be combined with a reproducible one (the local path acts as an override). Unknown keys directly under [source] are rejected.

Sub-tableMeaning
[source.git]Clone from a git repository.
[source.url]Download from a URL (optionally an archive).
[source.pypi]Discover and download a standardized PyPI source distribution.
[source.local]Use a directory on disk.

One direct key is accepted alongside the sub-tables:

KeyTypeRequiredMeaning
patchesstringnoName of a recipe-root directory (a single path segment) whose series file pekit applies to the materialised source tree before any target runs. Requires a reproducible source (patches_source otherwise). See Patches.

See Sources for materialisation, caching, and provenance behaviour. Resolved git, url, and PyPI sources are pinned trust-on-first-use in the recipe's machine-written pekit.lock.

[source.git] #

KeyTypeRequiredMeaning
urlstringyesGit remote URL to clone.
refstringnoRef (tag/branch/commit) to check out. Templated. Default "{{version}}".
versionsstringnoVersion cap: a constraint string filtering enumerated or requested versions (see Versions).
tag_regexstringnoRegex filtering tags during enumeration. A named version capture supplies the complete version; named major, minor, and patch captures compose a dotted version. Optional revision (fourth numeric component), suffix (unseparated), prerelease, and buildmeta captures may extend it. Unnamed captures have no extraction semantics.
tracked_pathstringnoTrack one repository-relative regular file on a fixed moving ref. Versions are synthesized from changed blob observations and matching lock history. Requires a non-templated ref; incompatible with tag_regex. See Tracking one file on a moving ref.
signaturetablenoUpstream release-signature verification. See [source.git.signature] below. Incompatible with tracked_path.

[source.git.signature] #

Optional sub-table of [source.git]. If present, every resolve requires a valid OpenPGP signature on the release. The signature is checked after the mirror fetch and before the lock is written or the tree is checked out. An unsigned or lightweight tag, or an unsigned commit, is signature_missing. An SSH or X.509 signature is signature_unsupported. A signature that fails, or a tag object that names another tag or points somewhere other than the resolved commit, is signature_invalid. Each of these aborts the run and writes no lock entry. The verified signer's fingerprint is recorded as the lock entry's signature_key.

KeyTypeRequiredMeaning
objectstringnoWhat carries the signature: "tag" (the rendered ref is an annotated, signed tag naming the resolved commit; default) or "commit" (the resolved commit has a gpgsig header). Any other value is invalid_signature.
key_filesstring arrayyesPinned public keys (armored or binary, one or more per file), relative to the recipe directory. An empty or absent list is missing_key.
fingerprintsstring arraynoAllowlist of signer primary-key fingerprints, as for [source.url.signature]. A valid signature by any other pinned key is signature_untrusted_key.
ignore_expiryboolnoPermit a signature made after the pinned signing key expired, as for [source.url.signature]. Default false.

[source.url] #

KeyTypeRequiredMeaning
urlstringyesURL to download. Templated with the version.
listing_urlstringnoExplicit URL to fetch when enumerating versions. Use when releases are listed separately from their download paths; default is derived from url.
extractboolnoTreat the download as an archive and extract it. Default false.
rootstringnoSub-directory within the extracted tree to use as the source root. Default ".".
excludestring arraynoExact files or directory subtrees, relative to root, that extraction never creates. Canonical relative paths only: no .., trailing slash, backslash, glob or template syntax. A nonempty list requires extract = true. See Archive extraction.
versionsstringnoVersion cap: a constraint string filtering enumerated or requested versions (see Versions).
file_regexstringnoRegex extracting versions when enumerating from a listing. Named version, major, minor, patch, revision, suffix, prerelease, and buildmeta captures map filenames with the same semantics as Git tag_regex; unnamed captures only filter.
checksumstring or tablenoExpected checksum. A bare string applies to all versions; a table maps version → checksum.
signaturetablenoUpstream signature verification — see [source.url.signature] below.
patch_seriestablenoIncremental upstream patch series layered over each major.minor base archive — see [source.url.patch_series] below.

When patch_series is present, the selected version must be stable major.minor.patch. The primary url, root, checksum-table lookup, and signature render against major.minor; the complete selected version remains the package version.

[source.pypi] #

KeyTypeRequiredMeaning
projectstringyesPyPI project name. Must start and end with an ASCII letter or digit and otherwise contain only letters, digits, ., _, or -.
artifactstringyesDistribution kind. Currently must be exactly "sdist".
versionsstringnoVersion cap: a constraint string filtering enumerated or requested versions (see Versions).

Pekit requests https://pypi.org/simple/<normalized-project>/ as application/vnd.pypi.simple.v1+json. It accepts one non-yanked, stable, Pekit-compatible, PEP 625 .tar.gz sdist per version, requires that entry's SHA-256, extracts its standardized root, and records the exact URL and digest in pekit.lock. Wheels and legacy sdist formats are not candidates. There is no configurable index URL in this source kind.

[source.url.signature] #

Optional sub-table of [source.url]. Its presence makes upstream signature verification required: the detached signature is fetched and verified against the pinned keys before a version is locked or built. A missing signature is signature_missing; a failed verification is signature_invalid; either aborts the run and writes no lock entry. Verification is in-process OpenPGP — no host gpg is involved.

Key expiry is evaluated at the signature's signed creation time, so a later key expiry does not invalidate an older release. Current key revocations and explicit signature expiry are still enforced, and a signature dated after key expiry or materially in the future is rejected by default.

KeyTypeRequiredMeaning
key_filesstring arrayyesCommitted public key files (armoured or binary OpenPGP), resolved relative to the recipe root. Must be non-empty.
urlstringno (yes with of = "checksums")Signature URL template — the checksum manifest's location with of = "checksums". {{source_url}} expands to the rendered artifact URL; version variables are also available. Default "{{source_url}}.sig".
ofstringnoWhat the signature covers: "artifact" (the published file, default), "decompressed" (its decompressed content — kernel.org's .tar.sign signs the uncompressed tar), or "checksums" (a clear-signed checksum manifest at url that must list the artifact exactly once; see Signed checksum manifests). Any other value is invalid_signature.
fingerprintsstring arrayno (yes with of = "checksums", full fingerprints only)Allowlist of signer fingerprints (hex; spaces and 0x ignored, case-insensitive). When set, a valid signature by any other pinned key is signature_untrusted_key.
ignore_expiryboolnoPermit a new signature made after the pinned signing key expired. Default false. This bypasses key expiry only; cryptographic verification, fingerprint allowlisting, present-time revocation, explicit signature expiry, and timestamp checks remain enforced.

[source.url.patch_series] #

Optional sub-table of [source.url] for upstreams that publish a base archive and numbered incremental patches. Version major.minor.N materialises the base plus patches 1 through N. Enumeration always exposes patchlevel zero and every contiguous published patchlevel; a gap is url_patch_gap.

KeyTypeRequiredMeaning
urlstringyesPatch artifact URL template. Must contain {{patch}}; {{major}}, {{minor}}, and other version variables are available.
file_regexstringnoRegex used against the patch-directory listing. Must contain a named patch capture. When omitted, Pekit derives the matcher from url.
patch_widthintegernoZero-pad {{patch}} to this width. Default 0 (no padding).
stripintegernoNon-negative path-component count passed to patch -p. Default 0.
signaturetablenoPer-patch detached-signature verification. Uses the [source.url.signature] schema and defaults its URL to {{source_url}}.sig.

Patches are applied in order using the host patch command with batch mode, forward-only application, and fuzz disabled. All patch hashes and configured signatures are verified before Pekit writes the version's lock entry.

[source.local] #

KeyTypeRequiredMeaning
pathstringyesPath to the source directory, resolved relative to the recipe root.

[input.<name>] #

An additional authenticated upstream, for a build that genuinely consumes more than one. A recipe may declare any number, each under its own name. Names are lower-case alphanumeric words separated by hyphens (invalid_name otherwise), and must stay distinct after upper-casing and mapping - to _, because that is the $PEKIT_INPUT_<NAME> a target reads.

Accepts the same keys as [source.url], including a nested signature table, with two differences:

DifferenceMeaning
versionsMust pin exactly one version — "= 7.0.9" or a bare "7.0.9". A range is invalid_versions: inputs are never selected by --latest or --all-versions.
patch_seriesNot accepted (unknown_key). A patch series applies to the source tree.

{{version}} inside an input block renders that input's own version, never the recipe's. Each input is fetched, signature-checked and pinned in pekit.lock as an [[input]] entry, materialised at $PEKIT_INPUT_<NAME>, bind-mounted read-only under isolation, and carried in the source package under upstream/<name>/.

When a recipe delegates build, the fetched source tree's own [input.<name>] tables are part of the build too. The delegating recipe resolves and pins them in its own pekit.lock, and their key_files resolve beside the source's pekit.toml. A recipe input with the same name replaces the source's as a whole. See Additional inputs.

[delegate] #

Lets a recipe borrow definitions that live inside the fetched source tree rather than in the recipe directory. May be written as a bare boolean (delegate = true, equivalent to all = true) or as a table.

KeyTypeRequiredMeaning
allboolnoDelegate everything (build, env, wrap, packages).
buildboolnoDelegate build targets, together with the [input.<name>] tables the source declares for them.
envboolnoDelegate [env].
wrapboolnoDelegate [wrap].
packagesboolnoDelegate package definitions.

Any other key is an unknown delegate key error. A category is delegated when either its own flag or all is set.

[source_package] #

Controls the corresponding-source package a recipe emits alongside its binary packages. Emission is automatic; the table controls naming, emission and additional shared source inputs. A recipe emits one when both of these hold: it has a reproducible [source] (git, url or PyPI; local overrides and bare branch refs never qualify), and it produces at least one peipkg-format package.

KeyTypeRequiredMeaning
namestringnoPackage name. Default <recipe-dir>-source.
enabledboolnoSet false to emit no source package. Default true.
workspace_inputsarray of relative pathsnoAdditional shared workspace inputs for this recipe. Under workspace isolation they are copied into read-only worker mounts and included byte-for-byte in the source bundle. Missing, ignored or escaping inputs fail.
licensestringnoSPDX expression covering material that only the source package carries, such as upstream test programs or build helpers that no member ships. It is added to the members' licenses, never substituted for them. An expression that isn't valid SPDX is invalid_value.

Any other key is an unknown source_package key error.

The emitted package is noarch, versioned identically to the recipe's package members (members that disagree on version are a source_package_version_conflict error), licensed as the conjunction of the members' licenses and license (see below; and classed as the most encumbered member's class — proprietary over firmware over unknown over free, with an undeclared member counting as unknown), and installs under /usr/src/dist/<name>-<version>/ (with any -source suffix stripped from <name>).

The conjunction is built term by term. Each expression is split into its top-level AND terms. A term containing OR keeps its parentheses, so the surrounding ANDs can't rebind it. Repeated terms appear once, and the terms are sorted. Members licensed GPL-2.0-or-later OR LGPL-3.0-or-later, GPL-3.0-or-later and GPL-3.0-or-later AND LGPL-2.1-or-later produce (GPL-2.0-or-later OR LGPL-3.0-or-later) AND GPL-3.0-or-later AND LGPL-2.1-or-later.

The package's content:

  • upstream/ — the pristine source input: a url source's downloaded artifact byte-for-byte, so its hash matches the committed pekit.lock, or a git archive export of the locked commit.
  • source/ — the prepared tree, with patches already applied. Large trees travel in prepared-source.tar instead; rebuild.py verifies and reconstructs source/ before invoking Pekit.
  • workspace/ — the complete captured recipe, shared inputs, inherited policy and environment profiles, preserving their relative paths.
  • recipe/ and patches/ — compatibility views.
  • acquisition/ and build-environment/ — captured vendoring outputs and dependency identities when provided by the isolated job.
  • build-inputs.json, rebuild.py, REBUILD.md — schema-2 or schema-3 hashes, modes, provenance and the outside-checkout reconstruction command.

Output/cache state, ignored developer files, keyring/credential files and configured key paths are excluded. Missing or escaping source links fail closed. See source reconstruction for required dependency services, signing keys and the bounds of reconstruction.

Every peipkg-format member the recipe emits carries the source package's name in its manifest's build.source_package field, linking each binary to its corresponding source.

[build] / [test] / [install] / [clean] #

These four sections define targets — the commands pekit runs. Each section takes one of two shapes, distinguished by whether a command key is present directly in the section:

  • Bare target: command (and the other target keys) sit directly under the section, e.g. [build] with command = "make". This defines a single target named main.
  • Named targets: the section contains sub-tables, e.g. [build.docs] and [build.linked], each a target. Each name must be a valid selector.

Mixing the two — a bare command alongside a named sub-table in the same section — is a mixed_targets error. (A sub-table whose key is itself a target key, such as [build.dependencies], is read as part of the bare target, not as a named target.)

Fields of a single target:

KeyTypeRequiredMeaning
commandstring or array of stringsyesThe command. String form runs through a shell; array form is an argv executed directly. An empty array is rejected.
needsarray of stringsnoNames of other targets in the same section that must run first.
clear_outboolnoWipe this target's output directory before running. Default true.
keyring_inputsstring arraynoDotted keyring entries requested by this target. Default none. The operator keyring must grant public access, or acquisition for build.vendor. Signing entries cannot be exported.
dependenciestablenoBuild and test targets. Dependencies the target needs provisioned in its root. Under a peipkg provider the composed root holds only what is declared — a test stage that shells out needs dash (pekit runs command through /usr/bin/sh) and whatever else its script calls. See below.
gateboolnoTest targets only. When true, package and publish run this test after staging its needs and before writing any artifact. Default false; --no-gates explicitly bypasses gated tests and the lint gate.
signtablenoBuild targets only. Files in the target's output to sign after the command succeeds, by signature kind. See sign below.

Any other key in a target table is an unknown target key error. sign is accepted only in [build] targets, gate only in [test], and dependencies in [build] and [test]; using them elsewhere is an unknown key.

The dependencies table is keyed by provider (a selector), each mapping dependency names to version constraints:

[build.linked.dependencies]
pesb-dev.libc = ">=2.38"
pesb-dev."pkgconfig(zlib)" = "*"
LevelTypeMeaning
providertableA dependency-provider selector.
provider.<name>stringVersion constraint for the capability name. Must be non-empty; use "*" for any version.

Dependency names may be real package names or virtual capabilities (sonames, pkgconfig(...), etc.) and are validated against peipkg's capability grammar. See Commands and targets.

[build.<name>.sign] #

The sign table is keyed by signature kind. Each kind is a table mapping a path or glob, relative to the target's $PEKIT_OUT, to the dotted path of the keyring entry holding the signing key:

[build.main.sign.pip]
"bin/peinit"  = "tcb.priv"
"lib/*.so.*"  = "tcb.priv"

The kind decides the signature format and where it is stored; the keyring entry is resolved through the ordinary keyring mechanism when the target runs. Signing happens after the target's command exits 0 and before any dependent target or package sees the output.

LevelTypeMeaning
kindtableA signature kind. pip produces Peios binary/firmware signatures; module signs .ko and .ko.zst with a kernel PKCS#7 trailer. Other names are unknown_key.
kind.<pattern>stringA relative path or glob (the same glob syntax as [files]). The value is a keyring leaf path such as tcb.priv whose value is the path to the private key. Must be non-empty.

For pip, the key is an ML-DSA-65 private key — PKCS#8 PEM as produced by openssl genpkey -algorithm ML-DSA-65, or a raw 32-byte seed — and every matched file must be a 64-bit little-endian ELF. A pattern that matches nothing (sign_target_missing), a keyring entry that is absent or does not load (signing_key), a file that cannot carry the signature (sign_failed), and two patterns naming different keys for one file (sign_conflict) are all errors; pekit never leaves a listed file unsigned. The mechanism is described in Signing and provenance.

[gen] #

A [gen] section defines source-generating targets: they run at the recipe root and write generated source into the tree rather than producing an out_dir artifact. Bare/named shapes work exactly as for the target sections above ([gen] is gen.main; [gen.<name>] names a target). Gen targets have their own fields — they take no needs or clear_out:

KeyTypeRequiredMeaning
commandstring or array of stringsyesThe generator. Runs at the recipe root; writes generated source in place.
verify_commandstring or array of stringsnoThe drift gate: exit 0 = in sync, non-zero = stale. Run by pekit verify and by the consuming-command pre-flight.
verify_on_buildarray of stringsnoBuild target names this gate applies to. Absent gates every build; [] gates none; a list gates only those.
verify_on_testarray of stringsnoAs verify_on_build, for the test namespace.
dependenciestablenoBuild-time dependencies for command (and for verify_command, unless overridden). Same shape as a build target's dependencies.
verify_dependenciestablenoWhen present, fully replaces dependencies for the verify_command run.

Any other key is an unknown target key error. Setting verify_on_build, verify_on_test, or verify_dependencies without a verify_command is an invalid_gen error (there is nothing to gate).

A gen target's $PEKIT_OUT is a pekit-managed scratch directory (<out_dir>/.scratch/gen/<name>) outside the artifact area, cleaned on success and retained on failure — a convenient place for verify_command to regenerate into for comparison. For the command surface, the pre-flight, and --no-verify, see Commands and targets.

package.pekit.toml #

The package file describes one or more package artifacts to emit from the built source. The same schema applies to the base file (package.pekit.toml) and to per-member member files (<name>.package.pekit.toml); see Multi-package recipes and Supporting files for how the files are discovered and layered.

Worked example #

format = "peipkg"
builds = ["main"]

[package]
name = "tool"
version = "{{version}}"
architecture = "x86-64-v3"
description = "An example tool"
license = "MIT"
homepage = "https://example.com/tool"
default_root = "opt.tool"

[dependencies]
libc = ">=2.38"
zlib = { constraint = ">=1.3", root = "opt.tool" }

[optional_dependencies]
libcurl = ">=8.0"

[provides]
tool = "{{version}}"

[files]
"main:bin/tool" = "usr/bin/tool"
"@recipe:share/" = { path = "usr/share/tool/", override = true }

[symlinks]
"usr/bin/t" = "tool"

excludes = ["@recipe:share/*.tmp"]

[claims.provides.editor.default]
path = "usr/bin/editor"
target = "usr/bin/tool"

[[publish.localdir]]
path = "dist"
overwrite = false

Top-level keys #

The package file accepts exactly these top-level keys; anything else is an unknown package key error.

KeyTypeRequiredMeaning
formatstringnoArtifact format: "tar" or "peipkg". Default "tar".
dependency_providerstringnoThe dependency namespace this package's runtime [dependencies] belong to, matching an env file's dependency_provider. Workspace ordering follows a package's runtime dependencies only when this equals the selected env's provider. Inherited through package layers like format, so a workspace sets it once in its shared package.pekit.toml. Undeclared means the runtime dependencies do not affect ordering.
clear_outboolnoWipe the package stage directory before building. Default true.
buildsarray of stringsnoNames of build targets this package requires before staging.
packagetablenoPackage metadata. See [package].
filestablenoPayload file mappings. See [files].
symlinkstablenoSymlinks to embed in the payload. See [symlinks].
excludesarray of stringsnoRefs/globs to drop from directory payloads. See excludes.
multipacktablenoExpand into a family of instances. See [multipack].
publishtablenoWhere to copy finished artifacts. See [publish].
dependenciestablenoRuntime dependencies. See [dependencies].
optional_dependenciestablenoOptional runtime dependencies (same schema as dependencies).
conflictstablenoname → constraint of packages this one conflicts with.
providestablenoname → version of capabilities this package provides.
replacestablenoname → constraint of packages this one replaces. "*" means every version, as it does in dependencies; the manifest records such an entry with no constraint.
side_effectsarray of stringsnoDeclared install-time side effects.
sd_overridestablenopayload path → SDDL security descriptor. See [sd_overrides].
claimstablenoClaim slots on provides/dependencies. See [claims].

Most string-valued fields (metadata, dependency names/constraints, file refs and destinations, claim paths/targets) are run through the template engine, where {{version}} and {{multipack}} are available. After [package].version is rendered, {{release}} is also available: the package's own full version, including its revision (1.13.2-3 for version = "{{version}}-3"). Pin packages built from the same source to each other with "= {{release}}", so the pins follow the revision automatically; the package.dependencies lint rule reports relations that spell out "{{version}}-N" by hand. See Versions and Multi-package recipes.

[package] #

Package metadata. Accepts exactly these keys; any other is an unknown package metadata key error. All values are strings.

KeyTypeRequiredMeaning
namestringnoPackage name. Defaults to the package selector (suffixed with the multipack value for multipack instances) when omitted.
versionstringnoPackage version. Required for peipkg, where the rendered value must be a peipkg version — [epoch:]upstream-revision (PSPU book 5 §5.5), so "{{version}}-1" and never a bare "{{version}}". A value that does not parse is an invalid_package_version error when the package is expanded, before anything is built.
architecturestringnoTarget architecture. Required for peipkg.
descriptionstringnoHuman-readable description.
licensestringnoLicense identifier. Required for peipkg.
license_classstringnoLicence class: unknown, free, firmware or proprietary (PSPU book 5 §18). Any other value is an invalid_license_class error. Absent means unknown; set it whenever license carries a LicenseRef- term, which is exactly when the expression alone cannot say.
homepagestringnoProject homepage URL.
default_rootstringnoPreferred named root for a top-level install. Must be a dotted named reference matching [a-z0-9][a-z0-9_-]* per segment, never a filesystem path (a / is rejected).
special_system_packagebooleannoDeclares the package exempt from the payload layout rules, for the rare package whose job is to lay down the structure those rules protect — the base-filesystem package that mints the mountpoint tree is the archetype. Setting it disables pekit's layout validation for this package. It grants nothing at install time: whoever installs or composes the result must also pass --dangerously-bypass-path-restrictions, so a recipe can propose its own exemption but never grant one. Defaults to false.

Note that dependencies, provides, conflicts, and the rest are top-level tables, not sub-keys of [package].

[dependencies] #

Runtime dependency constraints. [optional_dependencies] has the identical schema. Each entry is either a bare constraint string (the short form) or a table that additionally places the dependency in a named root.

FormTypeMeaning
<name> = "constraint"stringVersion constraint; the dependency lands in the depender's own root.
<name> = { constraint, root }tableTable form (below).

Table form keys — only these two are accepted (unknown_key otherwise):

KeyTypeRequiredMeaning
constraintstringnoVersion constraint. Omitting it matches any version.
rootstringnoNamed root to place the dependency in. Same grammar as default_root.

Dependency names may be real or virtual capabilities. See Dependencies and claims.

[files] #

Maps a source ref (the key) to a destination path within the package payload (the value). The value is either a string destination or a table.

Value formMeaning
"ref" = "dest/path"Copy the ref to dest/path.
"ref" = { path, override }Table form (below).

Table form keys — only these are accepted:

KeyTypeRequiredMeaning
pathstringyesDestination path in the payload. Must be non-empty.
overrideboolnoMark the payload as an override entry (skips manifest payload validation for peipkg). Default false.

The source ref selects where the file is read from. A bare path with no prefix is resolved against the recipe root (or the layer's owner). Explicit forms: @recipe:<path>, @source:<path>, @workspace:<path>, and <build-target>:<path> (reading a build target's output; :<path> alone uses the main build target). Destinations ending in / and glob patterns are supported. See Packages for ref resolution and glob semantics.

Maps a destination path within the payload (the key) to a symlink target (the value).

Value formMeaning
"usr/bin/x" = "target"Create a symlink at usr/bin/x pointing at target.
"usr/bin/x" = { target, override }Table form (below).

Table form keys — only these are accepted:

KeyTypeRequiredMeaning
targetstringyesSymlink target text. Must be non-empty.
overrideboolnoMark as an override entry. Default false.

excludes #

An array of refs/globs. During directory payload expansion, files matching an exclude pattern (resolved against the same root as the file mapping being expanded) are dropped from the payload. Patterns use the same ref forms as [files] keys.

[multipack] #

Expands one definition into a family of package instances, one per enumerated value. The only accepted key is enum, which takes one of two shapes:

Explicit list:

[multipack]
enum = ["gtk3", "gtk4"]

Each value must be a valid selector, non-empty, and unique.

Enumerated from files:

[multipack.enum.files]
path = "@source:themes/*/theme.ini"
regex = "^(?P<value>.+)$"

[multipack.enum.files] accepts exactly path and regex, both required:

KeyTypeRequiredMeaning
pathstringyesA ref glob (templated) whose matches are enumerated.
regexstringyesRegex applied to each match's basename. Must have exactly one capture group, or a single named value group, which supplies the instance value.

See Multi-package recipes.

[publish] #

Where finished artifacts are published. localdir copies an artifact by basename and is written as an array of tables:

[[publish.localdir]]
path = "dist"
overwrite = false

Each entry accepts only path and overwrite:

KeyTypeRequiredMeaning
pathstringyesDestination directory, relative to the workspace root (or recipe root outside a workspace). Templated. The artifact keeps its own filename.
overwriteboolnoAllow replacing an existing file at the destination. Default true.

peipkg publishes peipkg-format artifacts into a complete signed repository and is written as one table:

[publish.peipkg]
path = "dist/repository"
name = "experimental"
signing_key = "keyring:signing.repository_key"
KeyTypeRequiredMeaning
pathstringyesRepository state directory, relative to the workspace root (or recipe root outside a workspace).
namestringnoName written into a newly initialized repository's signed descriptor. Defaults to the final component of path; it does not alter an existing repository.
signing_keystringyesRepository metadata signing key. A normal value is a key-file path; keyring:<dotted.entry> reads the path from that keyring leaf.

If the directory named by path is absent or empty, it is initialized before the first package batch is published. A non-empty directory that is not already publisher state is refused rather than overwritten. Only peipkg-format artifacts may use this target. See Publish targets for key resolution, batching, and repository semantics.

[claims] #

Attaches claim slots to provides and dependency entries (a claim is a symlink slot a provider fills or a consumer expects). The [claims] table has two sides — provides and dependencies — each keyed by the provides or dependency name, then by slot, then to a descriptor. Any side other than provides/dependencies is an unknown_key error.

[claims.provides.<name>.<slot>]
path = "usr/bin/editor"
target = "usr/bin/tool"

[claims.dependencies.<name>.<slot>]
path = "usr/bin/editor"

A slot descriptor accepts exactly these two keys (anything else is rejected); both are strings and both are templated:

KeyTypeRequiredMeaning
pathstringnoThe symlink location (set on a consumer, or as a provider default).
targetstringnoThe holder file within this package (set on a provider). A provider's target must point at a file the package ships.

Provider-side slots (claims.provides) attach to matching [provides] entries by name; consumer-side slots (claims.dependencies) attach to matching [dependencies]/[optional_dependencies] entries. See Dependencies and claims and the Peios claims model for the resolution behaviour.

See also #

[sd_overrides] #

Each key is a payload path — exactly as it appears in the package, with no leading slash — and each value is an SDDL string. pekit compiles it to a binary security descriptor at pack time and puts the result in the manifest.

[sd_overrides]
"home" = "O:SYG:SYD:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;;FX;;;WD)"

An entry with no override gets its descriptor by inheritance from its parent directory when the consumer creates it, and that is what almost every entry should get. Declare one where a path needs to be more restrictive than its parent would make it, needs access for a principal the parent's inheritable ACEs do not name, or needs to start a new inheritance scope of its own.

Three constraints are worth knowing before writing one:

  • The path must name a real payload entry, and it must be a regular file or a directory — never a symlink, which carries no descriptor of its own. A consumer rejects the whole package otherwise.
  • Absolute SID aliases only. The compile happens on a build host with no domain and no machine to resolve against, so SY, BA, CO, WD and literal SIDs are fine while domain- and machine-relative aliases (DA, EA, LA and the rest) are an error.
  • A rights field may end in a hex literal. FRFX, 0x1200af and FRFX0x6 all parse, the last of them as file read, file execute and the two bits after that. Use it for rights with no two-letter mnemonic — FILE_ADD_FILE and FILE_ADD_SUBDIRECTORY, in particular — rather than writing the whole mask as hex and losing what the mnemonics said. The literal consumes the rest of the field, so it can only come last.

Edit this page