From 607be5ee239a9b7969eab96145c8af4ee57053d1 Mon Sep 17 00:00:00 2001 From: Doan Luu Duc Tai Date: Sun, 1 Nov 2026 08:25:58 -0600 Subject: chore: remove obsolete migration code and docs --- README.txt | 9 +- crates/chat-crypto/README.txt | 3 +- crates/chat-server/web/javascript.html | 2 +- docs/CHAT-APPLICATION.txt | 20 +-- docs/CHAT-CRYPTO.txt | 170 ++++++++++++++++++ docs/CHAT-OPENSSL-MIGRATION.txt | 234 ------------------------- docs/CHAT-PRODUCTION.txt | 61 ++----- docs/CONFIGURATION.txt | 2 +- docs/GUIX.txt | 112 ++++++------ docs/MAP.txt | 10 +- docs/OPERATIONS.txt | 312 ++++++++++++++------------------- docs/POLICY.txt | 4 +- src/app/files.rs | 1 - src/app/project.rs | 6 +- 14 files changed, 392 insertions(+), 554 deletions(-) create mode 100644 docs/CHAT-CRYPTO.txt delete mode 100644 docs/CHAT-OPENSSL-MIGRATION.txt diff --git a/README.txt b/README.txt index ab5a3bd..3b6488e 100644 --- a/README.txt +++ b/README.txt @@ -61,8 +61,8 @@ CHECK AND RELEASE Cargo.lock + about.hbs generate THIRD-PARTY-LICENSES.html. Display GNU license names; preserve exact SPDX identifiers and upstream legal text. - Build/test locally. Deploy prebuilt archives. VPS needs Podman, Git, GnuPG; - no Rust/Cargo/Node/npm/TypeScript. Backup, restore, host setup, deployment: + Build/test locally. Deploy prebuilt archives; Guix supplies runtime tools. + VPS needs no Rust/Cargo/Node/npm/TypeScript. Backup, restore, host setup, deployment: docs/OPERATIONS.txt. Building a release does not activate production. CHAT — EXPERIMENTAL @@ -76,8 +76,9 @@ CHAT — EXPERIMENTAL SQLite snapshots use bundled SQLite C library. Static releases use OpenSSL, hash-verified musl headers, Rust musl target. VPS needs no compiler. -GUIX — EXPERIMENTAL +GUIX RECOVERY Single .tar.gz capsule with encrypted private state: docs/GUIX.txt. Canonical Guix store needs privileged bootstrap; existing managed stores refused. - Real Guix lifecycle/clean-host restore remain unproven. Do not deploy yet. + Production uses the packed rootless runtime. Offline clean-host restore, + second recovery and VM reboot passed. Host prerequisites/limits: docs/GUIX.txt. diff --git a/crates/chat-crypto/README.txt b/crates/chat-crypto/README.txt index 0fb26e7..cb8d9b2 100644 --- a/crates/chat-crypto/README.txt +++ b/crates/chat-crypto/README.txt @@ -18,7 +18,6 @@ PRIVATE CHAT CRYPTOGRAPHY Fixed encryption: RFC 9980 ML-KEM-1024 + X448, OpenPGP v6. Signing: ML-DSA-87 + Ed448, OpenPGP v6. Sequoia 2.4.2 + OpenSSL 3.6.5. No algorithm substitution or advisory exception. - Superseded RustCrypto backend is not selected. Provides purpose-specific identities, Argon2id/AES-256-GCM protected files, detached authentication proofs, signed two-recipient messages, bounded @@ -29,7 +28,7 @@ PRIVATE CHAT CRYPTOGRAPHY ../../docs/CHAT-APPLICATION.txt: formats, setup, tests, backups, limits. ../../docs/CHAT-PRODUCTION.txt: packaged HTTPS. - ../../docs/CHAT-OPENSSL-MIGRATION.txt: backend decision, source hashes, + ../../docs/CHAT-CRYPTO.txt: backend decision, source hashes, build flags, FFI/capability limits, reviewed dependency exceptions. Experimental. Compatibility tests are not an application security audit. diff --git a/crates/chat-server/web/javascript.html b/crates/chat-server/web/javascript.html index b39504c..7c8d1d7 100644 --- a/crates/chat-server/web/javascript.html +++ b/crates/chat-server/web/javascript.html @@ -24,6 +24,6 @@ along with this program. If not, see . wasi.mjsGNU-AGPL-3.0Complete corresponding source

The readable JavaScript is compiled from the included TypeScript sources. The source archive includes shared protocol declarations, compiler configurations, the npm lock, and build/test scripts. No third-party JavaScript runs in the browser. Node, npm, and the pinned TypeScript compiler are development tools; the production server serves prebuilt scripts.

-

The WebAssembly module is compiled from the Rust workspace and unmodified OpenSSL 3.6.5, under their respective free software licenses. The source archive includes first-party sources, Cargo dependencies, the OpenSSL source archive, build scripts, and notices. General-purpose Rust and WASI SDK tools are documented with verified download locations in CHAT-OPENSSL-MIGRATION.txt.

+

The WebAssembly module is compiled from the Rust workspace and unmodified OpenSSL 3.6.5, under their respective free software licenses. The source archive includes first-party sources, Cargo dependencies, the OpenSSL source archive, build scripts, and notices. General-purpose Rust and WASI SDK tools are documented with verified download locations in CHAT-CRYPTO.txt.

Third-party licenses / Source archive / Return

