Quickpack Rust backend
A compiled alternative to the Python quickpack that reduces packing overhead and startup latency.
Background
Quickpack is Cantrip's fast, local-only alternative to
charmcraft pack. It skips LXD builds, linting, and
analysis, producing a valid .charm file suitable for
development deploys and upgrade testing. The original implementation
is written in Python and lives in src/quickpack/.
Because the agent packs charms frequently during its
build–test–debug loop, even small latency reductions
compound. The Rust implementation (src/quickpack-rs/)
eliminates the Python interpreter startup cost and handles file I/O
and ZIP compression natively, while producing byte-compatible output.
Architecture
The Rust binary mirrors the Python package module-for-module:
| Python module | Rust module | Responsibility |
|---|---|---|
cli.py |
main.rs |
CLI argument parsing (clap) |
pack.py |
pack.rs |
Orchestration, dispatch script, ZIP creation |
parts.py |
parts.rs |
UV and dump plugin processing |
metadata.py |
metadata.rs |
YAML parsing, metadata/manifest generation |
jujuignore.py |
jujuignore.rs |
Gitignore-style pattern matching |
Both implementations share the same command-line interface:
quickpack [charm_dir] [--output-dir DIR] [--quiet]
The Rust version does not expose a Python importable API
— it is a standalone binary only. Both versions shell out to
uv venv and uv sync to build the charm's
virtual environment; that subprocess call is identical in both cases
and dominates the total wall-clock time.
How the backend is selected
When the agent's quick_pack tool is invoked, it checks
for the Rust binary in two locations, in order:
quickpack-rson$PATH— this is the installed name, useful when the binary has been placed in/usr/local/binor similar.- The in-tree build artefact at
src/quickpack-rs/target/release/quickpack, relative to the cantrip package directory.
If neither is found, the tool falls back to the Python
quickpack library transparently. The
backend field in the tool result data indicates which
implementation was used ("rust" or
"python").
Performance
Benchmarks on an Ubuntu 24.04 machine (x86_64), packing a minimal
charm with one ops dependency:
| Metric | Rust | Python | Speedup |
|---|---|---|---|
Startup (--help) |
~43 ms | ~215 ms | 5× |
Full pack (incl. uv sync) |
~0.31 s | ~0.63 s | ~2× |
The full-pack speedup is bounded by uv sync, which
accounts for the majority of wall-clock time in both
implementations. The Rust advantage is most visible in the
non-uv overhead: file copying, YAML
serialisation, ZIP compression, and process startup.
For charms with large dependency trees or slow networks, the relative
speedup shrinks because uv sync dominates further. For
charms with few or no dependencies, the ~5× startup advantage
is more visible.
Building from source
The Rust toolchain (1.70+) is required. From the repository root:
cd src/quickpack-rs
cargo build --release
The binary is written to
src/quickpack-rs/target/release/quickpack. Cantrip
will find it automatically at this location. To install system-wide:
sudo cp src/quickpack-rs/target/release/quickpack /usr/local/bin/quickpack-rs
Running the tests
The Rust implementation has its own spread test suite at
tests/spread/quickpack-rs/task.yaml. It runs the same
24 functional tests as the Python version (the
python -m quickpack test is excluded as
Python-specific). To run locally:
spread tests/spread/quickpack-rs
The Python test suite remains the canonical source of truth. The
Rust suite verifies output compatibility: both implementations must
produce structurally identical .charm archives.
See also: