Skip to content

Repository files navigation

📦 Upac

One atomic deploy. Any package format. One rollback away.

A modular package management for Linux systems with composefs-based atomic deploys.

Links for repositories: GitHub Codeberg

General information: Version REUSE

Licensing: lib: LGPL-3.0-or-later WITH LGPL-3.0-linking-exception cli: GPL-3.0-only documentation: CC-BY-SA-4.0

The lib badge covers LGPL-3.0-or-later plus the LGPL-3.0-linking-exception — every crate under lib/ permits static linking without pulling the resulting binary under LGPL; see CONTRIBUTING.md § License and REUSE for what that means in practice.

🔍 Overview

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.

📖 Design

Full architecture and design decisions are in the project design notes:

For english (canonical):

  1. Introduction and definitions;
  2. Problem statement;
  3. Defining what the project is NOT (Non-goals);
  4. Disk structure;
  5. Project repository structure;
  6. Operating mechanisms;
  7. FFI and the boundaries of interaction between the components;
  8. Program modules.

For russian:

  1. Вступление и определения;
  2. Постановка задач;
  3. Определение того, чем проект НЕ является (Non-goals);
  4. Структура диска;
  5. Структура репозитория проекта;
  6. Механизмы работы;
  7. FFI и границы взаимодействия составных частей;
  8. Модули программы.

🚀 Usage

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 pin <digest>       # pins a deploy so gc's automatic retention never removes it
up commit unpin <digest>     # unpins a previously pinned deploy
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 diff [--from-prefix <d>] [--to-prefix <d>] [--from-config <d>] [--to-config <d>]  # combined package + untracked-file diff across two commits

up rollback <commit>         # reverts the system state to a specified commit — not just commit state, can also target an earlier /usr prefix

up mime sync                 # regenerates desktop/mime-type integration (upac-mime.xml + .desktop MimeType=) from installed decoder manifests

up gc                        # removes unreachable commits/deploys and reclaims storage

🧩 Components

Workspace layout

Crate Path License Role
upac-abi lib/abi LGPL-3.0-or-later + exception C-ABI types, error codes, and conversions shared between upac-lib and its consumers
upac-macro lib/macro LGPL-3.0-or-later + exception Derive macros for C-ABI struct plumbing, used internally by upac-lib/upac-abi
upac-lib lib/lib LGPL-3.0-or-later + exception 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)

Core Library (upac-lib)

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 (decoders/)

Decoders are separate shared libraries 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, its dependencies, and any declarative (package-format-native) trigger names it declares. All four decoders (alpm, deb, rpm, xbps) are written in Rust and can also be statically linked into upac-lib via the builtin-alpm/builtin-deb/builtin-rpm/builtin-xbps Cargo features.

Decoder Formats Distributions
libupac_decoder_alpm.so .pkg.tar.zst, .pkg.tar.xz, .pkg.tar.gz Arch Linux, Manjaro, etc.
libupac_decoder_deb.so .deb Debian, Ubuntu, etc.
libupac_decoder_rpm.so .rpm Fedora, RHEL, openSUSE, etc.
libupac_decoder_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.

CLI (upac-cli)

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), commit (deploy history/rollback), and mime (desktop/mime-type integration), plus two top-level commands that don't belong to any single family: gc and diff (combined package + untracked-file diff).

🔧 Building

Prerequisites

  • Rust (stable — pinned via rust-toolchain.toml)
  • libblkid, libmount (util-linux) — used by upac-lib for filesystem/mount handling

Build the Rust workspace

cargo build --workspace

Static linking

By default up dlopens libupac.so at startup, and upac-lib in turn dlopens boot-plugin .sos (booters/{uki,systemd-boot,grub,refind}) and decoder .sos (decoders/{alpm,deb,rpm,xbps}) described by on-disk manifests — this is the dynamic-plugins feature, on by default on both upac-cli and upac-lib.

Each crate also has a static-link/builtin-* axis for producing self-contained binaries with no dlopen at all — builtin-uki/builtin-systemd-boot/builtin-grub/builtin-refind (bundled as builtin-all) for boot plugins, builtin-alpm/builtin-deb/builtin-rpm/builtin-xbps for decoders (no bundle yet):

# libupac.so with uki+systemd-boot+grub compiled in, `up` still dlopens it
cargo build --workspace --no-default-features --features upac-cli/dynamic-plugins,upac-lib/builtin-all

# one self-contained `up` binary, no dlopen anywhere
cargo build --workspace --no-default-features --features upac-cli/builtin-all

dynamic-plugins and static-link are mutually exclusive within a crate (both active means two conflicting impl definitions, a compile error) — Cargo can't express that as a hard constraint, so verify the whole feature graph with cargo-hack (cargo install cargo-hack) instead of guessing combinations by hand:

cargo hack build -p upac-cli -p upac-lib --feature-powerset \
    --mutually-exclusive-features dynamic-plugins,static-link \
    --at-least-one-of dynamic-plugins,static-link

Build a decoder

alpm/deb/rpm/xbps are normal Rust workspace members, built along with everything else:

cargo build -p alpm -p deb -p rpm -p xbps

Note: packaging (Arch/RPM/deb) has not been ported to this branch yet.

Docs tooling (xtask)

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

About

Package manager for installing any type of package in Linux, as well as registering binary file rollbacks based on ComposeFS, written in Rust

Topics

Resources

Contributing

Security policy

Stars

18 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages