appjail 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {appjail-0.2.0 → appjail-0.2.2}/PKG-INFO +112 -81
- {appjail-0.2.0 → appjail-0.2.2}/README.md +111 -80
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/amsource.py +13 -5
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/catalog.py +72 -7
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_index.py +20 -7
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_updateimage.py +1 -2
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/desktop.py +2 -6
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/index.py +2 -2
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/indexstore.py +28 -24
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/jail.py +2 -3
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/manifest.py +2 -5
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/portal.py +5 -10
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/runner.py +59 -41
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/runtimejail.py +29 -17
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/store.py +16 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/updater.py +1 -5
- {appjail-0.2.0 → appjail-0.2.2}/.gitignore +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/LICENSES/MPL-2.0.txt +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/REUSE.toml +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/pyproject.toml +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/__init__.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/__main__.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/appimage.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/attest.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cli.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_add.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_addimage.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_apps.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_desktop.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_open.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_portals.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/cmd_update.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/console.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/dbusproxy.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/fetcher.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/github.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/grants.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/history.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/imagesrc.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/permargs.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/seccomp.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/services.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/snappy.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/textsafe.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/tools.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/valid.py +0 -0
- {appjail-0.2.0 → appjail-0.2.2}/src/appjail/wayland.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: appjail
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Sandboxed AppImage manager built on bubblewrap
|
|
5
5
|
License-Expression: MPL-2.0
|
|
6
6
|
License-File: LICENSES/MPL-2.0.txt
|
|
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
|
|
30
30
|
A small command-line manager for [AppImages](https://appimage.org/) that runs every app inside a
|
|
31
31
|
[bubblewrap](https://github.com/containers/bubblewrap) sandbox. It works in user mode (no sudo), supports GUI apps
|
|
32
|
-
|
|
32
|
+
on Wayland (and X11-only apps through a private Xwayland), and treats the software you install as untrusted.
|
|
33
33
|
|
|
34
34
|
*appjail is young software: read the [limits](#limits) before relying on it.*
|
|
35
35
|
|
|
@@ -40,8 +40,8 @@ is built around the opposite assumptions:
|
|
|
40
40
|
|
|
41
41
|
- **Declarative manifests, no remote code.** An app is a small TOML file: source, pinned hash, permissions. Nothing
|
|
42
42
|
fetched from the network is ever executed or sourced, and appjail has no self-update.
|
|
43
|
-
- **You name the source.** You give a GitHub `owner/repo`, or an AppImage file or URL
|
|
44
|
-
|
|
43
|
+
- **You name the source.** You give a GitHub `owner/repo`, or an AppImage file or URL. That is the only place the
|
|
44
|
+
app is fetched from, whatever an AppImage's embedded update information says.
|
|
45
45
|
- **Downloads are jailed.** The download, the extraction and the verification tools run in throwaway sandboxes
|
|
46
46
|
with only the access they need (a private network namespace for downloads, none for extraction).
|
|
47
47
|
- **Everything is denied by default.** An app starts with a private home and a restricted Wayland socket. Network,
|
|
@@ -53,13 +53,16 @@ is built around the opposite assumptions:
|
|
|
53
53
|
|
|
54
54
|
- Linux on x86-64 with unprivileged user namespaces. Tested on Debian 13 and Ubuntu 26.04; older systems are
|
|
55
55
|
not supported.
|
|
56
|
-
- 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
|
|
57
|
-
run by the tests). appjail uses only the standard library.
|
|
58
|
-
- `bubblewrap` and `squashfs-tools` (`unsquashfs`)
|
|
59
|
-
-
|
|
60
|
-
`
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
- 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
|
|
57
|
+
not run by the tests). appjail uses only the standard library.
|
|
58
|
+
- Required: `bubblewrap` and `squashfs-tools` (`unsquashfs`).
|
|
59
|
+
- Needed for specific features:
|
|
60
|
+
- `passt` (the `pasta` command): apps with the `network` permission, which are refused without it. Also gives
|
|
61
|
+
the download jails a private network namespace; without it they use the host network and print a warning.
|
|
62
|
+
- `xdg-dbus-proxy`: D-Bus filtering and portals.
|
|
63
|
+
- `xwayland`: X11-only apps.
|
|
64
|
+
- `git`: the search index (`index update`) and the manifest history.
|
|
65
|
+
- A Wayland compositor for GUI apps. The host X11 socket is never shared.
|
|
63
66
|
|
|
64
67
|
On Debian or Ubuntu:
|
|
65
68
|
|
|
@@ -80,14 +83,15 @@ appjail --help
|
|
|
80
83
|
Or from a clone (pip downloads only the build backend, Hatchling):
|
|
81
84
|
|
|
82
85
|
```sh
|
|
83
|
-
git clone
|
|
86
|
+
git clone https://github.com/alberto743/appjail.git
|
|
84
87
|
cd appjail
|
|
85
88
|
pipx install . # or: pip install --user .
|
|
86
|
-
appjail --help
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
To try it without installing, run it from the clone: `PYTHONPATH=src python3 -m appjail --help`.
|
|
90
92
|
appjail never updates itself: upgrade with `pipx upgrade appjail` (or `git pull` and reinstall) after reading what changed.
|
|
93
|
+
`appjail version` (or `--version`) prints the installed version, which comes from the git tag at build time (`0.2.0` on a
|
|
94
|
+
release, `0.2.1.dev4+g1a2b3c4` four commits after it) and is `unknown` when run from a source tree that is not installed.
|
|
91
95
|
|
|
92
96
|
Data lives in `~/.local/share/appjail` (images, extracted trees, private homes, staging) and `~/.config/appjail`
|
|
93
97
|
(manifests, tracked in a small local git repository). `APPJAIL_DATA_DIR` and `APPJAIL_CONFIG_DIR` override them.
|
|
@@ -102,16 +106,26 @@ appjail update # check every app for a newer release
|
|
|
102
106
|
appjail remove lagrange # delete the app and its private home
|
|
103
107
|
```
|
|
104
108
|
|
|
105
|
-
`
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
`remove` and `perms` ask for confirmation unless given `--yes`. `appjail COMMAND --help` lists every option.
|
|
110
|
+
`GITHUB_TOKEN`, if set, raises the GitHub API rate limit; it is only ever sent to `api.github.com`.
|
|
111
|
+
|
|
112
|
+
## Installing apps
|
|
113
|
+
|
|
114
|
+
**From a GitHub release.** `appjail add owner/repo` shows the repository owner, account age, stars and fork or archive
|
|
115
|
+
status before it downloads anything, and asks if several release assets match. By default an app gets audio and GPU
|
|
116
|
+
access and nothing else.
|
|
117
|
+
|
|
118
|
+
| Option | Effect |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `--tag TAG` | A specific release instead of the latest; the app is then *pinned* and `update` leaves it alone. |
|
|
121
|
+
| `--prerelease` | Follow pre-releases. |
|
|
122
|
+
| `--name NAME` | The app's name (default: from the project). |
|
|
123
|
+
| `--allow`, `--dir`, `--dbus`, `--dbus-own`, `--portal`, `--display`, `--no-audio`, `--no-gpu` | Permissions at install time (see [Permissions](#permissions)). |
|
|
124
|
+
| `--desktop [--mime TYPE]` | Also create a menu entry (see [Menu entry](#menu-entry-and-opening-files)). |
|
|
110
125
|
|
|
111
|
-
`
|
|
112
|
-
asks if several release assets match. By default an app gets audio and GPU access and nothing else.
|
|
126
|
+
`run` passes everything after `--` to the app (`appjail run lagrange -- --help`).
|
|
113
127
|
|
|
114
|
-
**
|
|
128
|
+
**From a file or URL.** For apps that are not GitHub releases, give the AppImage itself:
|
|
115
129
|
|
|
116
130
|
```sh
|
|
117
131
|
appjail add --file ~/Downloads/Tool-x86_64.AppImage [--name tool] [--sha256 HEX]
|
|
@@ -119,49 +133,60 @@ appjail add --url https://example.org/dl/Tool-x86_64.AppImage [--name tool] [--s
|
|
|
119
133
|
appjail update tool --file ~/Downloads/Tool-new.AppImage # an app added from a file
|
|
120
134
|
```
|
|
121
135
|
|
|
122
|
-
Nothing identifies who published such a file, so these apps are tier 5 (trust on first use) or tier 4 when you pass
|
|
123
|
-
hash the publisher gives with `--sha256` (a mismatch aborts). A URL must be https and is downloaded in the jail
|
|
124
|
-
at most unless `--max-size`); redirects may go to any host, and a redirect through plain http asks for an extra
|
|
136
|
+
Nothing identifies who published such a file, so these apps are tier 5 (trust on first use), or tier 4 when you pass
|
|
137
|
+
the hash the publisher gives with `--sha256` (a mismatch aborts). A URL must be https and is downloaded in the jail
|
|
138
|
+
(1 GiB at most unless `--max-size`); redirects may go to any host, and a redirect through plain http asks for an extra
|
|
125
139
|
confirmation unless `--sha256` matches. `update` of a URL app asks the server first with a conditional request and a
|
|
126
140
|
few small range requests, and downloads the whole file only when something changed.
|
|
127
141
|
|
|
128
|
-
|
|
142
|
+
## Permissions
|
|
143
|
+
|
|
144
|
+
Everything is denied unless granted. Show or change an app's permissions with `perms`:
|
|
129
145
|
|
|
130
146
|
```sh
|
|
131
|
-
appjail perms lagrange
|
|
132
|
-
appjail perms lagrange --allow network
|
|
133
|
-
appjail perms lagrange --add-dir ~/Documents:rw
|
|
134
|
-
appjail perms lagrange --add-dbus org.example.Name
|
|
135
|
-
appjail perms lagrange --add-dbus-own org.example.Name
|
|
136
|
-
appjail perms lagrange --allow portals
|
|
147
|
+
appjail perms lagrange # show
|
|
148
|
+
appjail perms lagrange --allow network # outbound network only (see the table)
|
|
149
|
+
appjail perms lagrange --add-dir ~/Documents:rw # a directory, read-only unless :rw
|
|
150
|
+
appjail perms lagrange --add-dbus org.example.Name # talk to one session-bus name through a filtering proxy
|
|
151
|
+
appjail perms lagrange --add-dbus-own org.example.Name # own a session-bus name (MPRIS player, single instance)
|
|
152
|
+
appjail perms lagrange --allow portals # desktop portals (file chooser, notifications, ...)
|
|
137
153
|
appjail perms lagrange --deny network --rm-dir ~/Documents # take grants back (also --rm-dbus, --rm-dbus-own, --rm-portal)
|
|
138
|
-
appjail portals lagrange
|
|
139
|
-
appjail portals lagrange --revoke-all
|
|
140
|
-
appjail run lagrange --file ~/report.pdf # one file, for this run only
|
|
154
|
+
appjail portals lagrange # files it was granted through the file chooser
|
|
155
|
+
appjail portals lagrange --revoke-all # take those grants back (also --revoke DOC_ID)
|
|
141
156
|
```
|
|
142
157
|
|
|
143
|
-
**Giving one file.** `run --file PATH` (add `:rw` to allow writing) makes just that file visible, not the rest of its
|
|
144
|
-
directory, and appends its path inside the sandbox to the app's arguments. It applies to that run only and is never
|
|
145
|
-
saved in the manifest; for a standing grant use a directory, or `portals` and the file chooser. Credentials, browser
|
|
146
|
-
profiles and the like are refused as for directories, and shell startup files are never writable. Apps that save by
|
|
147
|
-
writing a temporary file and renaming it fail on `:rw`.
|
|
148
|
-
|
|
149
158
|
| Permission | Effect |
|
|
150
159
|
|---|---|
|
|
151
160
|
| `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. |
|
|
152
|
-
| `network_host` | The host's own network, including localhost services. For apps that really need them. |
|
|
153
|
-
| `audio`, `gpu` | On by default; `--no-audio` and `--no-gpu` at `add` time, or `--deny` later. |
|
|
161
|
+
| `network_host` | The host's own network, including localhost services (`--allow network_host`, which implies `network`). For apps that really need them. |
|
|
162
|
+
| `audio`, `gpu` | On by default; `--no-audio` and `--no-gpu` at `add` time, or `--deny` later. Audio includes the microphone. |
|
|
154
163
|
| `portals` | The safe set of desktop portals; privileged ones are listed per app with `--add-portal`. |
|
|
155
164
|
| `userns` | Nested user namespaces (Chromium and Electron sandboxes). Weakens isolation, off by default. |
|
|
156
|
-
| `dirs` | Directories mapped into the private home. Credentials, browser profiles, shell startup files and system trees are refused. |
|
|
157
|
-
| `
|
|
165
|
+
| `dirs` | Directories mapped into the private home (`--dir` / `--add-dir`). Credentials, browser profiles, shell startup files and system trees are refused. |
|
|
166
|
+
| `dbus_filter`, `dbus_own` | Session-bus names the app may talk to (`--dbus`) or own (`--dbus-own`), through `xdg-dbus-proxy`. Desktop namespaces are refused. |
|
|
158
167
|
| `display` | `wayland` (default) or `xwayland`. |
|
|
159
168
|
|
|
169
|
+
**Temporary grants.** `run APP --dir PATH[:ro|:rw]` and `run APP --file PATH[:rw]` grant access for that run only and are never
|
|
170
|
+
saved. `--file` makes just that one file visible, not the rest of its directory, and appends its path inside the sandbox to
|
|
171
|
+
the app's arguments. Credentials, browser profiles and the like are refused as for directories, and shell startup files are
|
|
172
|
+
never writable. Apps that save by writing a temporary file and renaming it fail on `:rw`. For a standing grant use a
|
|
173
|
+
directory, or the portal file chooser.
|
|
174
|
+
|
|
160
175
|
Every sandbox also drops all capabilities, has its own PID, IPC and UTS namespaces, a read-only system, a minimal
|
|
161
176
|
`/dev`, a generated `/etc/passwd`, and a seccomp filter. The extracted app tree is hashed and checked at every
|
|
162
177
|
launch.
|
|
163
178
|
|
|
164
|
-
##
|
|
179
|
+
## Menu entry and opening files
|
|
180
|
+
|
|
181
|
+
`appjail desktop NAME [--mime TYPE]... [--mime-all] [--rw]` (also `add --desktop`) creates a menu entry that starts the
|
|
182
|
+
app through appjail. appjail generates it itself; the AppImage's own `.desktop` is never used. File types are opt-in:
|
|
183
|
+
only the ones you pick show under "Open with", and appjail never makes the app a default. Opening a file passes just
|
|
184
|
+
that file to the app, read-only unless `--rw` (`appjail open NAME -- FILE...`). `desktop --remove` and `--prune` delete
|
|
185
|
+
entries; `remove` does it too.
|
|
186
|
+
|
|
187
|
+
## Trust and updates
|
|
188
|
+
|
|
189
|
+
### Evidence tiers
|
|
165
190
|
|
|
166
191
|
Each app is pinned by hash and carries an *evidence tier* that `update` will never silently lower:
|
|
167
192
|
|
|
@@ -176,22 +201,33 @@ Tier 2 (GPG signatures) is not implemented. Most AppImages will land on tier 3 t
|
|
|
176
201
|
GitHub, "from the same account"), not "safe". `update APP --sha256 HEX` earns tier 4 for a tier 5 app when the hash
|
|
177
202
|
matches, and a tier 4 URL or file app refuses new content that comes without one.
|
|
178
203
|
|
|
179
|
-
|
|
180
|
-
|
|
204
|
+
### Updating
|
|
205
|
+
|
|
206
|
+
`appjail update [APP]` re-resolves the pinned repository, downloads the candidate in the jail, verifies it, and shows you
|
|
207
|
+
the version, the hashes, the tier and the release link. A release must be seen for the quarantine period before it can
|
|
181
208
|
be installed (`--quarantine DAYS`). For tier 4 apps appjail records the candidate's hash when it is first seen and refuses
|
|
182
209
|
the install if the content changed in the meantime. A change of repository owner or a drop in tier blocks the update.
|
|
183
210
|
`--channel stable|prerelease|pinned` chooses which releases an app follows.
|
|
184
211
|
|
|
185
|
-
What `update` prints per app:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
What `update` prints per app:
|
|
213
|
+
|
|
214
|
+
- *up to date*;
|
|
215
|
+
- *waiting*: a candidate was seen and its quarantine has not ended; run `update` again later, no action needed;
|
|
216
|
+
- *blocked*: the owner changed, the evidence got weaker, or the content changed during the quarantine; nothing was
|
|
217
|
+
installed and the message says why;
|
|
218
|
+
- *declined*: you answered no;
|
|
219
|
+
- *pinned*: the app follows no releases.
|
|
220
|
+
|
|
221
|
+
The exit status is 1 if any app was blocked or failed. A URL app's candidate is kept on disk during the quarantine and
|
|
222
|
+
installed from there (`--verify` downloads it again first); a file app has no quarantine, you hold the bytes.
|
|
223
|
+
|
|
224
|
+
Every change to the manifests is committed to a local git repository (`appjail history [APP] [--limit N]` lists them), and
|
|
225
|
+
`appjail run` warns if a manifest was edited outside appjail.
|
|
190
226
|
|
|
191
227
|
## Searching
|
|
192
228
|
|
|
193
|
-
`search` looks through **catalogs**: lists of apps with, where known, the
|
|
194
|
-
is only a suggestion; installing is always `appjail add owner/repo` (or `--
|
|
229
|
+
`search` looks through **catalogs**: lists of apps with, where known, the project each one comes from. A hit
|
|
230
|
+
is only a suggestion; installing is always `appjail add owner/repo` (or `--url`), after you have checked
|
|
195
231
|
the owner. Catalogs are untrusted data: appjail parses them strictly, strips terminal escape sequences from every
|
|
196
232
|
text, and never runs anything from them.
|
|
197
233
|
|
|
@@ -213,7 +249,8 @@ its repository in a network-only jail (only the list and the install scripts, no
|
|
|
213
249
|
each script as text to find the `SITE=` line, and keeps the result as a catalog. The scripts themselves are not
|
|
214
250
|
kept. Sources you add yourself provide a catalog directly. Every update shows the number of apps, the apps added and
|
|
215
251
|
removed, and every app whose repository changed, with a loud warning when the owner changed, and asks before it
|
|
216
|
-
replaces the catalog. One previous catalog is kept per source for `index rollback
|
|
252
|
+
replaces the catalog. One previous catalog is kept per source for `index rollback`; sources are stored in
|
|
253
|
+
`~/.local/share/appjail/index/`.
|
|
217
254
|
|
|
218
255
|
**Catalog format.** One UTF-8 JSON document, at most 16 MiB and 50,000 apps:
|
|
219
256
|
|
|
@@ -224,7 +261,11 @@ replaces the catalog. One previous catalog is kept per source for `index rollbac
|
|
|
224
261
|
"origin": "https://example.org/my-apps",
|
|
225
262
|
"revision": "2026-10-22",
|
|
226
263
|
"apps": [
|
|
227
|
-
{"name": "lagrange", "description": "A Gemini browser", "source": "skyjake/lagrange"},
|
|
264
|
+
{"name": "lagrange", "description": "A Gemini browser", "source": "github:skyjake/lagrange"},
|
|
265
|
+
{"name": "tool-a", "source": "codeberg:user/tool-a"},
|
|
266
|
+
{"name": "tool-b", "source": "gitlab:gitlab.example.org/group/sub/tool-b"},
|
|
267
|
+
{"name": "tool-c", "source": "forgejo:git.example.net/user/tool-c"},
|
|
268
|
+
{"name": "tool-d", "source": "url:https://example.com/dl/tool-d.AppImage"},
|
|
228
269
|
{"name": "tool", "description": "Hosted elsewhere", "note": "resolve manually: https://example.org/tool"}
|
|
229
270
|
]
|
|
230
271
|
}
|
|
@@ -239,12 +280,14 @@ replaces the catalog. One previous catalog is kept per source for `index rollbac
|
|
|
239
280
|
| `apps` | yes | A list of objects, with unique `name`s. |
|
|
240
281
|
| `apps[].name` | yes | The name to search for: letters, digits, `.`, `_`, `+`, `-`; starts with a letter or digit; up to 81 characters. |
|
|
241
282
|
| `apps[].description` | no | One line of text, up to 300 characters. |
|
|
242
|
-
| `apps[].source` | no |
|
|
283
|
+
| `apps[].source` | no | Where the app comes from, as `kind:target`: `github:owner/repo`, `gitlab:HOST/group/.../project`, `forgejo:HOST/owner/repo`, `codeberg:owner/repo` (shorthand for `forgejo:codeberg.org/...`), `url:https://...`. The host is always explicit (`gitlab:gitlab.com/...`), without port or credentials. Anything else, including `file:` and a bare `owner/repo`, is refused: put other locations in `note`. |
|
|
243
284
|
| `apps[].note` | no | Why there is no usable `source`, or a flag such as a repackager. Up to 200 characters. |
|
|
244
285
|
|
|
245
286
|
Unknown keys are errors, so a catalog means the same thing to every version that accepts it. A catalog that breaks
|
|
246
287
|
a rule is refused as a whole and nothing changes. appjail writes the catalog it validated, not the downloaded bytes.
|
|
247
|
-
|
|
288
|
+
`search` offers `appjail add` for `github:` and `url:` sources only; GitLab and Forgejo sources are shown as information
|
|
289
|
+
until appjail can install from those forges. An update that moves an app to another kind, host or owner is flagged as an
|
|
290
|
+
owner change.
|
|
248
291
|
|
|
249
292
|
## Command reference
|
|
250
293
|
|
|
@@ -254,7 +297,7 @@ Sources are listed in `~/.local/share/appjail/index/`.
|
|
|
254
297
|
| `add --file PATH` / `add --url URL` | Install an AppImage you give yourself; `--sha256 HEX` confirms it (tier 4). |
|
|
255
298
|
| `run APP [-- ARGS]` | Run an app; `--dir PATH[:ro\|:rw]` and `--file PATH[:rw]` grant access for this run only. |
|
|
256
299
|
| `list` | Installed apps, versions, sources, tiers, permissions and updates seen. |
|
|
257
|
-
| `update [APP]` | Check, quarantine and install new versions (see
|
|
300
|
+
| `update [APP]` | Check, quarantine and install new versions (see [Updating](#updating)). |
|
|
258
301
|
| `perms APP` | Show or change an app's permissions. |
|
|
259
302
|
| `portals APP` | List or revoke the files an app was granted through the file chooser. |
|
|
260
303
|
| `desktop APP` / `open APP -- FILE...` | Menu entry, and opening files with an app. |
|
|
@@ -262,25 +305,9 @@ Sources are listed in `~/.local/share/appjail/index/`.
|
|
|
262
305
|
| `history [APP]` | Recorded changes to the manifests. |
|
|
263
306
|
| `search WORDS` | Suggestions from the catalogs. |
|
|
264
307
|
| `index add\|remove\|update\|rollback\|status` | Manage the catalogs `search` reads (the built-in AM list and any you add). |
|
|
265
|
-
| `tools status\|install NAME` |
|
|
308
|
+
| `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). |
|
|
266
309
|
| `version` | The installed version. |
|
|
267
310
|
|
|
268
|
-
`appjail COMMAND --help` lists every option.
|
|
269
|
-
|
|
270
|
-
## Other commands
|
|
271
|
-
|
|
272
|
-
- `appjail history [app] [--limit N]`: every change to the manifests, from the local git repository. `appjail run` warns if a
|
|
273
|
-
manifest was edited outside appjail.
|
|
274
|
-
- `appjail tools status|install NAME`: helper binaries whose URL, size and hash are pinned in appjail's source (`gh` for
|
|
275
|
-
attestations, `dwarfs` for DwarFS-based AppImages).
|
|
276
|
-
- `appjail desktop NAME [--mime TYPE]... [--mime-all] [--rw]` (also `add --desktop`): a menu entry that starts the app through
|
|
277
|
-
appjail, generated by appjail (the AppImage's own `.desktop` is never used). File types are opt-in: only the ones you pick
|
|
278
|
-
show under "Open with", and appjail never makes the app a default. Opening a file passes just that file to the app, read-only
|
|
279
|
-
unless `--rw` (`appjail open NAME -- FILE...`). `--remove` and `--prune` delete entries; `remove` does it too.
|
|
280
|
-
- `appjail version` (or `--version`): the installed version. It comes from the git tag at build time (`0.2.0` on a release, `0.2.1.dev4+g1a2b3c4` four
|
|
281
|
-
commits after it); it prints `unknown` when run from a source tree that is not installed.
|
|
282
|
-
- Set `GITHUB_TOKEN` to raise the GitHub API rate limit. It is only ever sent to `api.github.com`.
|
|
283
|
-
|
|
284
311
|
## Troubleshooting
|
|
285
312
|
|
|
286
313
|
| Message or symptom | Cause and fix |
|
|
@@ -288,7 +315,7 @@ Sources are listed in `~/.local/share/appjail/index/`.
|
|
|
288
315
|
| bwrap fails with `Operation not permitted` or `setting up uid map: Permission denied` | Unprivileged user namespaces are blocked. On Ubuntu, AppArmor can restrict them (`kernel.apparmor_restrict_unprivileged_userns`); the project's CI relaxes it with `sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`. That changes a system-wide protection, so decide for yourself whether to do it, or give bubblewrap its own AppArmor profile. |
|
|
289
316
|
| `no usable Wayland session` or `Wayland socket not found` | `run` needs a Wayland compositor and `WAYLAND_DISPLAY`/`XDG_RUNTIME_DIR` in the environment (not available over a plain ssh login). Apps that only speak X11 use `perms APP --display xwayland` (needs `xwayland`). |
|
|
290
317
|
| An app with `network` is refused, naming `passt` | Install `passt`; the app is never put on the host network silently. |
|
|
291
|
-
| `GitHub API rate limit reached` | Unauthenticated requests are limited to 60 per hour per address. Set `GITHUB_TOKEN
|
|
318
|
+
| `GitHub API rate limit reached` | Unauthenticated requests are limited to 60 per hour per address. Set `GITHUB_TOKEN`. |
|
|
292
319
|
| `unsquashfs is not installed` | Install `squashfs-tools`, or accept the offer to run the AppImage's own extractor in a jail with no network. |
|
|
293
320
|
| DwarFS-based AppImage | `appjail tools install dwarfs` fetches the pinned helper. |
|
|
294
321
|
| An app cannot see a file or folder | Nothing outside its private home is visible by default: `appjail perms APP --add-dir PATH`, or `appjail run APP --file PATH` for one run. |
|
|
@@ -297,20 +324,24 @@ Sources are listed in `~/.local/share/appjail/index/`.
|
|
|
297
324
|
|
|
298
325
|
- **Not audited, not a security boundary you should bet on yet.** bwrap and the Linux kernel do the isolation; appjail
|
|
299
326
|
configures them conservatively but has had no external review.
|
|
327
|
+
- **The Wayland socket is restricted only if the compositor allows it.** Compositors with `wp_security_context_v1` (sway,
|
|
328
|
+
labwc, Mutter 46+, KWin 6.x) hide their privileged protocols from the app. Others, such as weston, get a warning from
|
|
329
|
+
`run` and the app receives the full host socket.
|
|
300
330
|
- Releases are resolved on GitHub only. Apps hosted elsewhere appear in `search` and can be added with `--url` or `--file`,
|
|
301
|
-
but then nothing identifies the publisher (tier 5). The cheap `update` check for
|
|
302
|
-
change confined to bytes it does not sample, with the same size and validators, is
|
|
331
|
+
but then nothing identifies the publisher (tier 5, or tier 4 with a matching `--sha256`). The cheap `update` check for
|
|
332
|
+
URL apps detects a new version; a change confined to bytes it does not sample, with the same size and validators, is
|
|
333
|
+
only seen by a full download.
|
|
303
334
|
- A sandboxed app can still do whatever its grants allow. An app with `network` can reach other machines on your LAN.
|
|
304
335
|
- Granting a directory, a portal or a D-Bus name widens what a compromised app can do.
|
|
336
|
+
- The audio permission includes the microphone.
|
|
305
337
|
- x86-64 only. The GNOME and KDE portal backends have not been tested (the GTK backend and a real X11-only AppImage
|
|
306
338
|
have, under headless weston; the portal dialogs themselves are not driven). Real internet IPv6 under `pasta` is
|
|
307
339
|
untested; IPv6 forwarding is tested in a fake network.
|
|
308
340
|
- Stopping `appjail run` with SIGINT, SIGTERM or SIGHUP stops the whole jail, including `pasta`. If appjail is killed
|
|
309
341
|
with SIGKILL, an app with `network` is stopped about a second later by a watcher inside its network namespace (a
|
|
310
342
|
small empty directory `appjail-life-*` stays in the runtime directory).
|
|
311
|
-
- The audio permission includes the microphone.
|
|
312
343
|
|
|
313
|
-
The full design
|
|
344
|
+
The full design is in [DESIGN.md](DESIGN.md); the reasoning behind each choice, rejected alternatives and deferred features are in [DECISIONS.md](DECISIONS.md).
|
|
314
345
|
|
|
315
346
|
## Development
|
|
316
347
|
|