Skip to content

DDI output and metadata

A build produces three artifacts in the output directory, sharing a <name>_<version>_<arch> basename:

<name>_<version>_<arch>.raw     ← DDI (GPT image)
<name>_<version>_<arch>.json    ← package metadata
<name>_<version>_<arch>.zip     ← transport zip (only with --compress)

DDI (.raw)

A GPT image with a protective MBR and exactly three partitions:

# Name Type Contents
1 root arch-specific DPS root UUID squashfs filesystem (zstd, 128 KiB block, all entries root:root)
2 root-verity arch-specific DPS verity UUID dm-verity superblock + Merkle hash tree over partition 1 (SHA-256, 4 KiB blocks, 32-byte salt)
3 root-verity-sig arch-specific DPS verity-sig UUID JSON {rootHash, signature, certificateFingerprint} (sector-padded)

The partition GUIDs of partitions 1 and 2 are derived from the roothash: root GUID = first 16 bytes, verity GUID = last 16 bytes. systemd requires this binding; mismatched GUIDs cause attach to fail.

Signature partition contents

{
  "rootHash": "<32-byte hex>",
  "signature": "<base64 DER PKCS7>",
  "certificateFingerprint": "<sha256 hex of DER cert>"
}

The PKCS7 signature covers rootHash (the hex string, not raw bytes; this is what systemd reconstructs at verify time). SHA-256 is set explicitly — go.mozilla.org/pkcs7 defaults to SHA-1, which OpenSSL 3.x rejects.

The verity salt is randomly generated per build. The same rootfs built twice produces different roothashes; this is expected.

Single-block roothash

When the squashfs fits in a single dm-verity data block, the kernel uses zero hash-tree levels and the roothash equals SHA256(salt + data_block). buildctl's vendored encoder handles this without padding.

Architecture UUIDs

Arch DPS root partition type UUID
arm64 B921B045-1DF0-41C3-AF44-4C6F280D3FAE
amd64 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
armv7 69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
armv6 737A3E5E-7802-4B9E-9E51-9E1E3C60C6C0
i386 44479540-F297-41B2-9AF7-D131D5F0458A
riscv64 72EC70A6-CF74-40E6-BD49-4BDA08E8F224
ppc64le C31C45E6-3F39-478E-8098-46DC49F7BD2A
s390x 08A7B11A-BAEF-4B5D-9B6D-6F4E4A4A3F2C

Verity and verity-sig partitions have their own arch-specific type UUIDs per the Discoverable Partitions Specification.

Metadata JSON (.json)

Carries every field from package.yaml (except build-only fields backend, backend_arguments, strip), plus fields computed at build time:

Field Source
format_version Constant "1.0".
ddi_artifact <name>_<version>_<arch>.raw (the DDI filename).
ddi_sha256 SHA-256 of the .raw file, lowercase hex.
ddi_size Size of the .raw file in bytes.
created_at Build timestamp, RFC 3339, UTC.
signing_key_id SHA-256 of the DER encoding of the signing certificate, lowercase hex. Matches certificateFingerprint in the signature partition.

command is included only if set; network only when runtime: nspawn. uid/gid are not included; user allocation is appctl's responsibility at install time.

For the full schema, see package-metadata.schema.json.

Transport zip (.zip)

--compress (or buildctl compress) writes a zip of the .raw and .json files using Store (no compression) — the squashfs is already zstd-compressed.