Skip to main content

appjail logo

appjail

PyPI Python CI License: MPL-2.0 REUSE

A small command-line manager for AppImages that runs every app inside a bubblewrap sandbox. It works in user mode (no sudo), supports GUI apps under Wayland, and treats the software you install as untrusted.

appjail is young software: read the limits before relying on it.

Why

Package managers for AppImages typically download a release and run it with your full privileges. appjail is built around the opposite assumptions:

  • Declarative manifests, no remote code. An app is a small TOML file: source, pinned hash, permissions. Nothing fetched from the network is ever executed or sourced, and appjail has no self-update.
  • You name the source. You give a GitHub owner/repo. That repository is the only place the app is fetched from, whatever an AppImage's embedded update information says.
  • Downloads are jailed. The download, the extraction and the verification tools run in throwaway sandboxes with only the access they need (a private network namespace for downloads, none for extraction).
  • Everything is denied by default. An app starts with a private home and a restricted Wayland socket. Network, directories, D-Bus names, portals and the rest are explicit grants you can inspect and change.
  • Updates are reviewed. A new release waits in quarantine (7 days by default), is shown to you as a diff, and is blocked outright if its evidence gets weaker or the repository changes owner.

Requirements

  • Linux on x86-64 with unprivileged user namespaces. Tested on Debian 13 and Ubuntu 26.04; older systems are not supported.
  • Python 3.12 or newer (tested with 3.13 on Debian 13 and 3.14 on Ubuntu 26.04; 3.12 is the declared minimum and is not run by the tests). appjail uses only the standard library.
  • bubblewrap and squashfs-tools (unsquashfs) are required.
  • Recommended: passt (the pasta command) for apps with network access, xdg-dbus-proxy for D-Bus filtering, xwayland for X11-only apps, git for the manifest history and the search index.
  • A Wayland compositor for GUI apps. Apps that need X11 run on a private Xwayland inside their own sandbox; the host X11 socket is never shared.

On Debian or Ubuntu:

sudo apt install bubblewrap squashfs-tools passt xdg-dbus-proxy xwayland git

Install

appjail is a Python package with no dependencies. Install it as a command with pipx or pip:

pipx install appjail    # or: pip install --user appjail
appjail --help

Or from a clone (pip downloads only the build backend, Hatchling):

git clone <repository-url> appjail
cd appjail
pipx install .          # or: pip install --user .
appjail --help

To try it without installing, run it from the clone: PYTHONPATH=src python3 -m appjail --help. appjail never updates itself: upgrade with pipx upgrade appjail (or git pull and reinstall) after reading what changed.

Data lives in ~/.local/share/appjail (images, extracted trees, private homes, staging) and ~/.config/appjail (manifests, tracked in a small local git repository). APPJAIL_DATA_DIR and APPJAIL_CONFIG_DIR override them.

Quick start

appjail add skyjake/lagrange     # review the repository, pick the asset, download, pin, extract
appjail run lagrange             # run it in its sandbox
appjail list                     # installed apps, tiers and permissions
appjail update                   # check every app for a newer release
appjail remove lagrange          # delete the app and its private home

add also takes --tag TAG (a specific release instead of the latest), --name, --prerelease, and the permission options below at install time (--allow, --dir, --dbus, --dbus-own, --portal, --display xwayland, --no-audio, --no-gpu). run takes --dir PATH[:ro|:rw] and --file PATH[:rw] for one run only, and passes everything after -- to the app (appjail run lagrange -- --help). remove and perms ask for confirmation unless given --yes.

add shows the repository owner, account age, stars and fork or archive status before it downloads anything, and asks if several release assets match. By default an app gets audio and GPU access and nothing else.

Permissions

appjail perms lagrange                              # show
appjail perms lagrange --allow network              # outbound network only (see below)
appjail perms lagrange --add-dir ~/Documents:rw     # a directory, read-only unless :rw
appjail perms lagrange --add-dbus org.example.Name  # talk to one session-bus name through a filtering proxy
appjail perms lagrange --add-dbus-own org.example.Name  # own a session-bus name (MPRIS player, single instance)
appjail perms lagrange --allow portals              # desktop portals (file chooser, notifications, ...)
appjail perms lagrange --deny network --rm-dir ~/Documents   # take grants back (also --rm-dbus, --rm-dbus-own, --rm-portal)
appjail portals lagrange                           # files it was granted through the file chooser
appjail portals lagrange --revoke-all               # take those grants back (also --revoke DOC_ID)
appjail run lagrange --file ~/report.pdf            # one file, for this run only

Giving one file. run --file PATH (add :rw to allow writing) makes just that file visible, not the rest of its directory, and appends its path inside the sandbox to the app's arguments. It applies to that run only and is never saved in the manifest; for a standing grant use a directory, or portals and the file chooser. Credentials, browser profiles and the like are refused as for directories, and shell startup files are never writable. Apps that save by writing a temporary file and renaming it fail on :rw.

Permission Effect
network Outbound internet through a private network namespace made by pasta. The app cannot reach services on your machine's localhost or its own addresses. Needs passt; without it the app is refused rather than put on the host network.
network_host The host's own network, including localhost services. For apps that really need them.
audio, gpu On by default; --no-audio and --no-gpu at add time, or --deny later.
portals The safe set of desktop portals; privileged ones are listed per app with --add-portal.
userns Nested user namespaces (Chromium and Electron sandboxes). Weakens isolation, off by default.
dirs Directories mapped into the private home. Credentials, browser profiles, shell startup files and system trees are refused.
dbus, dbus_own Session-bus names the app may talk to or own, through xdg-dbus-proxy. Desktop namespaces are refused.
display wayland (default) or xwayland.

