diff options
Diffstat (limited to 'docs/GUIX.txt')
| -rw-r--r-- | docs/GUIX.txt | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/docs/GUIX.txt b/docs/GUIX.txt new file mode 100644 index 0000000..89f4c9a --- /dev/null +++ b/docs/GUIX.txt @@ -0,0 +1,195 @@ +Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + +GUIX RECOVERY + + Goal: one .tar.gz, unpack/bootstrap, one command restores and starts services. + Private state is GPG-encrypted inside the tarball. Bootstrap, binaries and + public release/source are plaintext. Outer tarball is not encrypted or signed. + Trust its origin; SHA256SUMS detects corruption, not malicious replacement. + Keep decryption key/passphrase separately; never send them through chat. + + Guix supplies Podman/tools. Existing Caddy/cgit/SearXNG/chat OCI applications, + static musl/OpenSSL builds, domains, HTTPS/WKD and crypto remain. + No new Rust/npm dependency, Guix System install, or native service rewrite. + One root .env and public .env.example; databases/TLS/owner/history: var/data. + +BUILD LOCAL + + sh infra/guix/build.sh + cargo run --release --locked -- release + + Needs working Guix daemon, x86_64 Linux and rootless Podman prerequisites. + Channel commits: bin/guix-channels.scm. time-machine builds the manifest's + bin/guix-runtime.tar.gz. Release runs full checks and packages matching source, + binaries, browser assets and saved images. No compilation on recovery host. + Podman info smoke test is not clean-host/container lifecycle proof. + + Ordinary pack uses canonical /gnu/store. Relocatable -RR wrappers create a + one-ID user namespace incompatible with required subordinate UID mappings. + https://guix.gnu.org/manual/devel/en/html_node/Invoking-guix-pack.html + https://guix.gnu.org/manual/devel/en/html_node/Replicating-Guix.html + https://raw.githubusercontent.com/containers/podman/main/docs/tutorials/rootless_tutorial.md + +MAKE CARRY FILE + + Production: create consistent encrypted export; copy and verify it locally: + + ./bin/run backup --include-images + + Bundle a trusted freshly checked release with that export: + + sh infra/guix/bundle.sh /path/backup.tar.gz.gpg /path/release.tar.gz /path/recovery.tar.gz + + Bundler executes the trusted release's CLI to validate it with restore + --release. Backup-kind archives, private .env/var/data, missing runtime/chat + artifacts and missing saved images are refused before publication. + CLI, scripts, config, runtime and provenance all come from the same release. + Original release archive is unchanged and includes corresponding source and + LICENSE. Original encrypted backup retains prior source/work and rollback. + Runtime is duplicated outside release for bootstrap simplicity; extra disk + space avoids another installer/format. One carry file remains sufficient. + + Old backups remain supported; release must implement this migration's + --release validation and runtime export. Blindly reusing old management binary + loses Guix files on the next backup. Restore now activates matching new source + and binary, including canonical infra/guix/run.sh needed by later backups. + +BOOTSTRAP HOST; RESTORE + + Fresh x86_64 Linux: trusted shell, GNU coreutils/tar/gzip/findutils/diffutils, + awk, util-linux flock, sudo/root access. Bootstrap refuses /var/guix and foreign + /gnu trees. Installs only /gnu/store entries; never host /bin or /etc links. + + tar -xzf recovery.tar.gz + sudo ./stack-recovery/bootstrap + + Root-owned non-writable closure entries are staged on /gnu's filesystem, + published without overwriting, then recorded by pack digest. Retry reconciles + unfinished installs; later packs retain old entries. Root install records and + lock: /gnu/.stack-runtimes. Target needs no Guix daemon/downloads. Existing + managed Guix hosts need separate planning; do not merge unmanaged store items. + + Administrator also supplies privileged newuidmap/newgidmap, >=65536 subordinate + UIDs/GIDs, user namespaces, writable login XDG_RUNTIME_DIR, low-port sysctl + net.ipv4.ip_unprivileged_port_start <=80. DNS/firewall/ports remain host duties. + OPERATIONS.txt Debian setup installs distro Podman too: existing fallback, + not evidence of a Guix-only target. Service user imports GPG key locally. + + ./stack-recovery/restore /home/ops/stack + + New destination required. Capsule checks hashes/bootstrap/prerequisites, + decrypts through Rust manifest/path validation, requires saved backup images, + then activates its paired release under the capsule's Guix runtime/config. + Activation installs new public source/binaries/images, preserves .git, .env, + var/data, and starts four services. Source worktree becomes new release; + original source remains in encrypted backup/history. Only tested version + pairs establish application/data compatibility; format-2 integrity cannot. + + Failed activation retains data plus private var/guix-recovery intent marker. + Fix cause and rerun the same restore command with the same capsule/destination. + Matching backup/release digests permit resume without decrypting/restoring + twice; unrelated destinations are refused. Recovery lock excludes concurrent + activation and is closed in child processes. Success removes pending marker. + Do not replace an interrupted activation with plain up; it cannot finish + copying a release. Completed recoveries use normal management commands: + + /home/ops/stack/bin/run up --production + + Launcher supplies --root and Guix-only CONTAINERS_CONF; do not pass --root. + Runtime expands privately into content-addressed var/guix; old packs remain. + Activation atomically replaces the archive/provenance files, including existing + read-only packs; regression test covers replacement followed by export/restore. + Direct bin/stack and releases without pack use host tools. Guix symlinks remain + inside opaque runtime pack; Rust application extractor still rejects links. + Boot setup remains explicit: systemd user service plus linger, OPERATIONS.txt. + Unit uses bin/run at ~/stack; adapt paths for another destination. + +CHECKS; DEPLOYMENT GATE + + sh infra/guix/test.sh + cargo stack check + + Shell tests need bubblewrap. Synthetic packs test actual bootstrap/filesystem + behavior. Synthetic recovery tests actual orchestration, failed activation, + resume, intent/lock refusal, private sentinels, and child FD closure. Fake tools + do not prove real encryption, image import, services or kernel UID mapping. + Rust tests prove archive-kind refusal before publication and runtime roundtrip. + Actual static CLI also checked synthetic bundle validation/publication/privacy. + No strict Rust/Clippy/Rustdoc, typed ESLint, LibreJS or crypto gate weakened. + Existing documented lint/dependency exceptions remain unchanged. + + Image import supplies a temporary reject-by-default policy accepting local + Docker/OCI archives only. Export separately accepts containers-storage. + Manifest, architecture/layer/image-ID locks still gate startup; global host + policy stays unchanged. Generated protocol JSON cannot contain legal metadata; + its AGPL Rust generator carries the notice. POLICY.txt. + + Legacy backups may have a collapsed Caddy caddy/ tree. Verified backup restore + moves credentials to data/caddy and autosave to config/caddy, preserving bytes + and permissions. Conflicting layouts are refused before publication. Current + exports copy /data/. and /config/. separately. Keep this compatibility path + while retained backups depend on it. + + For each release, before production: + 1. Full checked release with real Guix runtime and source/binary pair. + 2. Fresh host without Guix/distro Podman/Git/GPG: offline bootstrap/restore; + four services, exact images, detached UID/network helpers. + 3. .env count, domains, HTTPS/WKD, original owner fingerprint; unchanged chat + rows and history decryption with both original participant identities. + 4. Backup recovered stack, second recovery, reboot startup. + 5. First host-Podman activation and Guix runtime upgrades. Ordinary direct + activate retains its process PATH; deploy through matching release runtime. + 6. Fresh verified independent production backup; deploy only prebuilt release; + verify public routes/state, retain prior release/runtime and encrypted export. + + Retain a verified independent export and complete original worktree, + binary/launcher, installed unit, var/state.json, image-lock.json, images.tar + and engine storage. Activation replaces public source and image metadata. + Stop/cancel the new unit and stop Guix containers before rollback; confirm + inactivity before moving engine trees. Retain the new tree, restore old engine + path/metadata/worktree/launcher/unit and reload the user manager. For a Guix + rollback use the restored prior bin/run, which selects its matching runtime. + Only a host-tools release uses host PATH with Guix overrides unset. + Preserve CURRENT .env/var/data; never extract retained copies over live data. + Old releases do not remove newer Guix launcher/runtime files automatically. + Never delete store entries still used by retained/running tools. + +DEPLOYED BASELINE — 2026-11-01 + + Signed commit 9349a2d deployed packed Podman 5.7.0 and four rootless services. + Production .env, owner, every SQLite table and all 34 TLS key/certificate files + matched. All 126 release source/binary files, public HTTPS/WKD, search and + packed conmon paths verified. Only two ACME renewal retry timestamps changed. + + A fresh Debian 12 x86_64 KVM guest had no Guix/Podman/Git/GPG. Network was + restricted before readonly-carry bootstrap/restore; GPG used a restricted + forwarded agent, never a copied secret key. Restore, export/second recovery + and final VM reboot passed; all 651 immutable snapshot files matched. + Production-host reboot was not performed. The production database had zero + conversations/messages; real historical-message decryption was untestable. + Disposable browser tests recovered 54-message history with both identities; + Firefox GNU LibreJS 7.21.1 rejected its unlicensed control. + + Tested runtime SHA256: + c5c7b849da152ee7a98b01602a1e91285ce4e3a80021a9cecb5f1e594dfc7810 + Its channel provenance is included in bin/guix-channels.scm. New source/packs + require their own checks. Empty helper lists select Guix tools through PATH; + ordinary host configuration stays unchanged. + https://github.com/containers/podman/blob/v5.7.0/vendor/go.podman.io/common/pkg/config/config.go#L849-L875 + https://github.com/containers/podman/blob/v5.7.0/cmd/podman/images/save.go#L91-L94 + + Private evidence: var/guix-*.log and .tools/guix-production/; these include + private state hashes and stay ignored. Do not publish them with source. |
