⚠️ Branch in progress. This branch (lib-rs) is a from-scratch rewrite of upac's core library in Rust, built around composefs instead of OSTree. The FFI/orchestration engine is done; the actual command bodies, the hook system, and packaging are still being implemented — expect gaps andtodo!()s. SeeROADMAP.mdfor the current phases andTODO.mdfor concrete near-term items.
A modular package management library for Linux systems with composefs-based atomic deploys.
Upac is a package manager for Linux-compatible systems. It manages the updating, removal, and installation of various package formats using per-format decoders, and keeps every change behind an atomic, composefs-backed deploy so the system can always be rolled back to a previous commit.
upac-cli (binary: up) is a command-line frontend written in Rust that dynamically loads libupac.so and drives it through its C ABI.
upac-lib is the core library, also written in Rust, designed to be embedded into package managers through a stable C ABI. It handles installation, database management (via redb), and composefs-based system snapshotting — without imposing any policy on how packages are fetched or what format they come in.
The library is intentionally split into independent components: decoders handle format-specific unpacking, the core library handles installation and database operations, and everything crosses the FFI boundary through a shared upac-abi crate.
It covers the disk layout, the deploy/rollback model, the /etc merge, GC, the FFI boundary, and the planned module structure.
Full architecture and design decisions live in the project design notes:
For english (canonical):
Introduction and definitions;Problem statement;Defining what the project is NOT (Non-goals);Disk structure;Project repository structure;Operating mechanisms;FFI and the boundaries of interaction between the components;Program modules.
For russian:
Вступление и определения;Постановка задач;Определение того, чем проект НЕ является (Non-goals);Структура диска;Структура репозитория проекта;Механизмы работы;FFI и границы взаимодействия составных частей;Модули программы.
up pkg install -f <path>... [-m <message>] # installs package(s) from local file(s), with checksum verification (-f is local-only; a future network form will resolve by name instead)
up pkg remove <name>... [--arch <arch>] [--arch-sub <sub>] [-m <message>] # removes installed package(s) by name, optionally disambiguated by arch (alias: uninstall)
up pkg update -f <path>... [-m <message>] # updates installed package(s) from local file(s) (same -f convention as install, for the same reason)
up pkg list [--version --arch --author --license --url --packager --size --description --checksum] # lists installed packages, with optional extra columns
up pkg diff [<from>] [<to>] # diffs installed packages between two prefixes (commit digests); defaults if omitted
up pkg search <query> [--version ... --checksum] [--regex] # searches package metadata (same field flags as pkg list)
up pkg search <query> --package <name> --package-arch <arch> [--package-arch-sub <sub>] [--regex] # same, scoped to one package's own metadata
up file add <path>... --package <name> --arch <arch> [--arch-sub <sub>] [-m <message>] # tracks standalone file(s) against a package
up file remove <path>... --package <name> --arch <arch> [--arch-sub <sub>] [-m <message>] # untracks standalone file(s) from a package
up file diff [<from>] [<to>] # diffs tracked files between two prefixes
up file search <query> [--regex] # searches tracked files by path
up file search <query> --package <name> --package-arch <arch> [--package-arch-sub <sub>] [--regex] # same, scoped to one package's files
up commit new <message> # creates a new commit of the current deploy state
up commit list # lists config-commits for the current deploy (rollback targets)
up commit prefixes # lists deploy-level (prefix) commits
up commit history # lists deploy-level commits with their nested config-commits, marking the active one
up commit diff [<from>] [<to>] # diffs tracked files between two config-commits
up commit rollback <commit> # reverts the system state to a specified commit
up diff [--from-prefix <d>] [--to-prefix <d>] [--from-config <d>] [--to-config <d>] # combined package + untracked-file diff across two commits
up gc # removes unreachable commits/deploys and reclaims storage| Crate | Path | License | Role |
|---|---|---|---|
upac-abi |
lib/abi |
LGPL-3.0-or-later | C-ABI types, error codes, and conversions shared between upac-lib and its consumers |
upac-macro |
lib/macro |
LGPL-3.0-or-later | Derive macros for C-ABI struct plumbing, used internally by upac-lib/upac-abi |
upac-lib |
lib/lib |
LGPL-3.0-or-later | Core library: composefs-based atomic deploys, redb package database, exposed via a C ABI (libupac.so) |
upac-cli |
user/upac-cli |
GPL-3.0-only | CLI frontend (binary up) |
upac-sign-cli |
user/sign-cli |
GPL-3.0-only | CLI for signing upac hook files and other artifacts with Ed25519 certificates (binary up-si) |
The core library exposes a C-compatible ABI through libupac.so. All strings cross the boundary as { ptr, len } pairs rather than null-terminated C strings. All functions return an integer error code.
Decoders are separate shared libraries, still written in Zig, that handle format-specific package unpacking. Each decoder receives a package path, an output directory, and a SHA-256 checksum; it verifies the checksum, extracts the package, parses the metadata, and returns a PackageMeta struct.
| Decoder | Formats | Distributions |
|---|---|---|
libupac-alpm.so |
.pkg.tar.zst, .pkg.tar.xz, .pkg.tar.gz |
Arch Linux, Manjaro, etc. |
libupac-rpm.so |
.rpm |
Fedora, RHEL, openSUSE, etc. |
libupac-deb.so |
.deb |
Debian, Ubuntu, etc. |
libupac-xbps.so |
.xbps |
Void Linux |
Adding support for a new package format means writing a new decoder .so — the core library does not need to change.
A command-line frontend written in Rust that dynamically loads libupac.so and the appropriate decoder at runtime. Subcommands are grouped under pkg (packages), file (standalone tracked files), and commit (deploy history/rollback), plus two top-level commands that don't belong to any single family: gc and diff (combined package + untracked-file diff).
- Rust (stable — pinned via
rust-toolchain.toml) - Zig ≥ 0.16.0 — for the decoders under
decoders/ libblkid,libmount(util-linux) — used byupac-libfor filesystem/mount handling
cargo build --workspacecd decoders/alpm
zig buildNote: packaging (Arch/RPM/deb) and the old
make-based build pipeline from the Zig implementation have not been ported to this branch yet.
The repo tree embedded in each design chapter under doc/ is generated, not hand-edited. xtask is its own standalone workspace (see xtask/Cargo.toml) so it doesn't affect the main workspace's MSRV/edition:
cargo xtask gen-tree # regenerate the tree in every marked doc file
cargo xtask gen-tree --check # verify it's up to date, no writes