PAX

PAX is a Perl-native adaptive compiler and standalone binary packager.

Introduction

PAX exists to turn a Perl application plus its repeatable build inputs into one standalone executable.

Without that layer, a normal Perl deployment usually depends on some mix of:

PAX changes that deployment shape. The target artifact is one executable that can carry compiled code units, packaged runtime payloads, embedded assets, and native artifacts where PAX can prove a region is safe to specialize.

In bundled-runtime mode that includes the packaged helper programs and the linked shared libraries and SONAME aliases required by bundled XS modules, so helper commands and query/runtime helpers still work after the original source tree and CPAN installation are gone.

The design goal is not "replace Perl with magic". The design goal is:

The public command surface is intentionally small:

perl bin/pax help
perl bin/pax build
perl bin/pax run

Everything else in the repository is compiler/runtime implementation, test coverage, or release tooling. Users should not call internal diagnostic subcommands through bin/pax.

What You Get

Goals

Main Concepts

Quick Start

Build from a local paxfile.yml:

perl bin/pax build

Long builds print a DD-style task rundown on stderr by default. On a real terminal the board redraws live; in non-interactive runs it prints a static rundown. The build path is broken into concrete checkpoints such as code-unit compilation, application metadata inference, dependency analysis, native artifact analysis, manifest writing, and launcher compilation. The code-unit phase is further split into source discovery, entrypoint compilation, application unit compilation, and dependency unit compilation so long builds keep moving visibly. Application unit compilation includes the current file name, so a slow module no longer looks like a frozen counter. To suppress it:

PAX_PROGRESS=0 perl bin/pax build --compact

Build an explicit entrypoint:

perl bin/pax build bin/my-app

Build to a specific output path:

perl bin/pax build -o ./build/my-app bin/my-app
perl bin/pax build --output ./build/my-app bin/my-app

Build and immediately run:

perl bin/pax run -- version
perl bin/pax run bin/my-app -- version

pax run uses the same build inputs as pax build, writes or refreshes the standalone executable, and then executes that binary with arguments after --.

CLI Contract

usage:
  pax build ...
  pax run ...

Public commands:

Common options:

paxfile.yml

With no positional entrypoint, pax build and pax run read paxfile.yml. CLI flags override file values. When a positional entrypoint is supplied on the CLI, PAX treats that target as an isolated build and does not silently inherit libs, source_roots, assets, asset_dirs, cpanfiles, or app metadata from the ambient default paxfile.yml. An explicit --paxfile still applies its manifest defaults.

Example:

name: example-app
entrypoint: bin/example-app
output: build/example-app
libs:
  - lib
source_roots:
  - lib
assets:
  - share/banner.txt
asset_dirs:
  - share/public
cpanfiles:
  - cpanfile
runtime_mode: bundled_perl

Output path precedence:

  1. CLI --output / -o
  2. paxfile.yml output
  3. fallback .pax/standalone/<name>/<name>

Manual

Installation

For local development, install the distribution prerequisites and run from the repository checkout:

cpanm --installdeps .
perl bin/pax help

For release packaging, Dist::Zilla must also be available:

cpanm Dist::Zilla

First Build

The simplest workflow is a project-local paxfile.yml:

name: example-app
entrypoint: bin/example-app
output: build/example-app
libs:
  - lib
cpanfiles:
  - cpanfile
runtime_mode: bundled_perl

Then build:

perl bin/pax build

Run the result:

./build/example-app

Build Without paxfile.yml

PAX does not require a manifest when the CLI provides the required build shape:

perl bin/pax build -o ./build/example-app bin/example-app

That keeps one-off builds and self-hosting neutral even inside repositories that ship their own paxfile.yml. Extra roots, assets, and CPAN policy files must be declared explicitly on the CLI in that mode.

Inline entrypoints use the same public surface. -I adds Perl library roots, -M loads and imports modules before execution, and -e supplies the entrypoint code directly:

perl bin/pax build \
  -I lib \
  -MDateTime \
  -e 'print DateTime->now'

pax run accepts the same switches:

perl bin/pax run \
  -I lib \
  -MDateTime \
  -e 'print DateTime->now'

Self Compile

PAX can build PAX itself:

perl bin/pax build -o /tmp/pax bin/pax
/tmp/pax help

When the original source paths still exist, that self-built standalone pax binary can also rebuild from another standalone pax binary input.

That same self-built binary can then build another standalone application from its own paxfile.yml. It can also rebuild from another standalone pax binary when the original source checkout is no longer present, because the build path carries an embedded source snapshot for the application units it needs to rebuild.

Asset Embedding

Assets are copied into the executable payload and extracted into a private runtime directory when the binary starts. Framework code can read them through the embedded asset root prepared by the PAX runtime.

Example:

perl bin/pax build \
  --name webapp \
  --lib lib \
  --source-root lib \
  --asset-dir share \
  --cpanfile cpanfile \
  --runtime-mode bundled_perl \
  --output ./build/webapp \
  bin/webapp

This pattern supports web applications that include Perl modules, templates, CSS, JavaScript, and other static files.

Web Applications

PAX supports the single-binary packaging shape for framework applications that combine:

The validated SOW-03 proof includes a Dancer2 + Plack/Starman + Template Toolkit web application packaged as one executable and deployed through a multi-stage Docker flow.

Docker Deployment

Two-stage pattern for a generic project:

FROM perl:5.42 AS builder
WORKDIR /workspace
COPY . /workspace
RUN cpanm --installdeps .
RUN perl bin/pax build --output /out/app

FROM debian:bookworm-slim
COPY --from=builder /out/app /usr/local/bin/app
CMD ["/usr/local/bin/app"]

The final stage receives only the built executable. It does not need the source tree, asset tree, cpanfile, or web framework installation when the binary was built in bundled runtime mode.

For an external application, the validated deployment pattern is:

  1. build a standalone pax binary
  2. copy that pax binary into the application build stage
  3. compile the application into its own standalone binary
  4. copy only that final application binary into the runtime stage

Architecture

PAX packages an application through these stages:

  1. Entrypoint and manifest loading.
  2. Dependency and source-root discovery.
  3. Code unit compilation into PCU or hybrid PCU records where supported.
  4. Native artifact packaging for supported hot regions.
  5. Asset and runtime payload embedding.
  6. Standalone launcher generation.
  7. Runtime extraction and dispatch with fallback safety.

Compilation is adaptive. If a module shape fails, the preferred fix is a reusable compiler, loader, dependency discovery, or runtime improvement that works for other projects with the same structure.

Why The Two-Command Surface Works

PAX used to expose more internal diagnostic and build commands at the CLI surface. SOW-03 intentionally collapsed that down to:

That keeps the operator workflow small while still allowing the internal Perl modules to carry richer build, inspection, and validation logic behind the public facade.

Known Limits

Testing And Release Gates

Primary local validation:

make tdd-gate
make bdd-gate
make atdd-gate
make qa-gate
make test
make release-gate
make cpan-build
make cpan-gate

CPAN Release Gates

Release readiness is checked by the repository gates:

make cpan-bump-version VERSION=<next-version>
# update Changes with a meaningful top entry for <next-version>
make tdd-gate
make bdd-gate
make atdd-gate
make qa-gate
# commit the tracked release-preparation changes
make all-gates

Optional release:

make cpan-release

make cpan-release follows the same release shape used by the DD source: verify the repo gates, locate the built tarball in the repository root, and upload it with the local cpan-upload configuration.

Required release files:

The CPAN gate verifies the distribution tarball and git index exclude temporary runtime probes, generated workspaces, coverage output, planning artifacts, and other non-release files.

Completion rule:

Release flow rule:

FAQ

Is PAX only for one specific project?

No. PAX uses DD and other applications as validation corpora, but core compiler and runtime logic are expected to stay neutral and reusable.

Does PAX guarantee Rust-like speed for all Perl code?

No. The target is to package the whole application correctly and accelerate hot paths that PAX can prove are safe to specialize. Dynamic regions still use fallback execution.

Does pax run require a separate app server?

No. Under SOW-03, pax run builds the standalone executable and then runs that binary directly.

Can PAX build web applications with embedded static assets?

Yes. The validated packaging path includes templates, CSS, JavaScript, and framework code embedded into one standalone executable.

Repository Map

Documentation Rule

PAX documentation follows the DD-style parity rule recorded in docs/pax-doc-parity.md: document the product as both an operator manual and a main architecture reference, not just as a command list.

Contributor Rules