Skip to content

RLPM

A package backend is the engine Shelly uses to read repositories, resolve dependencies and apply package transactions. Shelly ships two of them and builds both by default: libalpm, the C library behind pacman, is the default, so nothing changes unless you opt in. The alternative is RLPM, the Rolling Linux Package Manager, Shelly’s own native package engine written in Zig. Builds that include both engines can select either through a configuration setting. Dedicated RLPM-only builds, including the Arch and Devario recipes, default to RLPM and cannot select libalpm.


Most systems are served by the default. Pick an engine based on how you want transactions to run under the hood:

libalpm (default)

The library behind pacman.

Nothing to configure or migrate: repositories, locks, keyrings and caches are exactly the ones pacman already uses on your system.

RLPM

Shelly’s own native engine, written in Zig.

Repositories, dependency resolution and transactions are implemented natively, with no libalpm dependency at all. It supports both traditional pacman tar databases and in-memory SQLite databases alongside runtime x86_64_v2/v3/v4 architecture detection, and powers RLPM-only builds.


The active engine is the NativePackageBackend entry in your CLI configuration (~/.config/shelly/config.json), so the standard shelly config commands are all you need:

  1. Terminal window
    shelly config get NativePackageBackend

    This prints libalpm or rlpm. On a default installation the answer is libalpm.

  2. Terminal window
    shelly config set NativePackageBackend rlpm
  3. The setting takes effect on the next CLI invocation, not inside an already-running session. The desktop app has no separate switch: it runs the engine the CLI is set to.

On a build that includes libalpm, switch back the same way:

Terminal window
shelly config set NativePackageBackend libalpm

On the standard Arch path profile, switching engines preserves the configured package locations and uses a compatible installed database format. This does not imply identical behavior in every transaction or failure case:

  • Configuration source: pacman.conf is still read for repositories and paths, and path defaults stay on the standard pacman profile: /etc/pacman.conf, /var/lib/pacman, /var/cache/pacman/pkg and the standard keyring and hook directories.
  • Installed database: the local database is still /var/lib/pacman, in the format both engines read and write.
  • Locking: both engines hold the same db.lck transaction lock, so pacman and Shelly still cannot run transactions at the same time.
  • Existing data: switching does not touch downloaded archives, mirror definitions, the keyring or your installed state.
  • Other package sources: Flatpak and AppImage use their own implementations. AUR builds use the selected native engine for dependency installation and installing built package archives.
  • No migration step: there is nothing to convert, reinstall or clean up when switching. RLPM update previews automatically copy local metadata into a separate database and replace legacy cache links.

For how this native configuration relates to Shelly’s CLI settings and the separate shellybuild.conf used by shelly build, see the configuration guide.


Where libalpm calls into Arch’s C library, RLPM re-implements the engine natively. The differences you can observe:

  • Independent implementation: RLPM does not delegate operations to libalpm. Repository handling, dependency resolution and transactions are Shelly’s own Zig implementation. RLPM-only builds omit libalpm linkage; dual-backend binaries still load their compiled dynamic dependencies even when RLPM is selected.
  • SQLite repository databases: a repository database containing a pacman.db member is read as SQLite directly in memory (sqlite3_deserialize), with no member extracted to disk, alongside ordinary tar repositories.
  • Runtime architecture detection: Architecture = auto expands through detected CPU and OS capabilities into the ordered x86_64, x86_64_v2, x86_64_v3, x86_64_v4 set, selecting the highest supported microarchitecture tier.
  • Repository provenance: installed packages remember which repository they came from (%INSTALLED_DB%), and that record survives even after the repository is removed from the configuration.
  • Sealed verification: archives are copied into an anonymous sealed in-memory file (memfd_create) before verification, preventing time-of-check to time-of-use (TOCTOU) modifications so the bytes GPG and the archive reader inspect cannot change afterwards. Checksums prefer SHA-256 over MD5, and an enabled embedded signature supersedes repository digests.
  • Isolated hooks: hooks and scriptlets require a network namespace by default, with loopback brought up best effort. A hook opts in with NetworkAccess=allowed, and administrators can lift isolation with DisableSandboxNetwork (or DisableSandbox) in the native package configuration (pacman.conf or shelly.conf, depending on the path profile); the programmatic API exposes this as sandbox.disable_network.
  • Root-relative confinement: filesystem inspection and mutations use Linux openat2 with RESOLVE_IN_ROOT | RESOLVE_NO_MAGICLINKS, ensuring symlink resolutions stay confined to the held root directory descriptor without path traversal risks.
  • Conservative space preflight: check_space applies a native safety cushion (the smaller of roughly 5% capacity and 20 MiB) and accounts for staging, directory overhead, and database journals, catching capacity issues before payload mutations.
  • Durable record keeping: each installed record is published through a journal behind a filesystem sync barrier. An interrupted journal is detected and repaired on recovery by restoring the last coherent record, rather than leaving a half-written database entry.

