# Getting started

---

# What is pekit

_pekit / Getting started_

> Pekit is the recipe-driven build and packaging tool for Peios. It reads declarative recipes, plans the work, then builds, tests, packages, signs, and publishes.

**Pekit** is the tool that turns source into packages. You describe a piece of software once — where its source comes from, how to build it, and how to split the result into `.peipkg` files — in a set of declarative **recipe** files, and pekit does the rest: fetches the source, selects versions, runs the build, stages the output, and writes packages you can install or publish.

Everything in the Peios tree is built with pekit. The kernel, the registry store, the init system, the package manager itself — each is a recipe, and `pekit package` produces the `.peipkg` files that [peipkg-compose](/peios/using-peios/package-management/composing-a-root.md) and [peiso](/peios/peiso/building-images/overview.md) assemble into a running system.

## The one idea: plan before execute

Pekit's guiding principle is **predictability**. Every command declares exactly which flags it accepts, every source declares what it can do, and every build is planned before any side effect happens. Before pekit clones a repository, runs a shell command, or writes a package, it has already resolved which recipe is selected, which source tree is needed, which versions apply, which build targets will run, and which artifacts will be produced.

That plan is inspectable. Add [`--dry-run`](/pekit/running/invocation.md) to any command and pekit builds the same plan it would execute, prints it, and stops — no clones, no downloads, no writes. Strictness is the other half of the same idea: unknown flags, unknown recipe keys, and flags a command does not support are **errors**, caught up front, not halfway through a build.

## The pipeline

A single invocation runs through a fixed sequence:

```text
argv
  → invocation        parse and validate flags against the command
  → recipe            locate and load the recipe (walking up from the cwd)
  → versions          resolve the version selector into concrete versions
  → source            materialise the source tree for each version
  → delegation        merge any behaviour borrowed from the source tree
  → execute           run targets, or stage builds and write packages
  → artifacts         staged output and .peipkg files under out_dir
```

`build`, `package`, and `publish` may run this for **several versions** at once; `test` and `install` require the selector to resolve to exactly one. `clean`, `gen`, and `verify` skip the version and source stages entirely — they operate on the recipe directly.

## The shape of a recipe

A recipe is a directory. At minimum it holds one file:

```text
mypkg/
├── pekit.toml                 # the recipe: source, build/test/install/clean targets, env
├── package.pekit.toml         # what to package: files, symlinks, dependencies, metadata
├── packages.pekit/            # (optional) extra package members for multi-package recipes
├── env.pekit.toml             # (optional) reusable environment / wrapper definitions
└── prod.keyring.pekit.toml    # (optional) named keyring for signing and secrets
```

`pekit.toml` is the recipe proper — it declares the [source](/pekit/recipes/sources.md), the [build/test/install/clean targets](/pekit/running/commands-and-targets.md), and shared [environment](/pekit/recipes/environments-and-keyrings.md). The [package files](/pekit/recipes/packages.md) describe how staged build output is mapped into one or more `.peipkg` payloads. A [`workspace.pekit.toml`](/pekit/running/workspaces.md) one level up turns a directory of recipes into a **workspace** that pekit can drive as a unit.

The [Anatomy of a recipe](/pekit/recipes/anatomy.md) page walks through every file; the [recipe format reference](/pekit/reference/recipe-format.md) is the exhaustive schema.

## The commands

Ten commands, each with a defined job:

| Command | What it does |
|---|---|
| `build` | Run build targets and their build dependencies. |
| `test` | Stage the builds a test needs, then run test targets. |
| `install` | Stage the builds an install needs, then run install targets. |
| `package` | Stage builds and write `.peipkg` package artifacts. |
| `publish` | Package, then publish the artifacts to a configured destination. |
| `clean` | Run a clean target and/or remove pekit's managed output directory. |
| `gen` | Run source-generating targets that write generated source into the tree. |
| `verify` | Run gen targets' drift checks without writing anything. |
| `lock` | Fetch and pin source inputs in `pekit.lock` without building. |
| `workspace` | Run any of the above across every member of a workspace. |

The [Commands and targets](/pekit/running/commands-and-targets.md) page covers what each command selects, its version behaviour, and how target selection works.

## Where pekit fits