diff --git a/docs/CHAT-APPLICATION.txt b/docs/CHAT-APPLICATION.txt index 388c677..6a90477 100644 --- a/docs/CHAT-APPLICATION.txt +++ b/docs/CHAT-APPLICATION.txt @@ -16,9 +16,9 @@ along with this program. If not, see . EXPERIMENTAL PRIVATE CHAT - Available on main: direct development and prebuilt Podman/HTTPS deployment. - See CHAT-PRODUCTION.txt for release, setup, and recovery. Feature work does - not merge main or activate the VPS. Experimental; no independent messenger audit. + Direct development and prebuilt Podman/HTTPS deployment are supported. + See CHAT-PRODUCTION.txt for release, setup and recovery. Experimental; + no independent messenger security audit. WHAT EXISTS @@ -108,9 +108,9 @@ OWNER FILE LOSS AND BACKEND RESET Run development commands at the stack root. Reset has no HTTP API; private chat-directory access is the administrative boundary. Production runs - ./bin/stack chat --owner-fingerprint and ./bin/stack chat --reset-owner in an - isolated maintenance container. First stop with ./bin/stack down; restart - with ./bin/stack up --production. No VPS Rust required. Read CHAT-PRODUCTION.txt + ./bin/run chat --owner-fingerprint and ./bin/run chat --reset-owner in an + isolated maintenance container. First stop with ./bin/run down; restart + with ./bin/run up --production. No VPS Rust required. Read CHAT-PRODUCTION.txt before destructive reset. Read the current public fingerprint, even if you have lost the private file: @@ -321,10 +321,8 @@ INTERFACE AND SITE STYLE LICENSES, SOURCE, AND VERIFICATION - Bundled SQLite snapshots require a musl C compiler for portable CLI builds. - The root CLI ran as x86_64 static PIE; VPS SQLite packages are unnecessary. - Local checks compiled C with Alpine musl 1.2.6 headers/host compiler and - linked with Rust's self-contained musl runtime. + Portable CLI builds statically link bundled SQLite with musl. Production + needs no SQLite package or compiler. Build inputs: CHAT-PRODUCTION.txt. The rendered dependency-license page is centralized on the main website at https://doan.luuduct.ai/THIRD-PARTY-LICENSES.html. Chat links point there; its @@ -400,4 +398,4 @@ LICENSES, SOURCE, AND VERIFICATION images and no build directories inside the recovered installation. Native tests cover authorization, challenge reuse, expiry, locked sessions, quotas, idempotency, and SQLite snapshot recovery. The implemented design and - limits are documented here. CHAT-OPENSSL-MIGRATION.txt records the backend review. + limits are documented here. CHAT-CRYPTO.txt records the backend review. diff --git a/docs/CHAT-CRYPTO.txt b/docs/CHAT-CRYPTO.txt new file mode 100644 index 0000000..fa9fa1a --- /dev/null +++ b/docs/CHAT-CRYPTO.txt @@ -0,0 +1,170 @@ +Copyright (C) 2026 Doan Luu Duc Tai + +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 . + +CHAT CRYPTOGRAPHY AND BUILDS + + Application formats, authentication, recovery and security limits: + CHAT-APPLICATION.txt. Static native packaging and deployment: + CHAT-PRODUCTION.txt. Functional compatibility is not a cryptographic, + side-channel or application security audit. + +FIXED ALGORITHMS AND BACKEND + + Sequoia OpenPGP 2.4.2, RFC 9980 ML-KEM-1024 + X448 encryption, + ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, AES-256 content encryption. + Only crypto-openssl is enabled; no algorithm substitution, experimental/ + variable-time backend opt-in, or ignored RustSec advisory. Native and browser + probes require the OpenSSL backend and expected key algorithms. + + The superseded Rust backend's required curve arithmetic depended on an + incompatible crypto-bigint generation affected by RUSTSEC-2026-0387. + OpenSSL avoids that dependency; the resolved graph has no cx448, + crypto-bigint or rsa crate. This does not prove absence of C defects or + browser side channels. See https://rustsec.org/advisories/RUSTSEC-2026-0387.html. + +SOURCE PROVENANCE + + Rust: rust-toolchain.toml. Sequoia: exact Cargo.toml requirement/Cargo.lock. + OpenSSL: 3.6.5. WASI SDK: 34.0 x86_64 Linux, bundled Clang/libclang + 23.1.0-wasi-sdk. Browser tooling: package.json/package-lock.json. + + WASI SDK archive: + https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz + SHA-256 b761e3a0721dbae9c09a0059e5fdb2bf917d1b4a8a7b430fb3b5aafb0984b2c4 + + OpenSSL archive: + https://github.com/openssl/openssl/releases/download/openssl-3.6.5/openssl-3.6.5.tar.gz + SHA-256 a2157c2830efdec3788939b00c9b0638306d3f0bbb76dc4832ee503bb397df98 + + Build scripts verify these digests before extraction. Pinned hashes do not + independently verify upstream signatures or promise cross-host identical + binaries. Sources, SDK and static libraries stay under ignored .tools/; + Cargo output stays under target/. Native development uses the host library; + production static native builds use the verified source, CHAT-PRODUCTION.txt. + +WASI BUILD AND ENTROPY + + infra/chat/openssl-wasi.conf adds a BASE_unix target with little-endian, + 32-bit bignums. C uses -O2; no cryptographic routine or RNG patch is applied. + OpenSSL's --with-rand-seed=getrandom reaches WASI random_get through libc. + The browser bridge uses only crypto.getRandomValues, splitting requests + at 65536 bytes. Entropy failures reject; no fixed seed or success fallback. + The browser probe includes a failing-entropy negative control. + + Static WASI builds disable assembly, async operations, automatic config + loading, DSO/modules, POSIX I/O, secure-memory arenas, shared libraries, + sockets, C stdio, thread pools, threads and interactive console UI. + Ordinary WASM memory provides no locked-page, secure-erasure or delivered- + JavaScript protection. OpenSSL uses C/FFI; first-party Rust unsafe-code + prohibition does not make dependencies memory-safe. + + _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply process-ID emulation. + getuid/geteuid/getgid/getegid map to getpagesize. These values provide neither + entropy nor identity credentials; there is no real OS user/privilege boundary. + This portability mapping does not emulate general POSIX security. + + Bindgen uses SDK libclang/sysroot, wasm32-wasip1 and -fvisibility=default + so declarations remain available. Rust's bundled WASI libc precedes SDK + libraries to resolve __wasi_init_tp; SDK libraries supply emulated-getpid. + Generated cryptographic bindings are not edited. The linker caps WASM linear + memory at 256 MiB; browser/host copies add overhead. + +BROWSER CAPABILITIES + + crates/chat-crypto/browser/wasi.ts supplies clocks, browser CSPRNG, empty + arguments/environment, in-memory request stdin and captured stdout/stderr. + Empty stdin runs the compatibility probe; application commands use JSON. + Requests exceeding 8388608 JavaScript string units are rejected; combined + stdout/stderr exceeding 1048576 string units abort execution. + + No real files, preopened directories, sockets or process launch are supplied. + Filesystem/network access is refused; unknown imports fail before + instantiation. The application runs crypto in one-shot + workers with a timeout; CHAT-APPLICATION.txt documents memory/state limits. + + Capability denials prevent accidental host storage/network access, not + malicious JavaScript from the same server. Memory, clocks, randomness and + bridge imports remain trusted. Browsers/OSes can retain memory, caches, + crash dumps, swap and restored state; no complete erasure is guaranteed. + +DEPENDENCY EXCEPTIONS + + deny.toml allows only exact older duplicate versions; advisory/license + checks still apply. No RustSec ignore. Wildcards, unknown Git/registry sources + and unreviewed duplicates remain rejected. Cargo.lock pins both API lines: + + block-buffer 0.10.4 vs 0.12.1 + crypto-common 0.1.7 vs 0.2.2 + digest 0.10.7 vs 0.11.3 + Sequoia's Argon2 0.5.3 -> BLAKE2 0.10 needs the older digest API; + root SHA-256 and parser-generator SHA3 use the newer API. + cpufeatures 0.2.17 vs 0.3.1 + Argon2 uses 0.2; root SHA-256/parser-generator SHA3 use 0.3. + generic-array 0.14.7 vs 1.4.5 + Argon2/BLAKE2 digest uses 0.14; SHA1 collision detection uses 1.x. + getrandom 0.2.17 vs 0.4.3 + Sequoia's unknown-unknown WASM dependencies declare 0.2; root + tempfile uses 0.4. Browser WASI uses OpenSSL/bridge entropy. + Audit the all-target graph rather than excluding this edge. + hashbrown 0.15.5 vs 0.17.1 + Parser-generator petgraph uses 0.15; its indexmap 2.14 uses 0.17. + itertools 0.13.0 vs 0.15.0 + shlex 1.3.0 vs 2.0.1 + Bindgen uses older APIs; lalrpop and cc require newer ones. + syn 2.0.119 vs 3.0.9 + Bindgen/prettyplease and Windows support use syn 2; root derives + and other dependencies use syn 3. Unification needs upstream API + migration or unrelated downgrades, not a lockfile update. + + Build dependencies remain audited. Clippy's name-only exceptions for + generic-array/getrandom/syn do not replace exact cargo-deny checks. + LINTING.txt documents the lint policy. + +EXTERNAL LIBRARIES AND LICENSES + + cargo-deny does not audit external OpenSSL C or the SDK. Review separately: + https://openssl-library.org/news/timeline/ + https://openssl-library.org/news/vulnerabilities-3.6/ + A Rust audit does not guarantee the pinned C version's future security. + Updates require upstream advisory review, every affected URL/hash, + native/WASI rebuilds, both browsers, cargo-deny, regenerated notices/sources + and release recovery. Never waive advisories to pass. + + libloading used by bindgen/clang has a package-scoped ISC allowance. + cargo-about covers workspace Rust licenses, not external C/SDK obligations. + Distributed OpenSSL/SDK artifacts retain their required licenses/sources; + OpenSSL LICENSE.txt is Apache-2.0. Rust notices alone are insufficient. + Corresponding sources include the unmodified OpenSSL archive; toolchain + declarations/download locations remain public. + +BUILD AND CHECK FROM THE STACK ROOT + + Native development: OpenSSL >=3.5 headers/library, libclang, pkg-config, + C compiler and pinned Rust. WASI cross-build: x86_64 GNU/Linux, Perl, make, + curl, tar, sha256sum. Browser tooling/install instructions: TYPESCRIPT.txt. + + cargo test --locked -p stack-chat-crypto + cargo run --locked -p stack-chat-crypto + sh infra/chat/build-openssl-wasi.sh + npm run build + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto + cargo stack check + + The WASI script builds verified OpenSSL into .tools/openssl-wasi, links the + Rust WASI command and prints its SHA-256. Rust-only edits may reuse inputs + with sh infra/chat/build-browser.sh; source/version/configuration/flag changes + require an OpenSSL rebuild. Extracted sources can be reused; independent + verification needs a fresh ignored tools/build tree. Native and browser + probe results must explicitly identify the OpenSSL backend. diff --git a/docs/CHAT-OPENSSL-MIGRATION.txt b/docs/CHAT-OPENSSL-MIGRATION.txt deleted file mode 100644 index 4bb87fb..0000000 --- a/docs/CHAT-OPENSSL-MIGRATION.txt +++ /dev/null @@ -1,234 +0,0 @@ -Copyright (C) 2026 Doan Luu Duc Tai - -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 . - - -OPENSSL BACKEND MIGRATION RECORD - - Date: 2026-10-30. Historical compatibility/dependency-security experiment - using disposable identities. For the later messenger and packaged deployment, - see CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt. - -DECISION AND FIXED ALGORITHMS - - Retain Sequoia OpenPGP 2.4.2, RFC 9980 ML-KEM-1024 + X448 encryption, - ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, and AES-256 content encryption. - Only the backend changes; algorithms and wire protocol stay fixed. - Probe assertions reject unexpected backends or key algorithms. - - Previous Rust curve arithmetic reached cx448 0.1.1 -> crypto-bigint 0.5.5; - the backend also included rsa 0.9.10. Neither RustSec issue was ignored; - no mathematical implementation was patched locally. - RUSTSEC-2026-0387 affected required X448/Ed448 curve arithmetic, not unused RSA. - Its fix requires crypto-bigint >=0.7.6; cx448 0.1.1 requires incompatible 0.5. - A lockfile update alone could not fix it. No practical browser key-extraction - exploit was demonstrated; functional tests do not prove constant-time behavior. - The closed probe used no RSA private operations, but that did not establish - unreachability through hostile imports or future application interfaces. - https://rustsec.org/advisories/RUSTSEC-2026-0387.html - https://rustsec.org/advisories/RUSTSEC-2023-0071.html - - Only crypto-openssl is enabled. allow-experimental-crypto and - allow-variable-time-crypto are removed. The resolved graph has no cx448, - crypto-bigint, or rsa crate. Removing those dependencies does not prove - absence of browser side channels or external C defects. - -VERSIONS AND SOURCE PROVENANCE - - Rust toolchain: 1.99.1, pinned in rust-toolchain.toml. - Sequoia OpenPGP: 2.4.2, exact requirement and Cargo.lock. - OpenSSL: 3.6.5, native host library and source-built static WASI libraries. - WASI SDK: 34.0 x86_64 Linux, bundled Clang/libclang 23.1.0-wasi-sdk. - Browser test runner: Playwright 1.64.1, Chromium 156.0.8078.4. - - SDK archive: - https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz - SHA-256 b761e3a0721dbae9c09a0059e5fdb2bf917d1b4a8a7b430fb3b5aafb0984b2c4 - - OpenSSL archive: - https://github.com/openssl/openssl/releases/download/openssl-3.6.5/openssl-3.6.5.tar.gz - SHA-256 a2157c2830efdec3788939b00c9b0638306d3f0bbb76dc4832ee503bb397df98 - - Digests match upstream GitHub release asset metadata; scripts verify them - before extraction. This pins inputs, without independently verifying release - signatures or proving cross-host bit-for-bit builds. Archives, SDK, libraries, - and build products stay under ignored .tools/ and target/. The OS supplies - the native development library. - -WHY WASI - - The Rust proof used wasm32-unknown-unknown and wasm-bindgen glue. OpenSSL uses - a wasm32-wasip1 command and explicit browser WASI bridge, calling the native - tests' compatibility_probe. The browser loads this binary directly and - requires output identifying OpenSSL 3.6.5; stale Rust-backend artifacts cannot - count as OpenSSL evidence. Do not reuse historical browser-build commands. - -BUILD CONFIGURATION AND BOUNDARIES - - infra/chat/openssl-wasi.conf adds a BASE_unix target with little-endian, - 32-bit bignums; cryptographic routines are unchanged. C uses -O2. No rand.patch, - unconditional entropy-success patch, or custom RNG is applied. SHA-256 checks - of all 5889 original regular files against the downloaded archive found none - changed or missing after build; only the target configuration was added. - OpenSSL 3.6.5 supports WASI getentropy in - providers/implementations/rands/seeding/rand_unix.c. - - Configure selects --with-rand-seed=getrandom. WASI libc's entropy request - reaches wasi_snapshot_preview1.random_get. The browser bridge services it - exclusively through crypto.getRandomValues, splitting requests at 65536 - bytes. There is no Math.random, fixed seed, or success-on-failure fallback. - The negative test replaces that API with a failure and requires rejection. - - Static builds disable assembly, async operations, automatic config loading, - DSO/modules, POSIX I/O, secure-memory arenas, shared libraries, sockets, - C stdio, thread pools, threads, and interactive console UI to avoid unsupported - capabilities. Ordinary WASM linear memory provides no secure-erasure, - locked-page, or delivered-JS protection. Rust output remains captured. - - _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply SDK process-ID emulation. - getuid/geteuid/getgid/getegid map to getpagesize, following common WASI port - configuration. The browser module has no real OS user IDs or privilege boundary. - These values supply neither randomness nor identity credentials. This portability - assumption does not emulate general POSIX security. - - Bindings use SDK libclang/WASI sysroot, target wasm32-wasip1, and - -fvisibility=default; without explicit visibility, bindgen omitted declarations. - Rust's bundled WASI libc precedes SDK libraries; reversed order failed to link - __wasi_init_tp. SDK libraries remain available for emulated-getpid. These were - toolchain integration failures; generated cryptographic bindings were not edited. - -BROWSER CAPABILITIES - - The WASI bridge supplies clocks, browser CSPRNG, empty arguments/environment, - EOF on stdin, and bounded in-memory stdout/stderr for this closed test. - It supplies no real files, preopened directories, sockets, or process launch. - Listed filesystem/network operations return NOTCAPABLE or BADF. Unknown - imports fail before instantiation. Standard output is capped at 65536 chars. - - Denials prevent accidental host storage/network access, not malicious JS from - the same server. Memory, clocks, randomness, and bridge imports remain trusted. - The test closes Chromium after 120 seconds if execution stalls. - -VERIFICATION - - Native: the same probe passes using OpenSSL 3.6.5. It checks v6 generation, - exact algorithm selection, protected-secret-key serialization/import, - signed UTF-8 encryption to both participants, decryption by each, wrong - password rejection, unexpected signer rejection, and ciphertext tampering. - - Browser: actual Chromium executes the OpenSSL WASI binary and passes the - same checks, then repeats with a fresh module instance. One observed normal - execution requested 1792 bytes across 72 browser CSPRNG calls. These counts - are observations, not cryptographic requirements or entropy estimates. - Removing secure randomness causes failure rather than a successful result. - A modified module requesting an unknown host import is also rejected before - instantiation. These negative tests require the expected failure reason. - - The harness observed no external requests, cookies, localStorage, - sessionStorage, IndexedDB databases, or service-worker registrations; responses - carry Cache-Control: no-store. This concerns only the probe. Browsers/OSes can - retain memory, caches, crash dumps, swap, and restored state. No complete chat - session existed at this checkpoint. Native tests, WASI build, and browser - execution are separate gates; functional tests and scans are not cryptographic - or side-channel audits. - -DEPENDENCY POLICY REVIEW - - RustSec reports no advisories for this resolved graph; no ignore was added. - Bans on wildcards, unknown Git/registry sources, and unreviewed duplicates remain. - cargo update cannot unify these upstream API generations. Exceptions match only - exact older versions; advisory/license checks still apply. Cargo.lock pins both sides: - - block-buffer 0.10.4 vs 0.12.1 - crypto-common 0.1.7 vs 0.2.2 - digest 0.10.7 vs 0.11.3 - Sequoia's Argon2 0.5.3 -> BLAKE2 0.10 uses the older digest API. - Root SHA-256 and the parser generator's SHA3 use the newer API. - - cpufeatures 0.2.17 vs 0.3.1 - Argon2 uses 0.2; root SHA-256 and parser-generator SHA3 use 0.3. - - generic-array 0.14.7 vs 1.4.5 - Argon2/BLAKE2 digest uses 0.14; SHA1 collision detection uses 1.x. - - getrandom 0.2.17 vs 0.4.3 - Sequoia's unknown-unknown WASM target dependencies declare 0.2; - the root CLI's tempfile uses 0.4. The current browser target is WASI, - whose OpenSSL entropy source is the bridge described above. The - all-target graph is still audited rather than excluding that edge. - - hashbrown 0.15.5 vs 0.17.1 - petgraph uses 0.15 directly and indexmap 2.14 uses 0.17. Both are - brought in by the parser generator at build time. - - itertools 0.13.0 vs 0.15.0 - shlex 1.3.0 vs 2.0.1 - bindgen uses older APIs; lalrpop and cc require the newer ones. - These are build dependencies, not a justification to skip auditing. - - syn 2.0.119 vs 3.0.9 - bindgen/prettyplease and Windows support use syn 2. Root derive - macros and other dependencies use syn 3. Changing that would require - upstream migration or unrelated dependency downgrades. - - The only new license allowance is ISC for bindgen/clang's libloading. - GNU-compatible licenses retain attribution; cargo-about covers the workspace. - OpenSSL C and SDK are outside Cargo. Distributed browser artifacts must carry - their original licenses/sources as required. OpenSSL LICENSE.txt is Apache-2.0. - Rust notices alone do not cover external C/toolchain distribution obligations. - -EXTERNAL LIBRARY MAINTENANCE - - cargo-deny excludes external OpenSSL C and SDK. Review separately; rebuild as needed: - https://openssl-library.org/news/timeline/ - https://openssl-library.org/news/vulnerabilities-3.6/ - Tested OpenSSL 3.6.5 is the 2026-09-29 security-patch release. A Rust audit - does not guarantee this pinned C version's future security. Document a supported - update policy before production deployment. - -REPRODUCE FROM THE STACK ROOT - - Native prerequisites: OpenSSL >=3.5 development headers/library, libclang, - pkg-config, and the pinned Rust toolchain. For the cross build also provide - a Linux x86_64 build host, Perl, make, curl, tar, and sha256sum. No root-owned - packages or system settings are changed by the cross-build script. - - cargo test --locked -p stack-chat-crypto - cargo run --locked -p stack-chat-crypto - sh infra/chat/build-openssl-wasi.sh - npm run build - PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" node_modules/.bin/playwright install chromium - PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto - cargo stack check - - The script pins versions/hashes, builds static OpenSSL under .tools/openssl-wasi, - builds the Rust WASI command, and prints its SHA-256. It uses no unverified - precompiled OpenSSL. Extracted sources may be reused; independent verification - needs a fresh ignored tools/build tree. Downloads/test artifacts are not secrets. - - Build logs and complete dependency trees are local ignored diagnostics in - .tools/chat-openssl-*.log and .tools/chat-openssl-duplicates.txt. The native - result and the browser result must explicitly identify the OpenSSL backend. - -REMAINING WORK - - Observed WASI artifact SHA-256 on this development machine: - 7fba6b96377619ef41f056d4027c4409107372e9300b2dd69be865e72892df79 - This is a diagnostic identifier, not a cross-host reproducibility promise. - - This checkpoint removed the Rust-backend blocker and demonstrated fixed - algorithms in-browser. It was not a complete messenger, OpenSSL/WASI/JIT timing - audit, LibreJS certification, or proof against malicious delivery. File format, - authentication, server/database, quotas, hostile-input handling, pagination/counts, - recovery, session lifecycle, and browser-state tests remained to implement. diff --git a/docs/CHAT-PRODUCTION.txt b/docs/CHAT-PRODUCTION.txt index 2507e07..6c901dc 100644 --- a/docs/CHAT-PRODUCTION.txt +++ b/docs/CHAT-PRODUCTION.txt @@ -16,7 +16,7 @@ along with this program. If not, see . PRODUCTION CHAT INTEGRATION - Implemented on main. Builds/tests do not activate the VPS. Experimental; + Builds/tests do not activate the VPS. Experimental; no independent cryptographic or application security audit. Read CHAT-APPLICATION.txt limitations before private use. @@ -57,7 +57,7 @@ BUILD ON THE WORKSTATION isolated musl/GCC built-in and host Linux UAPI headers, excluding glibc. Static OpenSSL has no dynamic providers and seeds through getrandom. The native compatibility probe must pass. WASI uses separately verified SDK/OpenSSL - inputs in CHAT-OPENSSL-MIGRATION.txt. Cryptographic algorithms stay fixed. + inputs in CHAT-CRYPTO.txt. Cryptographic algorithms stay fixed. Inputs cache under .tools/. Version/source/flag changes require an OpenSSL rebuild. Updates require upstream advisory review, every affected URL/hash, @@ -81,7 +81,7 @@ LOCAL MODES AND ORIGIN BINDING var/data/chat-local Production: - ./bin/stack up --production + ./bin/run up --production https://CHAT_HOST/ with the configured HTTPS_PORT var/data/chat @@ -97,10 +97,8 @@ LOCAL MODES AND ORIGIN BINDING DEPLOY AND CREATE THE REAL OWNER FILE - Follow OPERATIONS.txt. Extract the verified release's new management binary - into var/incoming/bootstrap; invoke with --root set to the current installation. - The new binary understands its release schema, verifies the archive, and - installs chat artifacts before startup. + Follow OPERATIONS.txt. Activate through the verified new management binary + with its matching runtime; activation installs chat artifacts before startup. The host needs rootless Podman prerequisites, user linger, low-port permission, DNS A/AAAA records for CHAT_HOST and www.CHAT_HOST, and the normal 80/443 firewall rules. Caddy obtains and renews public certificates automatically. @@ -111,7 +109,7 @@ DEPLOY AND CREATE THE REAL OWNER FILE failed containers. Complete matching-architecture releases need no compilation or registry pull. Check all four services after activation: - ./bin/stack status + ./bin/run status Check HTTPS, HTTP-to-HTTPS and www redirects, the homepage Services entry, /LIMITATIONS.txt, /javascript.html, and /SOURCE.tar.gz on the chat origin. @@ -127,7 +125,7 @@ DEPLOY AND CREATE THE REAL OWNER FILE BACKUP, RESTORE, AND KEY LOSS A complete encrypted application export: - ./bin/stack backup --include-images + ./bin/run backup --include-images Copy .gpg and .sha256 off the VPS; local backups cannot survive VPS loss. Configured recipients apply. Export includes one .env, online SQLite snapshots @@ -137,9 +135,9 @@ BACKUP, RESTORE, AND KEY LOSS Owner private file and GnuPG secret key need separate backups. Restore to a new directory, verify, then explicitly start production mode: - ./bin/stack restore /path/export.tar.gz.gpg /path/stack-restored + ./bin/run restore /path/export.tar.gz.gpg /path/stack-restored cd /path/stack-restored - ./bin/stack up --production + ./bin/run up --production Restore defaults to local mode with fresh project names, avoiding replacement of another installation. up --production reselects production database/origin. @@ -152,10 +150,10 @@ BACKUP, RESTORE, AND KEY LOSS reset deletes every conversation/message; it decrypts and recovers nothing. Reset has no HTTP endpoint and requires filesystem/Podman access: - ./bin/stack chat --owner-fingerprint - ./bin/stack down - ./bin/stack chat --reset-owner PASTE_CURRENT_OWNER_FINGERPRINT_HERE - ./bin/stack up --production + ./bin/run chat --owner-fingerprint + ./bin/run down + ./bin/run chat --reset-owner PASTE_CURRENT_OWNER_FINGERPRINT_HERE + ./bin/run up --production Reset requires a stopped server and matching full fingerprint. Root .env and exports survive. Create a new production owner file; old visitor files reject @@ -175,7 +173,7 @@ VERIFICATION configuring fixture .localhost hostnames, WEB_BIND=127.0.0.1, HTTP_PORT=8181, HTTPS_PORT=8443, and LOCAL_HTTP_PORT=8180: - ./bin/stack up --production + ./bin/run up --production CHAT_TEST_ROOT=/absolute/disposable/stack CHAT_TEST_ORIGIN=https://chat.fixture.localhost:8443 PLAYWRIGHT_BROWSERS_PATH=.tools/browsers npm run test:chat Run from the source checkout for the harness and Playwright installation. @@ -189,34 +187,3 @@ VERIFICATION Production uses ordinary trusted public certificates with no TLS bypass. Passing these checks demonstrates recovery and functional compatibility; it does not establish absence of attacks or constitute an independent audit. - -RECORDED RESULTS, 2026-10-31 - - The complete native workspace check passed: formatting, strict Clippy, - 14 tests, private rustdoc, cargo-deny with no ignored advisories, and - cargo-about generation. Static musl and WASI OpenSSL 3.6.5 probes passed - without changing the required RFC 9980 algorithms. - - Direct development passed in Chromium. Packaged HTTPS passed independently - in Chromium and Firefox with GNU LibreJS 7.21.1 active; its unlicensed-script - control was rejected. Both packaged tests recovered a complete encrypted - application export, including saved images, into a new directory, then - reopened the unchanged owner/visitor files and decrypted the history. - Export and checksum permissions were verified as 0600. - - Runtime inspection verified read-only root filesystem, zero effective - capabilities, no chat host ports, and unless-stopped restart policy. Killing - the disposable chat process with SIGKILL caused an automatic restart, and - its owner fingerprint persisted. The supplied systemd user unit passed - systemd-analyze verification; this did not reboot the real VPS. - - curl independently verified the fixture CA chain and hostname for canonical - chat, www redirect, and homepage. The homepage Services limitations link - resolved correctly. The source download matched the packaged source archive - byte-for-byte and contained only .env.example, without actual .env. - - Upgrade testing started the 2026-10-30 three-service release, then activated - the chat-enabled release through its new prebuilt management binary. All - four services started, existing data and SearXNG secret persisted, and the - new owner setup token was generated in the same root .env. These tests used - disposable local data; they did not register a real production owner. diff --git a/docs/CONFIGURATION.txt b/docs/CONFIGURATION.txt index f0f8457..edd3d30 100644 --- a/docs/CONFIGURATION.txt +++ b/docs/CONFIGURATION.txt @@ -115,7 +115,7 @@ RENDER AND APPLY Production uses prebuilt management command/images: - ./bin/stack up --production + ./bin/run up --production Running file servers can read rendered website updates. Listener, container environment, and loaded Caddy changes need the service update above. diff --git a/docs/GUIX.txt b/docs/GUIX.txt index 05aa572..89f4c9a 100644 --- a/docs/GUIX.txt +++ b/docs/GUIX.txt @@ -37,9 +37,8 @@ BUILD LOCAL 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. Former -RR wrapper creates one-ID - user namespace; privileged helpers cannot map host subordinate IDs there. - Builder's canonical store could bypass relocation and hide this defect. + 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 @@ -48,7 +47,7 @@ MAKE CARRY FILE Production: create consistent encrypted export; copy and verify it locally: - ./bin/stack backup --include-images + ./bin/run backup --include-images Bundle a trusted freshly checked release with that export: @@ -132,23 +131,19 @@ CHECKS; DEPLOYMENT GATE No strict Rust/Clippy/Rustdoc, typed ESLint, LibreJS or crypto gate weakened. Existing documented lint/dependency exceptions remain unchanged. - Clean-host test exposed missing distro container signature policy. Image load - now supplies a temporary policy: reject by default; accept local Docker/OCI - archives only. Export supplies a separate temporary policy accepting only - containers-storage; registry transport remains rejected. Verified archive - manifests and architecture/layer/image-ID locks - still gate startup. Registry transport is not accepted by this load policy; - global host policy stays unchanged. Policies are generated by AGPL Rust code; - the foreign schema rejects comment/legal metadata, so no headerless source file - is added. The initial failed VM retained decrypted data and pending intent. - - A second clean-host check found legacy backups had collapsed Caddy's /data - and /config into one caddy/ tree. After manifest verification, backup restore - moves credentials into data/caddy and autosave into config/caddy; bytes and - permissions are retained. Conflicting old/new layouts are refused before - publishing a destination. New exports copy /data/. and /config/. separately. - - Before production, in order: + 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. @@ -160,48 +155,41 @@ CHECKS; DEPLOYMENT GATE 6. Fresh verified independent production backup; deploy only prebuilt release; verify public routes/state, retain prior release/runtime and encrypted export. - Rollback: stop new containers before old runtime binds production ports. - Keep .env/var/data; never delete closure entries used by running processes. - -VERIFIED — 2026-11-01 - - Offline KVM guest: official Debian 12 x86_64 image, no initial /gnu, Guix, - Podman, Git or GPG. Host supplies only documented kernel/account prerequisites. - Network restricted before bootstrap; readonly carry file plus separately - forwarded GPG agent; no private key copied. Bootstrap and one restore command - loaded four saved images and started all services without source builds. - - Strict HTTPS verified all four domains using original certificates; both WKD - routes matched the original key. All 651 immutable snapshot files matched - after legacy Caddy path normalization: 18 active TLS keys, 16 certificates. - All 126 release source/binary files matched. Original .env, owner fingerprint - and every canonical SQLite table matched. Production snapshot contained zero - conversations/messages; original real-message decryption therefore untestable. - Disposable Chromium/Firefox tests verified both participant identities, - 54-message history, encrypted snapshots/recovery and GNU LibreJS 7.21.1. - - Full local release gates passed live npm/Rust advisory checks, strict fmt, - Clippy, Rustdoc, typed ESLint and both TypeScript compilers; no suppressions. - Required OpenSSL 3.6.5 hybrid suites, protected OpenPGP v6 identity, wrong-pass - and tamper controls passed. Synthetic shell tests cover bootstrap collisions, - interrupted recovery/retry, lock exclusion and child descriptor closure. - - Runtime: 500687877 bytes, SHA256: + 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 - Guix channel 230aa373f315f247852ee07dff34146e9b480aec; 113 store entries. - Git 2.52.0, GnuPG 2.4.7, Podman 5.7.0, conmon 2.1.13, crun 1.25.1. - Empty helper lists force PATH lookup, selecting packed conmon/crun. Ordinary - host config stays unchanged. Rootless cleanup handles immutable user-owned - temporary trees without changing symlink targets or the published pack. + 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 - Deployment gate above still applies to each carried artifact. Retain complete - original worktree, binary/launcher, installed user unit and engine storage - independently before activation. Reload the installed unit after switching to - bin/run. For rollback stop Guix containers first, retain their engine tree, - restore prior engine path, binary/launcher and unit; invoke prior activation - with host PATH and Guix CONTAINERS_CONF overrides unset. Keep current .env and - var/data. An old release alone does not remove a newer Guix launcher/runtime. - Private evidence: var/guix-*.log and .tools/guix-production/. These are ignored - and include private state hashes; do not publish them with public source. + Private evidence: var/guix-*.log and .tools/guix-production/; these include + private state hashes and stay ignored. Do not publish them with source. diff --git a/docs/MAP.txt b/docs/MAP.txt index de546b8..dc4da33 100644 --- a/docs/MAP.txt +++ b/docs/MAP.txt @@ -15,7 +15,7 @@ along with this program. If not, see . FILE MAP - Public tree: every tracked file plus pending Guix source/docs and this map. + Public tree: every tracked file and its use. Static inventory; update when public paths change. Root: stack/. Private/generated trees below list roles only; contents stay unlisted. @@ -64,10 +64,10 @@ stack/ Personal service source, build recipes and recovery tooling. | `-- README.txt Workspace crate roles and shared policy. |-- docs/ Plaintext setup, policy, security and recovery guides. | |-- CHAT-APPLICATION.txt Chat setup, protected files, user flows, recovery, security limits. -| |-- CHAT-OPENSSL-MIGRATION.txt OpenSSL crypto migration evidence, dependencies, reviewed exceptions. +| |-- CHAT-CRYPTO.txt Current crypto/backend builds, provenance, boundaries and dependency exceptions. | |-- CHAT-PRODUCTION.txt Chat release packaging, HTTPS activation, owner setup, production recovery. | |-- CONFIGURATION.txt Supported root settings, validation, public/private state boundaries. -| |-- GUIX.txt Experimental Guix capsule, canonical-store bootstrap, gates and blockers. +| |-- GUIX.txt Guix capsule, canonical-store bootstrap, recovery gates and rollback. | |-- LINTING.txt Strict Rust/browser checks, generated-code limits, justified exceptions. | |-- MAP.txt This public-file tree and private/runtime directory roles. | |-- OPERATIONS.txt Stack development, encrypted backup/restore, host setup, deployment. @@ -91,7 +91,7 @@ stack/ Personal service source, build recipes and recovery tooling. | | | `-- supervisord.conf Run nginx and fcgiwrap inside the cgit container. | | `-- chat/ Prebuilt chat image recipe. | | `-- Containerfile Package prebuilt static chat server and web/crypto assets. -| |-- guix/ Experimental Guix runtime and recovery capsule tooling. +| |-- guix/ Guix runtime and recovery capsule tooling. | | |-- bootstrap.sh Root-owned canonical closure installation; refuse managed/foreign stores. | | |-- build.sh Capture Guix channels; build canonical runtime pack; probe Podman info. | | |-- bundle.sh Validate paired release; wrap encrypted backup, release and bootstrap. @@ -187,6 +187,6 @@ PRIVATE AND GENERATED PATHS — NOT PUBLIC SOURCE not enumerate private files. Backups must keep .env and var/data; public Git alone cannot recover services. See OPERATIONS.txt and GUIX.txt. - Experimental host integration: /gnu/store holds canonical Guix packages; + Host integration: /gnu/store holds canonical Guix packages; /gnu/.stack-runtimes holds root-owned package install records and lock. Neither holds application configuration, databases or TLS keys. diff --git a/docs/OPERATIONS.txt b/docs/OPERATIONS.txt index 91ab10b..bf675be 100644 --- a/docs/OPERATIONS.txt +++ b/docs/OPERATIONS.txt @@ -15,84 +15,82 @@ along with this program. If not, see . STACK OPERATIONS - Root: ~/stack. Develop on local Linux; production: ops@23.155.44.67. + Root: ~/stack. Workstation: local Linux. Production: ops@23.155.44.67. Public source: https://git.doan.luuduct.ai/stack. - GNU Affero General Public License, version 3 or later. - Experimental Guix capsule: GUIX.txt. Commands below use host Podman. + Production commands use bin/run, which selects packed Guix tools/config. + Direct bin/stack uses host tools; never mix engines against the same storage. KEEP - Full --include-images encrypted export: public source, compiled CLI, .env, - persistent data, certificates, bare repos, working Git history if present, - exact service images, checksum manifest. No old server filesystem needed. - Excludes engine storage, caches, old exports, host packages/SSH keys/firewall, - disks, OS configuration. Recreate host setup separately. - Public Git alone cannot recover .env, live data, TLS keys, or var/ repos. - - Decryption needs GPG private key + passphrase. Keep key export/revocation - certificate in KeePassXC; keep independent database copy. Server holds public - key only. Never send private key/password through chat. - Default recipient: 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6. + For new-host recovery keep the carry file described in GUIX.txt: encrypted + application export plus matching checked release, Guix runtime and bootstrap. + Private state is encrypted; public tools/source are plaintext. Keep its trusted + checksum and an independent copy on another device/location. + It preserves one .env, databases, TLS keys, bare repos, working Git history, + binaries, browser assets, sources and exact service images. Engine storage, + caches, host accounts/SSH/firewall, disks and OS configuration are excluded. + Public Git alone cannot recover private state. + + Keep GPG secret key/passphrase separately; default backup recipient: + 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 + Keep an independent protected key export/revocation certificate. Chat owner + and visitor private identity files/passwords also need separate backups. + Never send private keys or passwords through chat. DEVELOP LOCAL - From ~/stack on workstation: - cargo stack dev cargo stack status cargo stack down - Open http://localhost:8080, http://localhost:8080/git/, - http://localhost:8080/search/. Alternate port: - - cargo stack dev --port 9080 - - Local mode: loopback only, local links, unchanged public DNS, no public TLS. - Missing .env: create with random SearXNG secret. Edit secrets there; never commit. - infra/: service settings. web/: content. crates/: new backends; add workspace members. - infra/services.toml declares services. Each gets only listed environment keys. + Open http://localhost:8080, /git/, /search/, http://chat.localhost:8080/. + Alternate port: cargo stack dev --port 9080. Local mode binds loopback only; + no public TLS or DNS changes. Missing .env is initialized with random secrets. + infra/services.toml lists each service's environment, mounts and health check. - Project engine storage: var/podman/. Bare 'podman ps' uses another store. - Inspect project containers from ~/stack: + Local development uses host Podman. Project storage is var/podman; bare + podman ps uses another store. From the local checkout: podman --root "$PWD/var/podman/storage" --runroot "$PWD/var/podman/run" ps -a - Keep both global options for 'logs CONTAINER', 'exec CONTAINER', 'images'. - cargo stack status gives names. Never migrate var/podman/; export images. + Keep both global options for logs/exec/images. Use bin/run status on production. + Never transfer var/podman as backup; export saved images instead. -CHECK LOCAL - - Install development audit tools once: +CHECK AND BUILD LOCAL cargo install --locked --version 0.20.2 cargo-deny --root .tools cargo install --locked --version 0.9.3 --features cli cargo-about --root .tools - cargo stack check + cargo run --release --locked -- release - Checks Guix shell/bootstrap fixtures (bubblewrap), formatting, workspace Clippy, tests, private - rustdoc, advisories, duplicate versions, sources, licenses. cargo-about - regenerates THIRD-PARTY-LICENSES.html. Format with cargo fmt --all. - Dependency changes: update Cargo.lock deliberately; run full check; commit - lockfile and license page. Never disable audit failures to ship. + Check covers TypeScript compilers/typed ESLint/npm audit, Guix shell fixtures, + formatting, strict workspace Clippy/tests/private Rustdoc, advisories, exact + duplicate exceptions and licenses. cargo-about regenerates the license page. + Dependency edits require deliberate lockfile review, full checks and notices. + Never waive failures. Format with cargo fmt --all. -BACK UP PRODUCTION + Build the Guix pack first when adding/changing runtime tools: GUIX.txt. + Release builds static Rust/chat WASM, corresponding source, cgit/chat images + and complete image export. Output: dist/stack-release-TIMESTAMP.tar.gz and + .sha256. Actual .env, live data, .git and prior backups are excluded. + Build/test locally; no compiler or registry pull is needed on the matching + x86_64 production host. Restore/inspect a disposable release before deployment. - On VPS: +BACK UP PRODUCTION cd ~/stack - ./bin/stack backup --include-images + ./bin/run backup --include-images Briefly pauses Caddy/cgit/SearXNG during file copy; resumes before compression - and encryption. Chat stays live for SQLite online backup. Snapshots include - chat and chat-local; exclude raw journals and server locks. - Verifies public recipient fingerprint, checks bare repos, saves exact images: + and encryption. Chat stays live for SQLite online backup. Export includes + chat/chat-local snapshots; excludes raw journals and server locks. + Verifies recipient fingerprint, bare repos and exact image identities. + Output has restrictive permissions: var/exports/stack-TIMESTAMP.tar.gz.gpg var/exports/stack-TIMESTAMP.tar.gz.gpg.sha256 - Restrictive permissions. Removes temporary plaintext on success/handled errors. - No scheduled backup. Choose retention and off-host copies yourself. - On workstation, copy actual filenames printed above: + Copy both files off the VPS; substitute actual filenames: umask 077 mkdir -p ~/stack/var/backups/production @@ -101,166 +99,118 @@ BACK UP PRODUCTION cd ~/stack/var/backups/production sha256sum -c stack-TIMESTAMP.tar.gz.gpg.sha256 - Replace TIMESTAMP. Checksum detects corruption; keep trusted export + checksum. - Keep another copy on separate device/location. After verification, optionally - delete matching VPS export files only. Never delete var/data or .env. - ./bin/stack backup without images needs registry/build access for local startup; - not standalone production recovery. Prefer --include-images. - -RESTORE ON LINUX - - Host tools: Podman, Git, GnuPG, rootless networking prerequisites. - Target user needs >=65536 subordinate UIDs/GIDs. Use distro-supported Podman. - Prebuilt binary/images: x86_64 Linux (linux/amd64). - Copy export + checksum; verify checksum first. If needed, import key: - - gpg --import private-key-backup.asc - - GnuPG prompts locally. Never pass passphrase through arguments or .env. - Existing compatible CLI: - - ./bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack-restored - - Destination must not exist. Restore decrypts privately; rejects unsafe paths - and links; checks every manifest entry; assigns unique project identity; - defaults local; starts nothing; never overwrites existing installation. + No scheduled backup. Choose retention/off-host copies; remove matching VPS + exports only after verification. Never delete .env or var/data. + Export without --include-images is not standalone production recovery. + Pair the verified export with a checked release using infra/guix/bundle.sh; + the resulting carry file restores with the matching manager/source/runtime. - No CLI/toolchain yet: bootstrap trusted export with GnuPG/tar. Bash/fish: +RECOVER ON LINUX - umask 077 - mkdir -p ~/stack-bootstrap - gpg --output ~/stack-bootstrap/export.tar.gz --decrypt /path/to/stack-TIMESTAMP.tar.gz.gpg - tar -xzf ~/stack-bootstrap/export.tar.gz -C ~/stack-bootstrap stack/bin/stack - ~/stack-bootstrap/stack/bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack - - Bootstrap executes extracted code: use only your trusted export. - Rust restore validates full archive. After success, delete ~/stack-bootstrap: - it holds decrypted archive. Retain encrypted backup. - Test restored stack locally first: - - cd ~/stack - ./bin/stack up --local --port 8080 - ./bin/stack status + Follow GUIX.txt for host prerequisites and GPG key access. On a fresh x86_64 + Linux host, as the service user, with a new destination: - Saved images load automatically; image lock verifies identities. Matching - architecture needs no registry pull/build. Check homepage, Git, search, WKD. - ./bin/stack down before production setup. - Same restore accepts release archives: source/binary/images, no .env/live data. - Starting restored release creates fresh state; cannot recover lost data. + tar -xzf recovery.tar.gz + sudo ./stack-recovery/bootstrap + ./stack-recovery/restore /home/ops/stack -NEW PRODUCTION HOST + Capsule verifies/decrypts the export, activates its paired release and starts + four services. A failed activation can resume with the same restore command. + Keep hostname/origin and identity files unchanged; repoint DNS when IP changes. + Recovery does not configure host SSH, firewall, account or boot startup. - Restore into target user's ~/stack. Debian: review infra/host-setup.sh; - replace 'ops' if needed; run: + A raw format-2 export can be restored for inspection on a host that already + has compatible tools and any required Guix closure: - sudo sh ~/stack/infra/host-setup.sh + ./bin/run restore /path/export.tar.gz.gpg /path/stack-restored - Installs Podman/network tools, checks existing subordinate ranges, enables - user linger, sets: + Raw restore validates all manifest paths/hashes, defaults local mode with a + fresh project identity, starts nothing and refuses an existing destination. + Public release archives restore similarly but contain no private data. + An old raw export may lack the launcher/runtime; use a paired capsule for + complete recovery. Test owner/visitor identities and history after recovery. - net.ipv4.ip_unprivileged_port_start=80 +HOST AND BOOT SETUP - System-wide rootless ports >=80; ports <80 stay privileged. SSH/firewall unchanged. - Missing ranges: allocate unused ranges through distro account tools; rerun. - Other distros: equivalent packages, sysctl, linger. Supplied boot unit needs - systemd user manager. - - Host/provider firewall: allow 22/tcp, 80/tcp, 443/tcp, 443/udp for applicable - IPv4/IPv6. Never publish backend container ports. Preserve SSH; verify address - before changing DNS. A: new IPv4. AAAA: working new IPv6 only; remove stale AAAA. - Hosts live in root .env; edit and reapply rendered settings. infra/Caddyfile - is public template. Keys/render-only commands: CONFIGURATION.txt. - Preserve www aliases and openpgpkey names pointing to canonical hosts. + Host/provider firewall: retain SSH and allow HTTP 80/tcp, HTTPS 443/tcp+udp + for applicable IPv4/IPv6. Never publish backend container ports. A points to + the new IPv4; publish AAAA only with working IPv6. Keep www/WKD aliases. + Rootless IDs, mapping helpers, runtime directory and low-port sysctl: GUIX.txt. + Debian host-tools fallback: infra/host-setup.sh installs distro Podman and + prerequisites. Guix supplies its own Podman/network tools. cd ~/stack - ./bin/stack up --production - - Production binds 80/tcp, 443/tcp, 443/udp. Caddy issues/renews certificates, - redirects HTTP to HTTPS and www to canonical URLs. State: var/data/caddy/. - Public issuance needs working public DNS/connectivity. - Enable boot startup: - + ./bin/run up --production systemctl --user link ~/stack/infra/stack.service systemctl --user daemon-reload systemctl --user enable --now stack.service systemctl --user status stack.service - Unit assumes ~/stack; edit paths for other location. Current unit uses bin/run: - confirm launcher exists before enabling. Linger keeps user service alive - without login. Containers use unless-stopped restart policy. + Administrator enables linger for the service user. Unit assumes ~/stack; + adapt paths for another location. Caddy stores credentials in var/data/caddy, + issues/renews public certificates and redirects HTTP/www. ACME needs public + DNS/connectivity. Containers use unless-stopped restart policy. - Optional former VPS SSH Git path; administrator only: + Current VPS SSH Git compatibility path /srv/git/stack links to + /home/ops/stack/var/data/repos/stack. Preserve it for existing clone URLs; + public HTTPS Git needs no host symlink. - sudo install -d -m 755 /srv/git - sudo ln -s /home/ops/stack/var/data/repos/stack /srv/git/stack +DEPLOY A CHECKED RELEASE - Never overwrite existing repo. Current VPS already has this layout link. - Public HTTPS Git needs no host symlink. + Review/stage public changes, sign and push the current branch: -BUILD AND DEPLOY - - Develop/check on workstation: - - cd ~/stack - cargo stack release - - Release reruns full check; builds static Rust CLI/chat + browser WASM; - packages matching source; builds cgit/chat containers; saves configured images. - Output: dist/stack-release-TIMESTAMP.tar.gz + .sha256. - Excludes .env, live data, .git history, previous backups. - Restore into fresh local test directory; up --local on free port; inspect - four services; stop test. Commit/push reviewed public changes: - - git add -A git diff --cached --stat git commit -S3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 -m 'Describe the change' - git push origin main - - Review staged files. .gitignore excludes secrets/runtime/artifacts. - Use RSA-2048 signed commits; author/key: POLICY.txt. - Deploy archive, never git pull. Copy actual filenames: + git push origin HEAD - ssh ops@23.155.44.67 'mkdir -p ~/stack/var/incoming' - scp dist/stack-release-TIMESTAMP.tar.gz ops@23.155.44.67:stack/var/incoming/ - scp dist/stack-release-TIMESTAMP.tar.gz.sha256 ops@23.155.44.67:stack/var/incoming/ + Do not deploy with git pull. Take a verified independent export first; retain + complete old worktree/binary/unit, var/state.json, image-lock.json, images.tar + and prior engine storage for rollback. Guix upgrade/rollback rules: GUIX.txt. + Copy the checked public release and checksum to ~/stack/var/incoming. + Before activation install any new closure through its verified capsule + bootstrap; an existing managed Guix store needs separate planning. - On VPS: + Bash on VPS, after that bootstrap; substitute actual TIMESTAMP: - cd ~/stack/var/incoming - sha256sum -c stack-release-TIMESTAMP.tar.gz.sha256 + ( + set -eu cd ~/stack - mkdir -p var/incoming/bootstrap - tar -xzf var/incoming/stack-release-TIMESTAMP.tar.gz -C var/incoming/bootstrap stack/bin/stack - var/incoming/bootstrap/stack/bin/stack --root "$PWD" activate var/incoming/stack-release-TIMESTAMP.tar.gz - ./bin/stack status - rm -rf -- var/incoming/bootstrap - - Use verified NEW release binary: old binary may reject new service schema. - Activation verifies archive/platform, replaces public source/binary, loads - exact images, preserves .env/var/data, replaces running containers. No compiler, - Rust build, image build, or private-config overwrite. Expect brief interruption. - Take verified export before changes needing recovery. Verify production; - optionally delete corresponding incoming archive/checksum. - -RECOVERY LIMITS; ROTATE KEYS - - Application export is not bootable VPS image. Accounts, SSH, firewall, - packages remain host duties. Other CPU architecture needs native release. - Registry needed only without saved images or when rebuilding another target. - State: files, Git repos, Caddy, encrypted SQLite chat. SQLite uses online backup. - Recovery proof: reopen BOTH participants' unchanged identities; decrypt history. - See CHAT-PRODUCTION.txt. Future databases need supported snapshot/recovery. - - New backup recipient: publish public key, update default fingerprint, test - encryption/restore. Single export: --recipient FULL_FINGERPRINT --key /path/public-key.asc. - Retain old secret keys while any retained export needs them. - Restore accepts verified format-2 application archives. + STACK_ROOT=$PWD + STACK_RELEASE=$PWD/var/incoming/stack-release-TIMESTAMP.tar.gz + (cd var/incoming && sha256sum -c stack-release-TIMESTAMP.tar.gz.sha256) + STACK_STAGE=$(mktemp -d "$PWD/var/incoming/.activate.XXXXXX") + tar -xzf "$STACK_RELEASE" -C "$STACK_STAGE" stack/bin/stack stack/bin/guix-runtime.tar.gz stack/infra/guix/containers.conf + mkdir "$STACK_STAGE/runtime" + tar -xzf "$STACK_STAGE/stack/bin/guix-runtime.tar.gz" --no-same-owner -C "$STACK_STAGE/runtime" + env PATH="$STACK_STAGE/runtime/bin:$PATH" CONTAINERS_CONF="$STACK_STAGE/stack/infra/guix/containers.conf" "$STACK_STAGE/stack/bin/stack" --root "$STACK_ROOT" activate "$STACK_RELEASE" + systemctl --user daemon-reload + systemctl --user restart stack.service + ./bin/run status + chmod -R u+w -- "$STACK_STAGE" + rm -rf -- "$STACK_STAGE" + ) + + Use the NEW manager and matching runtime. Activation verifies archive/platform, + replaces public source/binaries/images, preserves .env/var/data/.git and + replaces containers. Expect interruption. The installed unit may already link + to infra/stack.service; reload its definition rather than copying over itself. + If activation fails, retain staging/data and diagnose before retry or rollback. + Verify public routes, TLS/WKD, owner and database before removing incoming files. + +RECOVERY LIMITS; KEY ROTATION + + Other CPU architectures need native release builds. Host DNS/PTR, firewall, + accounts and SSH remain outside the carry file. New databases need consistent + snapshot/recovery support before inclusion. Preserve both participants' chat + files/passwords; server backups cannot replace them. CHAT-PRODUCTION.txt. + + For a new backup recipient, publish its public key, update root .env and test + encryption/recovery. One export can select --recipient FULL_FINGERPRINT and + --key /path/public-key.asc. Keep old secret keys while retained exports need them. LICENSE PAGE; REDIRECTS - cargo-about generates web/THIRD-PARTY-LICENSES.html. Caddy serves canonical - https://doan.luuduct.ai/THIRD-PARTY-LICENSES.html; other deployments use SITE_HOST. - Website/chat links share page; local preview uses local main-site origin. - cgit Source/Raw expose generated file; chat /THIRD-PARTY-LICENSES.html redirects - to canonical page. Source exports retain offline license texts. - Homepage /message and /message/ redirect to current chat origin. + cargo-about generates web/THIRD-PARTY-LICENSES.html. Caddy serves it on the + main-site origin; chat redirects its former local license URL there. Source + exports retain offline license texts. cgit Source/Raw expose the generated file. + Homepage /message and /message/ redirect to the configured chat origin. diff --git a/docs/POLICY.txt b/docs/POLICY.txt index 747b774..c3dffae 100644 --- a/docs/POLICY.txt +++ b/docs/POLICY.txt @@ -56,7 +56,7 @@ STRICTNESS cargo-deny rejects duplicate versions, wildcard requirements, unknown registries/Git sources, and yanked versions. Chat has exact-version duplicate - exceptions reviewed in CHAT-OPENSSL-MIGRATION.txt. Ignore no RustSec advisory. + exceptions reviewed in CHAT-CRYPTO.txt. Ignore no RustSec advisory. Scope first-party AGPL/required dependency license allowances per package. Allowlist retains requested permissive licenses and Zlib; unused allowances may produce informational audit warnings. @@ -78,7 +78,7 @@ PORTABILITY packages, subordinate UID/GID allocations, one low-port sysctl, user linger, systemd user-service link. Host SSH keys, firewall policy, accounts, and disks are host administration, excluded from application exports. - Experimental Guix capsule also installs canonical /gnu/store packages and + Guix capsule bootstrap installs canonical /gnu/store packages and /gnu/.stack-runtimes install records. Private application state stays in stack/. Releases include static x86_64 Linux management/chat commands, WASM browser diff --git a/src/app/files.rs b/src/app/files.rs index e96e5a4..8b4f70b 100644 --- a/src/app/files.rs +++ b/src/app/files.rs @@ -43,7 +43,6 @@ pub(super) const SOURCE: &[&str] = &[ ".cargo", ".gitignore", ".gitattributes", - ".forgejo", "src", "tests", "crates", diff --git a/src/app/project.rs b/src/app/project.rs index ac55ff3..bb7fa5a 100644 --- a/src/app/project.rs +++ b/src/app/project.rs @@ -47,14 +47,14 @@ pub(super) struct Settings { pub(super) online: bool, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize)] #[serde(crate = "serde", deny_unknown_fields)] pub(super) struct Configuration { pub(super) format: u32, pub(super) services: BTreeMap, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize)] #[serde(crate = "serde", deny_unknown_fields)] pub(super) struct Service { pub(super) image: String, @@ -73,7 +73,7 @@ pub(super) struct Service { tmpfs: Vec, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize)] #[serde(crate = "serde", deny_unknown_fields)] struct Mount { source: String, -- cgit v1.2.3