shelly build
Compiles local recipes without installing.
Use this when you are writing a PKGBUILD, testing a modified recipe, or building a standalone package archive (.pkg.tar.zst) for distribution or later installation.
shelly build compiles a PKGBUILD recipe into an installable Arch Linux package archive (.pkg.tar.zst). It is
Shelly’s native, standalone package build engine, the same shelly install aur uses, made entirely in
Zig without relying on makepkg or fakeroot.
Shelly resolves recipe metadata with a static parser that never executes arbitrary shell code during analysis. Recipe lifecycle functions run strictly as ordinary Bash child processes of an unprivileged, non-root user, protected by interactive security reviews, cryptographic digest locks, and optional Linux kernel Landlock sandboxing.
shelly buildShelly offers two distinct workflows for building packages:
shelly build
Compiles local recipes without installing.
Use this when you are writing a PKGBUILD, testing a modified recipe, or building a standalone package archive (.pkg.tar.zst) for distribution or later installation.
shelly install aur
Fetches, reviews, builds, and installs in one command.
Use this when you want to discover, download, and install a package directly from the Arch User Repository (AUR) onto your current system.
makepkg| Feature | shelly build |
makepkg |
|---|---|---|
| Metadata Parsing | Static analysis (safe, bash-aware parser; no arbitrary code execution) | Sourced directly in host Bash |
| Privilege Model | Strict non-root builder execution | Requires fakeroot / LD_PRELOAD |
| File Ownership | Virtual ownership journal (mapped by inode into .PKGINFO & .MTREE) |
Relies on fakeroot host translation |
| Integrity Checks | SHA-256 review digest lock (prevents TOCTOU modifications) | Manual recipe inspection |
| Sandboxing | Kernel Landlock LSM (restricts filesystem access to $HOME) |
Unrestricted host access by default |
| Configuration | Structured TOML (shellybuild.conf) |
Shell script (makepkg.conf) |
Ensure your directory contains a valid PKGBUILD and any local source or install files:
cd ~/src/my-packageshelly buildTo automatically fetch and install missing build dependencies before compiling, pass -s (or --sync-deps):
shelly build -sBefore executing any shell commands, Shelly presents the full PKGBUILD, local source files, .install scriptlets, and results from its automated security scans (e.g. homograph detection, malicious scriptlets, or privilege elevation).
Confirm to proceed with the build, or decline to cancel safely.
Upon completion, the compressed package archive (e.g. my-package-1.0.0-1-x86_64.pkg.tar.zst) is placed in the packages destination configured in shellybuild.conf (falling back to the current directory).
Install it onto your system using Shelly’s standard installer:
shelly install standard ./my-package-1.0.0-1-x86_64.pkg.tar.zst-l (--install) installs the archives the build just produced, so a package no longer has to be built and then
installed by hand:
shelly build --install# Equivalent to makepkg -sishelly build -s --installshelly -AslLike makepkg -i, only the freshly built archives are added, so combine the flag with -s when the package’s own
dependencies may be missing.
Administrator credentials are requested once, up front — the builder locks itself against elevation with
PR_SET_NO_NEW_PRIVS and can never elevate afterwards. The build runs unprivileged as your regular user, and the
elevated coordinator installs the archives afterwards, through the same path as shelly install standard <archive>.
Other details worth knowing:
-s -l, build-dependency cleanup runs after the install, so a runtime dependency the built package now needs
is not removed.-i, the guest exports the archives and the host coordinator installs them; the container never writes to the
host database.--json, --ui-mode, --review-only, or --makesrcinfo exits 2 with
Cannot combine --install with <mode>. before building.Security is at the core of Shelly’s build architecture. Before any code is executed:
sudo, su, run0), network calls in build functions (curl, wget),
and Unicode homograph spoofing..install scripts or local
patches) are displayed for your review.shelly build [<pkgbuild>] [options]| Option | Short | Description |
|---|---|---|
--sync-deps |
-s |
Automatically install missing build and runtime dependencies with before building. |
--check |
-c |
Execute the check() test suite in the PKGBUILD and install check dependencies. |
--no-check |
Skip the check() test function, overriding shellybuild.conf. |
|
--isolated |
-i |
Build in a fresh, unprivileged systemd-nspawn container environment. |
--install |
-l |
Install the built package archives after a successful build. With -s this mirrors makepkg -si. |
--sign |
Generate a detached OpenPGP signature (.sig) next to each built package archive. |
|
--nosign |
Skip package signing, overriding shellybuild.conf. |
|
--key <key> |
Specify the GPG key ID or 40-character fingerprint to use for package signing. | |
--compression-level <1-5> |
Select an archive compression preset from 1 (conservative) to 5 (maximum), overriding package.compression_level. |
|
--package-destination <dir> |
Save package archives to a specified absolute directory instead of the default location. | |
--reviewed |
-r |
Mark the recipe as reviewed and bypass the interactive prompt (use only for automated workflows or trusted recipes). |
--makesrcinfo |
Statically parse the recipe, generate .SRCINFO to standard output, and exit without building. |
|
--noverify |
Skip the PKGBUILD’s verify() function (not recommended). |
Shelly provides concise shortcodes where -A represents shelly build. Options attach directly to the shortcode flag:
shelly -A # Build PKGBUILD in current directoryshelly -As # Build and install missing dependenciesshelly -Al # Build, then install the built packageshelly -Asl # Sync dependencies, build, and install (makepkg -si)shelly -Ac # Build and run test suites (check)shelly -Asc # Build, install dependencies, and run check()shelly -Ai # Build in an isolated systemd-nspawn containershelly -Ar # Build and mark recipe as reviewedshelly -Ars # Non-interactive build with dependency syncshelly -A ~/pkg/PKGBUILD # Build recipe at specific pathshelly -Ah # Show build command helpFor the complete options list, see the CLI Reference.
For power users, package maintainers, and curious developers, Shelly’s build engine operates across several dedicated stages:
┌─────────────────────────────────────────────────────────────┐│ 1. Static Parsing & Security Scans ││ • Word expansion, variable map, dependency resolution ││ • Security checks (privilege, obfuscation, homographs) │└──────────────────────────────┬──────────────────────────────┘ │┌──────────────────────────────▼──────────────────────────────┐│ 2. Review & Digest Lock ││ • Interactive confirmation of PKGBUILD + local files ││ • Compute SHA-256 review digest snapshot │└──────────────────────────────┬──────────────────────────────┘ │┌──────────────────────────────▼──────────────────────────────┐│ 3. Source Pipeline ││ • Download HTTP(S) sources to cache & clone Git mirrors ││ • Verify checksums (sha256, b2, etc.) & PGP signatures ││ • Safe extraction (guards against path traversal & bombs)││ • Execute verify() step │└──────────────────────────────┬──────────────────────────────┘ │┌──────────────────────────────▼──────────────────────────────┐│ 4. Lifecycle Execution (Sandboxed Non-Root Bash) ││ • prepare() ➔ pkgver() ➔ build() ➔ check() ││ • Landlock LSM restricts access to $HOME │└──────────────────────────────┬──────────────────────────────┘ │┌──────────────────────────────▼──────────────────────────────┐│ 5. Package Assembly & Virtual Ownership ││ • package() execution per split-package member ││ • Tidy/strip binaries & apply purge cleanups ││ • Generate .PKGINFO, .BUILDINFO, .MTREE metadata ││ • Assemble .pkg.tar.zst archive & optional GPG signing │└─────────────────────────────────────────────────────────────┘Traditional build tools source the PKGBUILD directly in Bash just to read package metadata, which allows untrusted
recipes to run arbitrary commands before you ever see a prompt.
Shelly uses a purpose-built static parser that extracts variables, arrays, dependencies, and execution plans without
shell execution. It features a Bash-aware expansion engine supporting parameter expansions (${var#prefix},
${var/search/replace}, substring slicing), arithmetic expressions ($((...))), and brace expansions (pkg-{a,b}).
Any dynamic constructs that require runtime evaluation (such as command substitutions in integrity arrays) are isolated
and evaluated only after user approval in a controlled sandbox.
Shelly handles remote and local sources with strict security controls:
/var/cache/shelly/sources), avoiding duplicate downloads across rebuilds.sha512sums, sha256sums, b2sums,
etc.) and validates detached OpenPGP signatures. Pinned Git commits and tags are verified against deterministic
archive generation.makepkg relies on fakeroot (an LD_PRELOAD library) to intercept filesystem calls and pretend files are owned by
root:root inside $pkgdir.
Shelly replaces fakeroot with a virtual ownership engine:
package() execution
into an in-memory journal..PKGINFO, .MTREE, and .pkg.tar.zst archive
metadata.Shelly natively supports split PKGBUILD recipes containing multiple package_<pkgname>() functions:
pkgdesc, depends, backup, options) are
evaluated independently..pkg.tar.zst archive is assembled per package member.--package <member_name>.Shelly provides two levels of isolation for building untrusted recipes:
On modern Linux kernels (5.13+), Shelly can enforce filesystem isolation on build steps using the kernel’s Landlock Linux Security Module (LSM).
When enabled in shellybuild.conf:
prepare, pkgver, build, check, package) execute with restricted privileges via
prctl(NO_NEW_PRIVS).$HOME directory (including ~/.ssh, ~/.gnupg, and browser data)./usr, /etc, /opt, /tmp, /proc) and the build directory remain accessible.extra_read or extra_write.-i, --isolated)For complete clean-room isolation, pass -i or --isolated:
shelly build -iThis builds the package as an unprivileged user inside a fresh systemd-nspawn container root. It ensures no host files
or undeclared dependencies influence the compilation, mirroring official Arch package build environments.
Adding -l (--install) is safe here: the guest exports the archives and the host coordinator installs them, so the
container never writes into the host system database.
Resolved output settings carry into the guest too: the --compression-level / package.compression_level preset is
applied to every selected split package, and a configured package.carch label is carried in the same way.
shellybuild.conf)shellybuild.conf is Shelly’s data-only TOML configuration file for the package builder. It replaces makepkg.conf for
Shelly’s native build engine.
Shelly merges configuration layers in the following order (later layers override earlier ones field-by-field):
/etc/shellybuild.conf$XDG_CONFIG_HOME/shelly/shellybuild.conf (or ~/.config/shelly/shellybuild.conf)--check, --sign, --package-destination) take final precedence.Below is the complete reference schema for shellybuild.conf with default values:
[build]# Target architecture and host tripletcarch = "x86_64"chost = "x86_64-pc-linux-gnu"
# Compiler and linker flags (arrays are joined with spaces during step execution)cppflags = []cflags = ["-O2", "-pipe"]cxxflags = ["-O2", "-pipe"]ldflags = ["-Wl,-z,relro", "-Wl,-z,now"]ltoflags = ["-flto=auto"]makeflags = ["-j2"]
# Default execution of check() test suitescheck = true
# Compiler cache and distributed compilation integrationsccache = falsedistcc = falsedistcc_hosts = []
[package]# Package metadata and compression formatpackager = "Unknown Packager"extension = ".pkg.tar.zst"
# Optional; omission labels packages with build.carch# carch = "x86_64_v3"
# Optional; omission preserves the compression library's defaults# compression_level = 3
# Build options and tidy behavioroptions = ["strip", "docs", "emptydirs", "zipman", "purge", "lto"]strip_binaries = ["--strip-all"]strip_shared = ["--strip-debug"]strip_static = ["--strip-unneeded"]
# OpenPGP package signing defaultssign = falsesign_key = "0000000000000000000000000000000000000000"
[destinations]# Workspace and artifact storage locationsbuild = "/var/tmp/shellybuild"packages = "/var/cache/shelly/packages"sources = "/var/cache/shelly/sources"logs = "/var/log/shelly/build"
[sandbox]# Kernel Landlock LSM step confinementenabled = falseextra_read = []extra_write = [][build]carch / chost: The build-time target architecture and host triplet, exported as $CARCH and $CHOST. To label
the finished archive with a different architecture, see Package Architecture.makeflags: Concurrency and build options passed to make (e.g. ["-j$(nproc)"]).cflags / cxxflags / ldflags / ltoflags: Optimization and hardening flags exported to build child processes.ccache: When true, prepends /usr/lib/ccache/bin to PATH.distcc: When true, prepends /usr/lib/distcc/bin to PATH and exports DISTCC_HOSTS.[package]packager: Identifies the builder in .PKGINFO (e.g. "Jane Doe <[email protected]>").carch: Optional architecture label for built archives, replacing build.carch only in the finished package. See
Package Architecture.compression_level: Optional archive compression preset from 1 to 5; omission preserves the compression library’s
defaults. See Package Compression.options: Controls package tidy operations. Supported options include strip, docs, emptydirs, zipman,
purge, lto, debug, libtool, and staticlibs. Recipes can override them using options=('!strip' '!purge').purge: Automatically removes usr/share/info/dir, .packlist files, and *.pod files from $pkgdir before
packing.sign / sign_key: Controls detached OpenPGP package signature generation.[destinations]build: Root directory for temporary extraction and compilation workspaces ($srcdir and $pkgdir).packages: Destination directory where finished .pkg.tar.zst archives are committed.sources: Cache directory for downloaded source tarballs and Git mirrors.logs: Directory where mandatory build logs are stored.[sandbox]enabled: Enables Landlock filesystem isolation for all build lifecycle steps.extra_read: List of absolute directory paths outside the standard system hierarchy granted read access (e.g.
["/home/user/.cargo/registry"]).extra_write: List of absolute directory paths granted read-write access (e.g. ["/home/user/.cache/ccache"]).Two settings name an architecture, and they split the work:
[build] carch (default "x86_64") is the build-time architecture. It exports $CARCH into the recipe, selects
the architecture-suffixed arrays (source_x86_64, depends_x86_64, and friends), drives the arch=() support
check, and keeps governing everything up to packaging, including .SRCINFO metadata.[package] carch is optional and is Shelly’s equivalent of makepkg’s PACKAGECARCH (as carried by CachyOS’s
pacman). It changes only the label of the assembled package: the .PKGINFO arch field, the .BUILDINFO pkgarch
field, and the name-version-release-arch file name. It has no CLI flag.A recipe declaring arch=('x86_64') is therefore evaluated and built as plain x86_64 and, with
[package] carch = "x86_64_v3", produces my-package-1.0.0-1-x86_64_v3.pkg.tar.zst. Packages declaring
arch=('any') remain any, and omitting the key everywhere labels packages with build.carch, which is the previous
behavior.
The label must match [A-Za-z0-9_]+ and must not be any, because it becomes part of the file name. User
configuration overrides system configuration, and the resolved value is carried into --isolated builds.
The package extension selects the archive format, and package.compression_level selects how hard to compress it.
Set the level to an integer from 1 to 5, or override it for one invocation with --compression-level: the CLI wins
over the user configuration, which wins over the system configuration. Omitting the setting everywhere preserves the
compression library’s own defaults, so level 3 is a recommended explicit starting point, not a new default. Each
preset maps to a backend level per format:
| Level | Preset | zstd | gzip | xz | bzip2 | Intended tradeoff |
|---|---|---|---|---|---|---|
| 1 | Conservative | 1 | 1 | 0 | 1 | Low resource use; larger packages |
| 2 | Fast | 3 | 3 | 3 | 3 | Quick builds; reasonable compression |
| 3 | Balanced | 6 | 6 | 6 | 5 | Recommended starting point |
| 4 | Compact | 12 | 8 | 7 | 7 | Favor smaller packages |
| 5 | Maximum | 19 | 9 | 9 | 9 | Highest compression effort offered |
All presets are lossless. Higher levels generally spend more CPU time and memory to reduce size, but smaller output is not guaranteed for every input. Shelly’s benchmark of the preset/format matrix put zstd preset 3 about 22% below preset 1, with preset 5 buying further size at a much larger cost in compression time and peak memory. zstd presets use standard levels through 19, without long-distance matching or ultra modes, and explicit presets ask xz and zstd for a single compression worker.
.pkg.tar.zst, .pkg.tar.gz, .pkg.tar.xz, and .pkg.tar.bz2. Invalid levels, incompatible
extensions, and unsupported native filter options fail before the PKGBUILD executes. Shelly does not silently
substitute another preset or fall back to an external compression program.--isolated builds, where the value is carried into the guest configuration..MTREE generation, and signing behavior are
untouched, and signatures still cover the completed archive.--review-only)For CI/CD pipelines or static linting, evaluate a PKGBUILD and emit structured JSON metadata without downloading
sources or compiling:
shelly build --review-only ~/src/my-package/PKGBUILD--review-digest)Guarantee that automated builds compile the exact recipe reviewed by your team, preventing upstream tampering or race conditions:
# Specify the accepted 64-character SHA-256 review digestshelly build --review-digest "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" -r.SRCINFO Statically (--makesrcinfo)Generate a valid .SRCINFO file using Shelly’s fast static parser:
shelly build --makesrcinfo > .SRCINFOFor scripts, combine -n (--no-confirm) and -r (--reviewed):
shelly build -n -r -s--keep-workdirs)If a build fails and you need to inspect the $srcdir or $pkgdir staging trees, use the internal flag
--keep-workdirs:
shelly build --keep-workdirsWork directories are preserved in the configured build destination (/var/tmp/shellybuild/ by default) instead of
being cleaned up on exit.