Pekit is the **producer-side build tool**. It stops at the `.peipkg` file. What happens next is other tools' work:

- [**peipkg**](/peios/using-peios/package-management/overview.md) installs `.peipkg` files onto a live system.
- [**peipkg-compose**](/peios/using-peios/package-management/composing-a-root.md) assembles a set of packages into an offline package root.
- [**peiso**](/peios/peiso/building-images/overview.md) turns a composed root into a bootable image.

Pekit produces packages in the [peipkg format](/peios/using-peios/package-management/overview.md) and understands peipkg concepts directly: recipes declare package **dependencies**, automatic **ELF dependencies** are scanned from built binaries, and packages can declare the shared-name **claims** they participate in. See [Dependencies and claims](/pekit/recipes/dependencies-and-claims.md).

Pekit also carries the supply-chain half of producing packages: fetched sources are pinned trust-on-first-use in a lockfile, upstream release signatures can be verified against committed keys, artifacts are Ed25519-signed, binaries can be PIP-signed for the kernel, and every manifest records the provenance of its inputs, recipe, and builder. See [Signing and provenance](/pekit/running/signing-and-provenance.md).

> [!IMPORTANT]
> **Pekit supersedes the old `peipkg-build` recipe system.** Earlier Peios used a `peipkg.toml` + `build.sh` recipe built by a separate `peipkg-build` binary. Pekit replaces that entirely; a pekit recipe (`pekit.toml` + package files) is the current and only supported way to build a package. Documentation describing `peipkg-build` and its `build.sh` recipes is legacy.

## What to know before you start

A few facts about the tool as it ships today:

- **No `--help` yet.** Pekit does not currently print usage text. This page and the [invocation reference](/pekit/running/invocation.md) are the manual; the complete flag surface is tabulated in the [command-line reference](/pekit/reference/cli.md).
- **Exit status is 0 or 1.** Pekit exits `0` on success and `1` on any error. It does not use finer exit codes.
- **Events go to stdout.** Pekit's own events print as timestamped lines on stdout (structured JSON with [`--json`](/pekit/running/invocation.md)); a target's output streams through on its original stdout and stderr, and fatal errors go to stderr.

## Where to start

To learn the recipe format, begin with [Anatomy of a recipe](/pekit/recipes/anatomy.md) and follow the recipes section in order.

To use pekit against an existing recipe, read [Commands and targets](/pekit/running/commands-and-targets.md) and [Invocation and flags](/pekit/running/invocation.md).

For the exhaustive schema and CLI surface, the [reference section](/pekit/reference/recipe-format.md) has one page per file format plus the full command-line reference.

---

# Quick start

_pekit / Getting started_

> Build and package a tiny "hello" program with pekit from an empty directory — a minimal pekit.toml, one pekit build, and a .peipkg you can inspect.

This page takes you from an empty directory to a working `.peipkg` in about five minutes. You will write a two-file recipe, run `pekit build`, inspect the staged output, then run `pekit package` and look inside the artifact it writes. At the end you have a complete, minimal recipe you can grow into a real one.

## Prerequisites

- **A `pekit` binary on your `PATH`.** Pekit is a single binary built from its source repository with `go build ./cmd/pekit`; there is no separate installer. Note that pekit has [no `--help` or version banner](/pekit/running/invocation.md) — the docs are the manual.
- **A POSIX shell** (`sh`). Target commands run under `sh -euc`; any Linux system has this.
- **`zstd` and `tar`**, for the final inspection step only.

## Create the recipe

A recipe is a directory, and only one file in it is required: `pekit.toml`. Make a directory and write the recipe:

```console
$ mkdir hello && cd hello
```

```toml
# hello/pekit.toml
out_dir = "out"

[build]
command = '''
mkdir -p "$PEKIT_OUT/bin"
printf '#!/bin/sh\necho "Hello from Peios"\n' > "$PEKIT_OUT/bin/hello"
chmod +x "$PEKIT_OUT/bin/hello"
'''
```

Two things are going on here:

- **`out_dir`** names the directory pekit manages, relative to the recipe. Everything pekit stages or writes lands under it, and `pekit clean` may remove it wholesale — keep nothing precious there.
- **`[build]`** in this bare form defines a single build target named `main`. Its `command` is an ordinary shell script; pekit runs it with a set of `PEKIT_*` environment variables exported, of which the important one is `$PEKIT_OUT` — the target's own staging directory. **A build's job is to put its artifacts in `$PEKIT_OUT`.** Here the "build" just writes a shell script; a real recipe would run `make`, `cargo build`, or similar and copy the results in.

This recipe has no `[source]` section, so it builds against its own directory. Fetching real source trees is covered in [Sources](/pekit/recipes/sources.md).

## Run the build

From `hello/` (or any directory beneath it — pekit walks upward to find the nearest `pekit.toml`):

```console
$ pekit build
```

You should see three timestamped lines, ending in success:

```text
[14:30:01] [build] loaded recipe
[14:30:01] [main] running target
[14:30:01] [main] target succeeded
```

Nothing else prints because the build command itself is silent; anything your build writes to stdout or stderr is streamed through with a `[build:main]` prefix. Pekit exits `0` on success and `1` on any error.

## Inspect the staged output

Each build target stages into `out/build/<target>/`. Look at what the run produced:

```console
$ find out -type f
```

```text
out/.pekit/dependencies/build.main.json
out/build/main/bin/hello
```

`out/build/main/` is target `main`'s `$PEKIT_OUT`, and it now holds exactly what the command wrote (the `out/.pekit/` entry is pekit's own bookkeeping). This staged tree is what packaging draws from. The script even runs:

```console
$ out/build/main/bin/hello
Hello from Peios
```

## Describe the package

Packaging is declared in a second file beside the recipe. Write `hello/package.pekit.toml`:

```toml
# hello/package.pekit.toml
format = "peipkg"

[package]
name = "hello"
version = "0.1.0-1"
architecture = "noarch"
description = "A friendly greeting."
license = "MIT"

[files]
":bin/hello" = "usr/bin/hello"
```

The `peipkg` format requires `[package].version`, `[package].architecture`, and `[package].license`; the rest of the metadata is optional. The `[files]` table maps sources to destinations: the left side `":bin/hello"` is a build ref meaning "`bin/hello` from build target `main`'s staged output", and the right side is where the file lands inside the package. Destinations must sit under the peipkg layout's permitted top levels (`usr/bin/`, `usr/share/`, `etc/`, and so on) — see [Packaging files](/pekit/recipes/packages.md).

## Write the package

```console
$ pekit package
```

`package` stages the builds the package needs (re-running `build.main`), then writes the artifact:

```text
[14:31:12] [package] loaded recipe
[14:31:12] [main] running target
[14:31:12] [main] target succeeded
[14:31:12] [main] wrote package: /home/you/hello/out/package/main-e33807f0ae9abdc7/hello_0.1.0-1_noarch.peipkg
```

The artifact is named `<name>_<version>_<architecture>.peipkg` and lands in a per-package stage directory under `out_dir` (the hash in the directory name is derived from the package's identity). Add `--dry-run` to any command to see this plan without writing anything.

## Look inside

A `.peipkg` is a Zstandard-compressed tar archive: reserved `.peipkg/` metadata entries followed by the payload as it will be installed.

```console
$ zstd -d --stdout out/package/main-*/hello_0.1.0-1_noarch.peipkg | tar tf -
```

```text
.peipkg/manifest.json
.peipkg/files.json
usr/
usr/bin/
usr/bin/hello
```

That file is a real package: [peipkg](/peios/using-peios/package-management/overview.md) can install it, and `pekit publish` can copy it to a configured destination. To start over, `pekit clean` removes the whole `out/` directory.

## Where to go next

- [Anatomy of a recipe](/pekit/recipes/anatomy.md) — every file in the recipe directory, all of `pekit.toml`'s sections, and the full `PEKIT_*` environment contract.
- [Commands and targets](/pekit/running/commands-and-targets.md) — the commands, named targets, `needs` ordering, target selection, and the `gen`/`verify` source-generation pair.
- [Invocation and flags](/pekit/running/invocation.md) — the command line: `--dry-run`, `--json`, and how recipes are located.
- [Recipe format reference](/pekit/reference/recipe-format.md) — the exhaustive schema for `pekit.toml` and package files.