Every sandbox also drops all capabilities, has its own PID, IPC and UTS namespaces, a read-only system, a minimal /dev, a generated /etc/passwd, and a seccomp filter. The extracted app tree is hashed and checked at every launch.

How trust works

Each app is pinned by hash and carries an evidence tier that update will never silently lower:

Tier Evidence
1 A verified GitHub build attestation (Sigstore), checked with a pinned gh that you install on request (appjail tools install gh).
3 The SHA-256 digest GitHub publishes for the release asset.
4 Trust on first use: appjail shows you the hash it computed.

Tier 2 (GPG signatures) is not implemented. Most AppImages will land on tier 3 or 4, which proves "unchanged and from the same account", not "safe".

appjail update re-resolves the pinned repository, downloads the candidate in the jail, verifies it, and shows you the version, the hashes, the tier and the release link. A release must be seen for the quarantine period before it can be installed (--quarantine DAYS). For tier 4 apps appjail records the candidate's hash when it is first seen and refuses the install if the content changed in the meantime. A change of repository owner or a drop in tier blocks the update. --channel stable|prerelease|pinned chooses which releases an app follows.

Searching

appjail can search the AM application list as a source of suggestions. It reads that data as plain text only and never runs anything from it.

appjail index update      # fetch the list in a jail, review the changes, pin it
appjail index rollback    # go back to the previous snapshot
appjail index status      # show the pinned snapshot
appjail search editor     # candidates only; installing is always `appjail add owner/repo`

search shows 20 results; use --limit N or --all for more.

Other commands

  • appjail history [app] [--limit N]: every change to the manifests, from the local git repository. appjail run warns if a manifest was edited outside appjail.
  • appjail tools status|install NAME: helper binaries whose URL, size and hash are pinned in appjail's source (gh for attestations, dwarfs for DwarFS-based AppImages).
  • Set GITHUB_TOKEN to raise the GitHub API rate limit. It is only ever sent to api.github.com.

Limits

  • Not audited, not a security boundary you should bet on yet. bwrap and the Linux kernel do the isolation; appjail configures them conservatively but has had no external review.
  • Only GitHub repositories are supported. Apps hosted elsewhere appear in search but cannot be added.
  • A sandboxed app can still do whatever its grants allow. An app with network can reach other machines on your LAN.
  • Granting a directory, a portal or a D-Bus name widens what a compromised app can do.
  • x86-64 only. The GNOME and KDE portal backends have not been tested (the GTK backend and a real X11-only AppImage have, under headless weston; the portal dialogs themselves are not driven). Real internet IPv6 under pasta is untested; IPv6 forwarding is tested in a fake network.
  • Stopping appjail run with SIGINT, SIGTERM or SIGHUP stops the whole jail, including pasta. If appjail is killed with SIGKILL, an app with network is stopped about a second later by a watcher inside its network namespace (a small empty directory appjail-life-* stays in the runtime directory).
  • The audio permission includes the microphone.

The full design, the reasoning behind each choice and the verification notes are in DESIGN.md.

Development

The code is stdlib-only Python under src/appjail/; tests are plain pytest. Many tests start real sandboxes and need a Linux machine with bubblewrap, unsquashfs, pasta, a headless Wayland compositor (weston) and the other packages in tests/vm/packages.txt.

APPJAIL_INTEGRATION=1 python3 -m pytest

Without APPJAIL_INTEGRATION=1 the tests that need real sandboxes are skipped. Tests that download from the network are opt-in (APPJAIL_E2E=1, APPJAIL_LIVE=1). The project's own workflow runs the suite inside a disposable QEMU guest built from tests/vm/ so that downloads and sandboxed apps never run on the developer's machine; see CLAUDE.md for the details. GitHub Actions runs the full suite and a REUSE compliance check on every push, and a separate workflow runs every opt-in suite (real downloads) on demand and weekly.

License

MPL-2.0. The repository follows the REUSE specification.

Metadata

Release files for appjail 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for appjail 0.1.2
File Size Uploaded Provenance
appjail-0.1.2.tar.gz 72.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for appjail 0.1.2
File Interpreter ABI Platform
appjail-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 156.9 kB

Release files / appjail-0.1.2.tar.gz

Download URL appjail-0.1.2.tar.gz
Size 72.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f5efa47d28a148a899b258bab3767971c331158a916a8ff6a9df0893260cf325
BLAKE2b-256 checksum
How to use checksums
7d761c1153f7c36c0744355cc2e8d86fd874458662a0465a0ae006ddd993eefe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / appjail-0.1.2-py3-none-any.whl

Download URL appjail-0.1.2-py3-none-any.whl
Size 84.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ae935259b8cef132902f0e669bb9c24bfb65dd67dce3bc553acd06a6c54e072b
BLAKE2b-256 checksum
How to use checksums
77b6266e067fd10f026d3e4daab5ea75b39219a453a80fbe160f5497822227d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for appjail 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for appjail-0.1.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page