Commit 7af1e74
0
plików
+0
dodanych
-0
usuniętych
@@ -0,0 +1,409 @@
1
+# pag – PaganOS Package Manager
2
+
3
+[](https://git.paganlinux.eu/pag)
4
+[](https://paganlinux.eu)
5
+
6
+Package manager for [PaganOS](https://paganlinux.eu). Atomic installation
7
+(staging → rename), GPG signature verification with fingerprint pinning,
8
+per-file SHA256, full transaction rollback, hooks and triggers, and support for
9
+immutable systems with deployments.
10
+
11
+Diagrams in this README are rendered with Mermaid – supported by the
12
+`git.paganlinux.eu` portal (and GitHub/Gitea).
13
+
14
+```bash
15
+pag --version # client version (e.g. pag 3.3.20)
16
+pag # full help (command list)
17
+pag --help # same as above
18
+```
19
+
20
+---
21
+
22
+## Table of contents
23
+
24
+- [Quick start](#quick-start)
25
+- [How it works](#how-it-works)
26
+ - [`pag install` flow](#pag-install-flow)
27
+ - [Trust chain](#trust-chain)
28
+ - [Immutable system](#immutable-system)
29
+ - [Rollback](#rollback)
30
+- [Commands](#commands)
31
+- [Environment variables](#environment-variables)
32
+- [Paths and files](#paths-and-files)
33
+- [Package format](#package-format)
34
+- [Hooks and triggers](#hooks-and-triggers)
35
+- [`/etc` configuration – `.pacnew` / `.pacsave`](#etc-configuration--pacnew--pacsave)
36
+- [Examples](#examples)
37
+- [Troubleshooting](#troubleshooting)
38
+- [Install and update](#install-and-update)
39
+- [License](#license)
40
+
41
+---
42
+
43
+## Quick start
44
+
45
+```bash
46
+# Refresh repository indexes (and see what is pending an update)
47
+sudo pag sync
48
+
49
+# Install / remove
50
+sudo pag install firefox gimp
51
+sudo pag remove gimp
52
+
53
+# Update packages only…
54
+sudo pag update
55
+# …or the whole system: packages + kernel / initramfs / GRUB
56
+sudo pag upgrade
57
+
58
+# Verify the integrity of all files
59
+sudo pag verify --deep
60
+```
61
+
62
+---
63
+
64
+## How it works
65
+
66
+### `pag install` flow
67
+
68
+```mermaid
69
+graph TD
70
+ A["pag install pkg"] --> B["Dependency resolution (DFS + provides)"]
71
+ B --> C{"Missing dependencies?"}
72
+ C -->|yes| C1["Error – abort"]
73
+ C -->|no| D["ABI verification (so-name)"]
74
+ D --> E["Pre-flight: free space + RW mount"]
75
+ E --> F["flock + database snapshot"]
76
+ F --> G["Parallel package download"]
77
+ G --> H["GPG + package SHA256 verification"]
78
+ H --> I["Staging: extract + per-file sums"]
79
+ I --> J["Atomic rename of files into the system"]
80
+ J --> K["pre/post-install hooks"]
81
+ K --> L["Write to SQLite + transaction history"]
82
+ L --> M{"PAG_IMMUTABLE=1?"}
83
+ M -->|yes| N["New deployment + GRUB entries"]
84
+ M -->|no| O["ldconfig + triggers"]
85
+ H -->|error| R["Rollback of the whole transaction"]
86
+ I -->|error| R
87
+ J -->|error| R
88
+ R --> S["Restore database and files"]
89
+```
90
+
91
+Key properties:
92
+
93
+- **Atomicity** – files first go to staging on the same partition as `/`, then
94
+ are moved with `rename()` (no half-install).
95
+- **Transactionality** – if any package fails, the whole transaction is rolled
96
+ back (`installed.json`, files, backups).
97
+- **File sharing** – a file owned by two packages is not removed when one of
98
+ them is removed (only the database entry is removed).
99
+
100
+### Trust chain
101
+
102
+```mermaid
103
+graph TD
104
+ A["repo.json + repo.json.asc"] --> B["GPG: VALIDSIG line"]
105
+ B --> C["Repo fingerprint pinning (TOFU → pin)"]
106
+ C --> D["Package index"]
107
+ D --> E["package .pag + .asc"]
108
+ E --> F["GPG: signature matches repo pin"]
109
+ F --> G["Whole-package SHA256"]
110
+ G --> H["Safe extraction (anti-traversal)"]
111
+ H --> I["sums.json: per-file SHA256"]
112
+ I --> J["Strip SUID bit"]
113
+ J --> K["Install"]
114
+ F -->|"missing / bad signature"| X["Reject package"]
115
+ G -->|"SHA256 mismatch"| X
116
+ H -->|"traversal / symlink escape"| X
117
+```
118
+
119
+- First use of a key = **TOFU**, afterwards the fingerprint is **pinned**
120
+ (`pag key-trust` / `pag key-untrust`).
121
+- Verification is **fail-closed**: missing/bad signature = no installation
122
+ (override only with `PAG_INSECURE=1`, build/dev only).
123
+- `pag self-update` goes through the same path: GPG → SHA256 → syntax check →
124
+ atomic client replacement.
125
+
126
+### Immutable system
127
+
128
+With `PAG_IMMUTABLE=1`, installation does not mutate `/`; it creates a new
129
+deployment. Rollback is just switching the `active` symlink.
130
+
131
+```mermaid
132
+graph TD
133
+ R["/"] --> D["/.deployments"]
134
+ D --> A["active → 20260723T120000"]
135
+ D --> D1["20260723T120000 (new)"]
136
+ D --> D2["20260722T090000 (previous)"]
137
+ D1 --> U["usr/ bin/ lib/ – system"]
138
+ D1 --> S1["var → /var"]
139
+ D1 --> S2["etc → /etc"]
140
+ D1 --> S3["home → /home"]
141
+ D1 --> S4["boot → /boot"]
142
+ D2 -.->|"deploy-rollback"| A
143
+```
144
+
145
+- `/var`, `/etc`, `/home`, `/boot` (and other `SHARED_PATHS`) are **shared**
146
+ between deployments – they are not duplicated.
147
+- `/boot` shares the kernel and initramfs, saving space per deployment.
148
+- Rollback from the bootloader: each deployment has its own GRUB entry
149
+ (`pag grub-update`).
150
+
151
+### Rollback
152
+
153
+```mermaid
154
+graph TD
155
+ T["Transaction"] --> OK{"Success?"}
156
+ OK -->|yes| H["History + snapshot + file journal"]
157
+ OK -->|no| RB["Automatic transaction rollback"]
158
+ H --> R["pag rollback"]
159
+ R --> R1["Restore installed.json from snapshot"]
160
+ R --> R2["Remove new files from the journal"]
161
+ R --> R3["Restore overwritten files from backup"]
162
+ R --> R4["Clean up empty directories"]
163
+ RB --> R1
164
+ RB --> R2
165
+ RB --> R3
166
+```
167
+
168
+`pag history` shows recent transactions (including executed hooks), and
169
+`pag rollback` reverts the **last successful** transaction with a snapshot.
170
+
171
+---
172
+
173
+## Commands
174
+
175
+### BASICS
176
+
177
+| Command | Description |
178
+|---|---|
179
+| `pag install <pkg>...` | Install packages (together with dependencies) |
180
+| `pag install -f <pkg>...` | **Force reinstall** (even the same version) – restores files, empty directories and hooks |
181
+| `pag remove <pkg>...` | Remove packages |
182
+| `pag update` | Update **packages** to newer versions |
183
+| `pag sync` | Refresh repository indexes + show how many packages await update |
184
+| `pag upgrade` | **System** update: packages + kernel / initramfs / GRUB |
185
+| `pag list` | List packages available in the repository |
186
+| `pag list --installed` | List installed packages |
187
+| `pag search <query>` | Search packages in the repo (+ Flathub) |
188
+| `pag info <pkg>` | Package details (version, dependencies, size, signature) |
189
+| `pag files <pkg>` | List files owned by a package |
190
+| `pag verify` | Verify the integrity of installed files |
191
+| `pag verify --deep` | Full per-file SHA256 verification |
192
+| `pag clean` | Clear the download cache |
193
+| `pag stats` | System statistics (package count, size, cache…) |
194
+| `pag download <pkg>...` | Download packages to cache (offline mode) |
195
+
196
+**Smart search:** `pag <name>` (any unknown command) searches the repo and
197
+Flathub and suggests names – e.g. `pag firefox` will find the package.
198
+
199
+### SECURITY / GPG KEYS
200
+
201
+| Command | Description |
202
+|---|---|
203
+| `pag key-add <url\|file>` | Import a repository GPG key |
204
+| `pag key-list` | List trusted keys |
205
+| `pag key-remove <id>` | Remove a key |
206
+| `pag key-trust <repo>` | Pin the repo key fingerprint (TOFU disabled) |
207
+| `pag key-untrust <repo>` | Forget the fingerprint (back to TOFU) |
208
+| `pag key-trusted` | List pinned repo fingerprints |
209
+
210
+### ADVANCED
211
+
212
+| Command | Description |
213
+|---|---|
214
+| `pag why <pkg>` | Why a package is installed (who depends on it) |
215
+| `pag autoremove` | Remove orphaned dependencies |
216
+| `pag remove-orphans` | Remove orphaned dependencies (alias) |
217
+| `pag pin <pkg> [version]` | Pin a package to a version (block updates) |
218
+| `pag unpin <pkg>` | Unpin |
219
+| `pag pinned` | List pinned packages |
220
+| `pag history` | Transaction history |
221
+| `pag rollback` | Revert the **last** transaction (restore files from backup) |
222
+| `pag repo-add <url> [name]` | Add a repository (drop-in in `/etc/pag/repos/`) |
223
+| `pag repo-list` | List configured repositories |
224
+| `pag sbom export [spdx\|cyclonedx]` | Export an SBOM manifest of all installed components |
225
+| `pag self-update` | Update the `pag` client itself (GPG signature + SHA256 + syntax, atomically) |
226
+
227
+### FLATPAK
228
+
229
+| Command | Description |
230
+|---|---|
231
+| `pag flatpak [<query>]` | Search and install from Flathub |
232
+| `pag flatpak search <query>` | Search Flathub |
233
+| `pag flatpak install <id>` | Install a flatpak |
234
+| `pag flatpak remove <id>` | Remove a flatpak |
235
+| `pag flatpak list` | List installed flatpaks |
236
+| `pag flatpak update` | Update all flatpaks |
237
+| `pag flatpak info <id>` | Flatpak details |
238
+
239
+> Installation as root forces the **system** scope (`--system`), so the
240
+> application is visible to all users, not just root.
241
+
242
+### IMMUTABLE SYSTEM (`PAG_IMMUTABLE=1`)
243
+
244
+| Command | Description |
245
+|---|---|
246
+| `pag deploy-list` | List deployments |
247
+| `pag deploy-rollback` | Switch to the previous deployment |
248
+| `pag deploy-cleanup [N]` | Remove old deployments (keep N, default 3) |
249
+| `pag initramfs-update` | Rebuild initramfs |
250
+| `pag grub-update` | Regenerate GRUB entries for all deployments |
251
+
252
+---
253
+
254
+## Environment variables
255
+
256
+| Variable | Meaning |
257
+|---|---|
258
+| `PAG_ROOT` | Alternative operation root (tests/chroot); default `/` |
259
+| `PAG_IMMUTABLE=1` | Immutable mode – installation creates a new deployment |
260
+| `PAG_YES=1` | Automatic confirmation (same as `-y` / `--yes`) |
261
+| `PAG_INSECURE=1` | Disables the HTTPS requirement and fail-closed GPG (**build/dev only!**) |
262
+| `PAG_NO_HOOKS=1` | Skip hooks and triggers |
263
+| `PAG_HOOK_TIMEOUT` | Hook timeout in seconds (default `60`) |
264
+| `PAG_LANG_DIR` | Directory with translation files (`pl.json`, `en.json`) |
265
+| `PAG_LANG_NO_FILES=1` | Ignore translation files (export built-ins) |
266
+| `PAG_ROOT_DEVICE` / `PAG_GRUB_ROOT` | Explicit `root=` for GRUB (ISO/IMG build) |
267
+| `PAG_IN_CHROOT=1` | Force chroot mode when detecting the root device |
268
+
269
+---
270
+
271
+## Paths and files
272
+
273
+| Path | Purpose |
274
+|---|---|
275
+| `/var/lib/pag/` | State database: `installed.json`, `files.db`, `world`, `pinned.json`, `history.json`, `pag.lock`, `hooks/` |
276
+| `/var/lib/pag/files.db` | SQLite: file owners + SHA256 sums |
277
+| `/var/cache/pag/` | Downloaded package cache |
278
+| `/var/cache/pag/repos/` | Repository index cache (JSON + ETag + timestamp) |
279
+| `/etc/pag/repos.conf` and `/etc/pag/repos/*.conf` | Repositories (drop-in) |
280
+| `/etc/pag/trusted.json` | Pinned repo key fingerprints |
281
+| `/etc/pag/gpg/` | Isolated GPG keyring |
282
+| `/etc/pag/triggers/*.json` | Custom triggers |
283
+| `/etc/pag/lang/`, `/usr/share/pag/lang/` | Translations (files override built-ins) |
284
+| `/var/log/pag/audit.log` | Audit: hooks, self-update |
285
+| `/.pag_staging` | Staging (same partition as `/` – no `EXDEV`) |
286
+| `/.deployments/` | Deployments (immutable mode) |
287
+
288
+---
289
+
290
+## Package format
291
+
292
+A `.pag` package is an archive containing:
293
+
294
+```
295
+metadata.json – name, version, release, dependencies, provides/requires (so-name)
296
+data.tar.xz – system files + sums.json (per-file SHA256)
297
+hooks/ – optional: pre-install, post-install, pre-remove, post-remove
298
+```
299
+
300
+Installation is **verified per file** against `sums.json`, and extraction is
301
+protected against *directory traversal* and escape via malicious symlinks.
302
+
303
+---
304
+
305
+## Hooks and triggers
306
+
307
+**Hooks** run as `root` (like apt/pacman), in a restricted environment:
308
+
309
+- `pre-install`, `post-install`, `pre-remove`, `post-remove`
310
+- clean env: `PATH`, `HOME=/root`, `LANG/LC_ALL=C.UTF-8`, `PKG_NAME`,
311
+ `PKG_VERSION`, `PKG_ACTION`, `PKG_HOOK_API=1`
312
+- timeout (`PAG_HOOK_TIMEOUT`), optional disable (`PAG_NO_HOOKS=1`), entries in
313
+ `/var/log/pag/audit.log` and in the transaction history
314
+
315
+You install code you trust – hooks have full privileges.
316
+
317
+**Triggers** run **once per transaction**, when matching paths were touched
318
+(only if the given binary exists):
319
+
320
+| Trigger | Paths | Command |
321
+|---|---|---|
322
+| `font-cache` | `/usr/share/fonts/`, `/usr/local/share/fonts/` | `fc-cache -fs` |
323
+| `glib-schemas` | `/usr/share/glib-2.0/schemas/` | `glib-compile-schemas …` |
324
+| `desktop-database` | `/usr/share/applications/` | `update-desktop-database -q …` |
325
+| `mime-database` | `/usr/share/mime/` | `update-mime-database …` |
326
+
327
+You can add your own triggers as `/etc/pag/triggers/*.json`.
328
+
329
+---
330
+
331
+## `/etc` configuration – `.pacnew` / `.pacsave`
332
+
333
+The `/etc` directory is shared between deployments (it is not reverted by
334
+`deploy-rollback`). To avoid losing user changes:
335
+
336
+- **Updating** a configuration file that the user modified: the new version is
337
+ written as `<file>.pacnew`, and the user's file **is kept**.
338
+- **Removing** a package with a modified configuration file: the file becomes
339
+ `<file>.pacsave` instead of being deleted.
340
+
341
+The comparison uses the SHA256 sum recorded at install time.
342
+
343
+---
344
+
345
+## Examples
346
+
347
+```bash
348
+# Install and updates
349
+sudo pag install firefox gimp
350
+sudo pag update # packages only
351
+sudo pag upgrade # packages + kernel/initramfs/GRUB
352
+sudo pag sync # refresh indexes + update info
353
+
354
+# Repair a package (missing files/directories, e.g. /etc/pulse/default.pa.d)
355
+sudo pag install -f pulseaudio
356
+
357
+# Application icons after installing an icon theme (cache built automatically)
358
+sudo pag install papirus-icon-theme
359
+
360
+# Security
361
+sudo pag key-add https://repo.paganlinux.eu/stable/paganos.asc
362
+sudo pag key-trust https://repo.paganlinux.eu/stable/
363
+sudo pag verify --deep
364
+
365
+# Dependencies and package provenance
366
+sudo pag why libjpeg-turbo
367
+sudo pag autoremove
368
+
369
+# SBOM for audit / compliance
370
+sudo pag sbom export cyclonedx > sbom.json
371
+
372
+# Revert the last transaction
373
+sudo pag rollback
374
+```
375
+
376
+---
377
+
378
+## Troubleshooting
379
+
380
+| Symptom | Solution |
381
+|---|---|
382
+| "Another pag instance is running" | Another `pag` process is active (`flock` lock). Wait or check `pgrep -af pag`. |
383
+| "Cannot refresh the index" | The repo cache is read-only for a regular user – run as root: `sudo pag sync`. |
384
+| "Invalid signature / missing signature" | Import the key: `sudo pag key-add <url>`, then verify `sudo pag key-trusted`. |
385
+| Missing files/directories after install | `sudo pag install -f <pkg>` (restores files, empty directories and hooks). |
386
+| System does not boot after `grub-update` | Set the root device explicitly: `PAG_ROOT_DEVICE=/dev/sda2 sudo pag grub-update`. |
387
+| Flatpak app not visible in the menu | Add `/var/lib/flatpak/exports/share` to `XDG_DATA_DIRS` and log in again. |
388
+
389
+---
390
+
391
+## Install and update
392
+
393
+```bash
394
+# On a running PaganOS, pag is in the base repository:
395
+sudo pag install pag # install from the repository
396
+sudo pag self-update # update the client itself (signed)
397
+
398
+# pag is also managed by the package manager – after a new version is
399
+# published in the repo:
400
+sudo pag update
401
+sudo pag upgrade
402
+```
403
+
404
+---
405
+
406
+## License
407
+
408
+PaganOS / pag – an open source project. See the distribution repository for
409
+details.