summaryrefslogtreecommitdiff
path: root/docs/GUIX.txt
diff options
context:
space:
mode:
Diffstat (limited to 'docs/GUIX.txt')
-rw-r--r--docs/GUIX.txt195
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.