RLPM runs the same transaction lifecycle you know from libalpm, in five stages:

┌─────────────────────────────────────────────────────────────┐
│ 1. Load and verify databases │
│ • Local and sync snapshots under the held lock │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ 2. Resolve the plan and ask questions │
│ • Dependencies, conflicts, providers, sizes │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ 3. Acquire and verify package archives │
│ • Cache acquisition, checksums, sealed signatures │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ 4. Preflight the filesystem, run pre-transaction hooks │
│ • Conflicts, backups, disk space, then pre hooks │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ 5. Commit payload and records, then finish │
│ • Removals, additions, ldconfig, post hooks │
└─────────────────────────────────────────────────────────────┘

The plan is reviewed before anything runs: dependency resolution, provider questions and the full list of additions and removals are settled while you are still being prompted, and the commit step only executes the reviewed plan.

There is no full filesystem rollback. If a mutation fails, RLPM stops at that step and reports completed and remaining work. An interrupted record-publication journal can require explicit recovery before RLPM will read the database again. Journal recovery restores coherent records; it does not undo payload changes or scriptlet effects. See execution and recovery for the distinction.

Version comparison targets libalpm’s C-locale ordering and is checked against fixed expectations recorded from a pinned reference build. Repository priority and other resolution policy also affect upgrade and downgrade selection. See version comparison and dependency resolution.


Engine selection is also a build-time choice for packagers and embedders:

The CLI, the TUI and Shelly.PackageManager all accept the same build option. The default build contains both engines and starts on libalpm; -Dlibalpm=false produces an RLPM-only build:

Terminal window
(cd Shelly.Cli.Zig && zig build -Dlibalpm=false)

RLPM-only source packages omit the pacman build and runtime dependency that the default variant carries. To choose distribution path defaults at build time, specify -Dpath-profile=pacman (the default) or -Dpath-profile=devario.

  • Per-command config (--config): Select another native configuration file (for example, --config /etc/shelly.conf), before or after the command. The path is made absolute and retained across privilege elevation and child processes.

  • Hook directory policy (HookDirMode): In pacman.conf or shelly.conf, setting HookDirMode = Replace tells the backend to use only explicit HookDir entries instead of appending them to system defaults (HookDirMode = Append).

  • Distribution path profiles (-Dpath-profile): Build-time path selection is independent of backend selection. Builds default to the pacman profile. Independent distributions that do not ship pacman select the devario profile:

    • pacman profile (default): /etc/pacman.conf, /etc/pacman.d/, /var/lib/pacman/, /var/cache/pacman/pkg/, /etc/pacman.d/gnupg/, /usr/share/pacman/keyrings/, and /usr/share/libalpm/hooks/.
    • devario profile (pacman-less): /etc/shelly.conf, /etc/shelly.d/, /var/lib/shelly/, /var/cache/shelly/pkg/, /etc/shelly.d/gnupg/, /usr/share/shelly/keyrings/, and /usr/share/rlpm/hooks/.

    /etc/shelly.conf uses the exact same INI format and directive syntax as pacman.conf ([options], SigLevel, repository sections, and mirror URLs).


libalpm is the initial default in dual-backend builds; RLPM-only builds default to RLPM. Compatibility is tracked in an in-repository ledger, and complete libalpm parity remains unfinished. Focused reference, GPG, namespace, and interoperability tests cover particular contracts; they do not certify every production workflow or performance case.

Changing the backend affects subsequent operations only. It neither undoes a partial transaction nor recovers an interrupted journal, and it cannot select an engine omitted from the binary. Neither engine is selected automatically as a fallback after failure.


These guides explain the engine’s behavior and library contracts beyond the overview:

Guide What it covers
Databases and caches Local records, tar and SQLite repositories, query policy, reloads, and reference lifetimes.
Version comparison Epochs, releases, permissive metadata, ordering, and comparison limits.
Dependency resolution Repository priority, providers, conflicts, removal, upgrades, and reviewable plans.
Transactions and locks Transaction states, shared locking, stale-state checks, questions, and cancellation.
Integrity and signatures Signing policy, checksums, key trust, consented import, and sealed archive bytes.
Downloads and refresh Verified caches, concurrency, mirror retries, publication, and worker privileges.
Filesystem preflight Archive validation, file ownership, conflicts, backups, path confinement, and space.
Hooks and scriptlets Discovery, matching, action order, network controls, and failure outcomes.
Execution and recovery Payload writes, durability barriers, local-record journals, and partial failures.

© 2026 Seafoam LabsShelly Chel