diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/CHAT-APPLICATION.txt | 298 | ||||
| -rw-r--r-- | docs/CHAT-CRYPTO.txt | 170 | ||||
| -rw-r--r-- | docs/CHAT-OPENSSL-MIGRATION.txt | 254 | ||||
| -rw-r--r-- | docs/CHAT-PRODUCTION.txt | 244 | ||||
| -rw-r--r-- | docs/CHAT-SECURITY-REVIEW.txt | 123 | ||||
| -rw-r--r-- | docs/CONFIGURATION.txt | 158 | ||||
| -rw-r--r-- | docs/GIT-HISTORY.txt | 84 | ||||
| -rw-r--r-- | docs/GUIX.txt | 195 | ||||
| -rw-r--r-- | docs/LINTING.txt | 243 | ||||
| -rw-r--r-- | docs/MAP.txt | 192 | ||||
| -rw-r--r-- | docs/OPERATIONS.txt | 409 | ||||
| -rw-r--r-- | docs/POLICY.txt | 145 | ||||
| -rw-r--r-- | docs/TYPESCRIPT.txt | 153 |
13 files changed, 1238 insertions, 1430 deletions
diff --git a/docs/CHAT-APPLICATION.txt b/docs/CHAT-APPLICATION.txt index 7ebac77..6a90477 100644 --- a/docs/CHAT-APPLICATION.txt +++ b/docs/CHAT-APPLICATION.txt @@ -16,34 +16,28 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. EXPERIMENTAL PRIVATE CHAT - Available on main. Direct development and a prebuilt Podman/HTTPS - deployment path are implemented. See CHAT-PRODUCTION.txt for the release, - production setup, and recovery procedure; feature work does not itself merge - main or activate the VPS. - This is working experimental software, not an independently audited messenger. + 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 - A Rust HTTP server and a Rust cryptographic command compiled to WASI. - Strict TypeScript implements the browser interface and a capability-limited - WASI host, compiled to small, readable, free JavaScript classic scripts. - Build configuration and source licensing are documented in TYPESCRIPT.txt. Text is displayed through textContent. - SearXNG and the other existing services are unchanged. - - Startup shows an ASCII download bar, percentage, and received/total MiB - while fetching the cryptographic module. If its size is unavailable, it shows - received MiB. A 45-second gap without download progress aborts with a reload - instruction; slow transfers continue while bytes arrive. After download, - a separate status identifies the connection to chat. No browser cache or - storage was added. - - Visitors choose CREATE NEW CHAT or OPEN CHAT FILE. Creation generates a - purpose-specific identity locally and downloads its protected file. Reopening - the exact downloaded file and entering its password is required before the - first authentication or message. No username/password account is created. - Returning users select their file and password again. The stable file does - not need updating as messages arrive. The owner has a separate chat identity - and inbox; the canonical RSA OpenPGP key is not used for chat encryption. + Rust HTTP server and Rust/WASI cryptography. Strict TypeScript compiles the + interface and capability-limited WASI host to small, readable, free classic + JavaScript. See TYPESCRIPT.txt for build/licensing. Text uses textContent. + Existing services, including SearXNG, are unchanged. + + Crypto download shows an ASCII bar, percentage, and received/total MiB; + unknown totals show received MiB. A 45-second stall aborts with reload + instructions; slow transfers continue while bytes arrive. Chat connection has + a separate status. No browser cache or storage was added. + + CREATE NEW CHAT generates a purpose-specific identity locally and downloads + its protected file. Reopen that exact file with its password before first + authentication/message. OPEN CHAT FILE restores access on later visits. + No username/password account; messages never require updating the stable file. + The owner has a separate identity/inbox. The canonical RSA OpenPGP key does + not encrypt chat. History defaults to zero messages. Authenticated counters show total, sent, and received messages. Choose 0 to N recent messages, where N is the current @@ -58,12 +52,10 @@ WHAT EXISTS LOCAL PREREQUISITES AND COMMANDS - Start in the root of the stack repository on main. Native - development needs Rust, libclang, OpenSSL 3.5 or newer development libraries, - a C compiler, pkg-config, make, Perl, curl, and tar. The checked configuration - uses OpenSSL 3.6.5. The WASI build script currently targets an x86_64 GNU/Linux - development host and downloads the hash-verified WASI SDK 34.0 and OpenSSL - source. General-purpose toolchains remain development tools, not VPS tools. + Start at the repository root on main. Native development needs Rust, libclang, + OpenSSL >=3.5 development libraries, C compiler, pkg-config, make, Perl, curl, + and tar. Checked OpenSSL: 3.6.5. WASI builds target x86_64 GNU/Linux and download + hash-verified WASI SDK 34.0/OpenSSL sources. Toolchains stay off the VPS. Build and initialize once: @@ -72,63 +64,54 @@ LOCAL PREREQUISITES AND COMMANDS sh infra/chat/build-source.sh cargo stack chat --init - Initialization appends CHAT_SETUP_TOKEN and CHAT_ORIGIN to the single root - .env, creating it privately if necessary. It refuses to overwrite an existing - setup token. The root .env.example lists both settings. Never commit .env. - Existing SearXNG settings stay in the same file. No crate has its own .env. - CHAT_BIND and public navigation now use the shared root configuration loader; - see CONFIGURATION.txt for the documented deployment keys. + Initialization privately creates/appends CHAT_SETUP_TOKEN and CHAT_ORIGIN + in the single root .env, preserving an existing token and SearXNG settings. + Root .env.example lists both; never commit .env or create crate-specific files. + CHAT_BIND and public navigation use the shared loader; see CONFIGURATION.txt. - For subsequent Rust-only edits, sh infra/chat/build-browser.sh reuses the - previously built, hash-verified OpenSSL/SDK inputs. Rebuild OpenSSL itself - after changing its source, version, configuration, or build flags. The WASI - module has a 256 MiB maximum linear-memory limit. + For Rust-only edits, sh infra/chat/build-browser.sh reuses verified OpenSSL/SDK + inputs. Source/version/configuration/flag changes require an OpenSSL rebuild. + WASI linear memory is capped at 256 MiB. Start: cargo stack chat - Open http://127.0.0.1:9080/ and choose OWNER SETUP, labeled for the owner - only. The direct http://127.0.0.1:9080/#setup link also works. - - Read CHAT_SETUP_TOKEN from your root .env locally and paste it into the owner - setup form. Choose an owner display name and a strong unique password. Save - the downloaded file, reopen it, and unlock it. That establishes the owner - public identity. The setup token stops authorizing setup once an owner exists. - It does not become an owner login or a key recovery mechanism. OWNER SETUP - stays visible; once an owner exists, it explains how to open the existing - owner file or reset after loss instead of silently replacing the key. Never publish - the owner private file or its password. Keep both in your private KeePassXC - database, along with a separately protected backup of that database. - - Open another browser window at http://127.0.0.1:9080/ to create a visitor chat. - Every page holds its own identity in memory; there is no shared browser login. - Stop the development server with Control-C. A server restart invalidates all - sessions but preserves identities, conversations, counts, and ciphertext. - - The direct development server refuses a non-loopback bind or an origin that - does not exactly match its development bind address. Packaged production uses - a private container listener behind Caddy HTTPS, with an explicit external - CHAT_HOST/HTTPS_PORT origin. Forwarded request headers do not choose that - origin. Do not expose the direct development server through a public proxy. + Open http://127.0.0.1:9080/ and choose owner-only OWNER SETUP, + or use http://127.0.0.1:9080/#setup. + + Read CHAT_SETUP_TOKEN from root .env locally; paste into setup. Choose a + display name and strong unique password; save, reopen, and unlock the file. + This registers the owner public identity and disables token-based setup. + The token grants neither login nor key recovery. OWNER SETUP remains visible + with existing-file/reset instructions; it never silently replaces a key. + Never publish private file/password. Keep both in KeePassXC with a separately + protected database backup. + + Create a visitor chat in another window at http://127.0.0.1:9080/. + Identity stays in each page's memory; browser login is not shared. Control-C + stops development. Restart invalidates sessions, preserving identities, + conversations, counts, and ciphertext. + + Direct development requires loopback and an exactly matching bind/origin. + Production uses a private listener behind Caddy HTTPS, with explicit + CHAT_HOST/HTTPS_PORT origin; forwarded headers cannot choose it. Never expose + direct development through a public proxy. OWNER FILE LOSS AND BACKEND RESET - A setup token grants registration permission. It cannot recover the private - key inside a lost owner file. If that file and its backups are gone, existing - messages cannot be decrypted with a newly generated identity. The reset below - intentionally starts a fresh chat service and clears all stored messages, - encrypted profiles, conversations, counters, and the owner registration. - It preserves the root .env and any previously exported backup archives. - It does not claim forensic erasure of backups, snapshots, or OS copies. - - Run these commands locally at the stack root for the current development - build. There is no reset operation in the HTTP API. Filesystem access to - the private chat directory is the administrative boundary. Production uses - ./bin/stack chat --owner-fingerprint and ./bin/stack chat --reset-owner - through an isolated maintenance container. Stop production with ./bin/stack - down first; restart with ./bin/stack up --production. No Rust is needed on - the VPS. See CHAT-PRODUCTION.txt before a destructive reset. + Setup tokens grant registration, not private-key recovery. A new identity + cannot decrypt old messages if the owner file/backups are lost. Reset clears + all messages, encrypted profiles, conversations, counters, and owner registration. + Root .env and exports survive. This does not forensically erase backups, + snapshots, or OS copies. + + Run development commands at the stack root. Reset has no HTTP API; private + chat-directory access is the administrative boundary. Production runs + ./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: @@ -139,26 +122,23 @@ OWNER FILE LOSS AND BACKEND RESET cargo stack chat --reset-owner PASTE_CURRENT_OWNER_FINGERPRINT_HERE - A fingerprint mismatch refuses without deleting data. The server and reset - process share a filesystem lock. Direct development additionally requires - the loopback service port to be free. This prevents resetting a running service, including a - development process launched before the lock was added. Online snapshots and - reading the public owner fingerprint remain available while chat is running. + Mismatched fingerprints refuse without deletion. Server/reset share a lock; + direct development also requires a free loopback service port, protecting + older running processes without the lock. Online snapshots and fingerprint + reads remain available during operation. Restart: cargo stack chat - Choose OWNER SETUP and use the existing CHAT_SETUP_TOKEN from the same root - .env to generate, save, and reopen a fresh owner file. Back that file and its - password up in KeePassXC. Visitors must create fresh chat files too: their - previous files pin the former owner key and are correctly rejected after - replacement. A reset cannot prove continuity with the lost key. + Use OWNER SETUP and existing root .env CHAT_SETUP_TOKEN; generate, save, and + reopen a fresh file. Back up file/password in KeePassXC. Visitors also need + fresh files: old owner-key pins correctly reject replacement. Reset cannot + prove continuity with the lost key. - If an old owner file is found later, an old encrypted backup may still allow - recovering its original conversations in an isolated restored installation. - The reset does not remove existing exported archives, and it never substitutes - a new key into old ciphertext or old visitor files. + An owner file found later may recover old conversations from an encrypted + backup in isolation. Reset retains exports and never substitutes new keys + into old ciphertext or visitor files. FILE AND MESSAGE CRYPTOGRAPHY @@ -168,12 +148,10 @@ FILE AND MESSAGE CRYPTOGRAPHY a fixed purpose-only user ID. Names are not placed in public certificate IDs. The server rejects uploaded secret-key material and unsupported certificates. - New JSON identity files are indented and use an array of 80-character - Base64 ciphertext chunks; the final chunk may be shorter. JSON indentation, - quotes, and commas are additional to the ciphertext width. The reader joins - the chunks before Base64 decoding. No private fields become plaintext. - This is the application's own JSON envelope, not OpenPGP ASCII armor. - The new file-format identifier is authenticated as associated data: + New identity JSON uses indentation and 80-character Base64 ciphertext chunks; + the last may be shorter. JSON punctuation/indentation adds width. Readers join + chunks before decoding. Private fields stay encrypted. This application envelope + is not OpenPGP ASCII armor. Its identifier is authenticated as associated data: stack-chat-file/v2;argon2id-m65536-t3-p1;aes256-gcm @@ -181,10 +159,9 @@ FILE AND MESSAGE CRYPTOGRAPHY stack-chat-file/v1;argon2id-m65536-t3-p1;aes256-gcm - v1 uses its original identifier as associated data. Changing a version label - or converting its field shape does not convert a file: the authentication tag - binds the version. No existing file needs rewriting; old readers do not support - new v2 exports. Cryptographic algorithms and KDF parameters are unchanged. + v1 retains its original associated data. The tag binds versions: relabeling + or reshaping fields cannot convert files. Existing files need no rewrite; + old readers reject v2. Algorithms/KDF parameters stay fixed. Argon2id version 0x13 uses 65536 KiB, three passes, one lane, and a 32-byte derived key. A fresh CSPRNG salt of 16 bytes and AES-256-GCM nonce of 12 bytes @@ -203,11 +180,10 @@ FILE AND MESSAGE CRYPTOGRAPHY formats return a generic error. Mutable KDF keys, plaintext envelopes, and private serialization buffers are zeroized where practical. - The browser checks the file's exact origin and pinned owner certificate on - unlock. Owner identity creation pins its own public certificate. Visitor - creation pins the public certificate currently advertised over the origin's - trusted connection. First-visit trust is consequently still trust in the - served application and TLS; it is not an out-of-band identity guarantee. + Unlock checks exact origin and owner pin. Owner creation pins its own public + certificate; visitors pin the certificate advertised over the trusted origin + connection. First visits still trust served application/TLS, without an + out-of-band identity guarantee. Each text message is signed, then encrypted for both participants. A signed plaintext JSON object binds v=1, kind=message, exact origin, conversation ID, @@ -246,12 +222,11 @@ AUTHENTICATION AND STORAGE keys or message plaintext. OS diagnostics and a future reverse proxy need their own logging review before deployment. - Sends are transactional. Retrying exactly the same ciphertext, message ID, - sender, and conversation returns its existing sequence without incrementing - counts. Reusing an ID with different data is rejected. This handles a response - lost after the message was stored. Keep the page unlocked to retry; reload - discards the pending ciphertext and draft, so inspect history before resending. - Send retries are deliberate; messages are not automatically resent. + Transactional sends deduplicate exact ciphertext/message-ID/sender/conversation + retries, returning the original sequence/counts. Reused IDs with changed data + are rejected. This handles lost responses after storage. Keep the page unlocked + to retry; reload loses pending ciphertext/draft, so inspect history before + resending. Retries are deliberate, never automatic. Limits: 8192 UTF-8 bytes per text, 16384 bytes per signed payload, 65536 base64 characters per stored ciphertext, 50 history rows per API request, 1000 visitor @@ -297,33 +272,28 @@ BROWSER STATE AND LIMITATIONS addresses during connections. Bearer-token theft permits API use until expiry or lock, though decrypting or signing still requires the private key. - A malicious server can replace JavaScript or WASM and steal passwords and - plaintext on the next visit. A pinned key and LibreJS do not prevent that. - Shared-host compromise, browser extensions, a compromised OS, browser bugs, - dependency attacks, malicious friends, and denial of service remain risks. - A purpose label advises separation; it cannot prevent exporting and reusing - a key elsewhere. This application is not a SimpleX equivalent. + Malicious servers can replace JavaScript/WASM and steal passwords/plaintext + on the next visit; key pins and LibreJS cannot prevent it. Shared-host/OS + compromise, extensions, browser bugs, dependency attacks, malicious friends, + and denial of service remain risks. Purpose labels cannot prevent key export + or reuse. This is not a SimpleX equivalent. BACKUP, EXPORT, AND RESTORE - The existing cargo stack backup creates a GPG-encrypted archive and now uses - SQLite's online backup API for chat.sqlite. It does not copy a live SQLite - file or journal with ordinary file-copy code. It can take a consistent chat - snapshot while the local chat server runs. Existing Podman data still follows - the stack's pause/copy/resume process; the chat container stays unpaused. - Separate snapshots cover both chat and chat-local. A snapshot is atomic - for chat data; unrelated services do not share a global transaction with it. - The backup includes the single root .env and must remain private/encrypted. + cargo stack backup uses SQLite online backups, not ordinary live database/ + journal copies. chat and chat-local get separate consistent snapshots while + running; chat stays unpaused. Other Podman services retain pause/copy/resume. + Chat snapshots are atomic, without a cross-service transaction. Exports + include root .env and must stay private/encrypted. For a chat-only consistent database export: cargo run --locked -p stack-chat-server -- --root . --snapshot var/exports/chat-snapshot.sqlite - Create var/exports privately first if it does not exist. The destination must - not already exist. This standalone snapshot is ciphertext plus public metadata, - not a GPG-encrypted full-stack archive; encrypt it before off-machine storage. - It does not contain browser private identities. A server-side backup cannot - recover a lost user or owner file/password. + Create var/exports privately first; destination must not exist. Standalone + snapshots contain ciphertext/public metadata, without full-stack GPG encryption. + Encrypt before off-machine storage. Browser private identities are absent; + server backups cannot recover lost identity files/passwords. Restore a full stack using the existing README.txt backup/restore instructions. For a chat-only restore, stop the chat server, put the verified snapshot at @@ -334,33 +304,25 @@ BACKUP, EXPORT, AND RESTORE origin deliberately makes old files fail their origin binding; there is no origin migration UI yet. Do not edit an encrypted file by hand. - Keep at least one encrypted backup away from the VPS. Test the owner identity, - visitor identity, correct counts, and decrypted messages after a restore. - Keep private identity files and passwords in KeePassXC with a protected - database backup. Never place them in source control or the public source - archive. Deleting a message locally or losing a file does not delete stored - ciphertext, old exports, or other participants' copies. + Keep an encrypted backup off-VPS. After recovery, test owner/visitor identities, + counts, and decryption. Keep private files/passwords in KeePassXC with a protected + database backup, never source control/public archives. Local deletion or file + loss does not delete stored ciphertext, exports, or participants' copies. INTERFACE AND SITE STYLE - The interface follows web/styles.css, using the red ASCII logo, - 80-column desktop width, - black/white light and dark themes, blue links, green prompts/actions, ASCII - form frames, and bracketed controls. Licensing links retain the main site's - amber and muted green accents. The LibreJS badge stays yellow with its - keyboard-accessible warning. Small screens keep readable form controls and - fit the ASCII rules to the available width instead of shrinking all text. - Scrolling belongs to the root viewport at the screen edge; the logo has no - inner scroll container. JavaScript licenses sit below the LibreJS badge. - No remote fonts, images, or additional scripts are required. + web/styles.css supplies red ASCII logo, 80-column desktop width, black/white + light/dark themes, blue links, green prompts/actions, ASCII frames, and bracketed + controls. License links retain amber/muted green. Yellow LibreJS badge has a + keyboard-accessible warning; JavaScript licenses sit below it. Small screens + retain readable controls and fit ASCII rules without shrinking all text. + Root viewport scrolls at the screen edge; no inner logo scrolling. + No remote fonts, images, or additional scripts. LICENSES, SOURCE, AND VERIFICATION - Portable root CLI builds now also need a musl C compiler because consistent - snapshots use bundled SQLite. The root CLI was built and executed as an - x86_64 static PIE with SQLite; no runtime SQLite package is needed on the VPS. - The local check used Alpine musl 1.2.6 headers with the host C compiler for - the C object build and Rust's self-contained musl runtime for final linking. + 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 @@ -399,16 +361,12 @@ LICENSES, SOURCE, AND VERIFICATION PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:chat PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:progress - Browser automation additionally needs Node and the root locked Playwright - development dependency, with browser downloads under .tools/browsers. See - TYPESCRIPT.txt for installation and the strict TypeScript build. These tools are - excluded from releases. The fixtures use temporary isolated installation roots - with one temporary .env each, and remove their private data on completion. - They listen on loopback port 9081 by default (override with CHAT_TEST_PORT), - leaving the normal local preview on 9080 available. The optional packaged - HTTPS variant is documented in CHAT-PRODUCTION.txt and retains its disposable - installation roots for inspection. Tests do not initialize the real owner - identity or touch the VPS. + Automation needs Node/root locked Playwright, with browsers in .tools/browsers; + see TYPESCRIPT.txt for installation/strict builds. Releases exclude these tools. + Fixtures isolate roots with one temporary .env each, deleting private data on + completion. Default loopback 9081 (CHAT_TEST_PORT overrides) leaves preview 9080 + free. Packaged HTTPS tests retain disposable roots; see CHAT-PRODUCTION.txt. + Tests neither initialize the real owner nor touch the VPS. LibreJS verification uses the Mozilla-distributed GNU LibreJS 7.21.1 in Playwright Firefox 157.0, without whitelisting the application. The full chat @@ -419,12 +377,10 @@ LICENSES, SOURCE, AND VERIFICATION https://addons.mozilla.org/firefox/downloads/file/4169076/librejs-7.21.1.xpi - Earlier tests exposed a LibreJS label mismatch: a combined GNU prefix and - or-later suffix was not recognized by that release. The table now uses its - recognized GNU-AGPL-3.0 identifier and the surrounding prose and legal headers - explicitly retain the version-3-or-later grant. Readable classic scripts and - workers are used instead of requiring module-script interception support. - This tests license recognition and operation, not the safety of delivered code. + LibreJS rejected a combined GNU prefix/or-later suffix. The table uses recognized + GNU-AGPL-3.0; prose/legal headers retain version-3-or-later. Readable classic + scripts/workers avoid module-script interception requirements. This tests + license recognition/operation, not delivered-code safety. The crypto browser probe fails closed without browser CSPRNG entropy and on unknown WASI imports. The application browser test uses real WASI/OpenSSL @@ -442,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 <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/>. + +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 8c9ee28..0000000 --- a/docs/CHAT-OPENSSL-MIGRATION.txt +++ /dev/null @@ -1,254 +0,0 @@ -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/>. - - -OPENSSL BACKEND MIGRATION RECORD - - Date: 2026-10-30. Historical backend migration record. - Scope at this checkpoint: a compatibility and dependency-security experiment. - The later messenger and packaged deployment are described separately in - CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt. Tests use disposable identities. - -DECISION AND FIXED ALGORITHMS - - Retain Sequoia OpenPGP 2.4.2 and RFC 9980 ML-KEM-1024 + X448 encryption. - Retain ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, and AES-256 content - encryption. Change the cryptographic backend, not the algorithms or wire - protocol. Probe assertions reject an unexpected backend or key algorithms. - - The previous Rust backend reached cx448 0.1.1 -> crypto-bigint 0.5.5 during - curve arithmetic. It also included rsa 0.9.10. See CHAT-SECURITY-REVIEW.txt - for the original reachability investigation. Neither RustSec issue was - ignored. No mathematical implementation was patched locally. - - The current manifest enables only Sequoia's crypto-openssl feature. The - allow-experimental-crypto and allow-variable-time-crypto opt-ins are gone. - The resolved graph has no cx448, crypto-bigint, or rsa crate. This removes - those Rust backend dependencies; it does not prove that all side channels - are absent from a browser or that external C dependencies have no 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 were checked against upstream GitHub release asset metadata. The - build script verifies each archive before extraction. Hash checking pins - these inputs; it is not a claim that upstream release signatures were - independently verified or that output is bit-for-bit reproducible across - hosts. Archives, SDK, libraries, and build products stay under ignored - .tools/ and target/. The native development library is supplied by the OS. - -WHY WASI - - The original Rust-backend proof used wasm32-unknown-unknown and generated - wasm-bindgen JavaScript. The OpenSSL proof uses a wasm32-wasip1 command - module and a small explicit browser WASI bridge. The Rust command calls - the same compatibility_probe function used by native tests. - - The browser harness loads the new WASI binary directly, not old generated - glue. It requires the output to identify OpenSSL 3.6.5, preventing a stale - Rust-backend artifact from being accepted as evidence of OpenSSL support. - Old browser-build commands are historical and must not be reused here. - -BUILD CONFIGURATION AND BOUNDARIES - - infra/chat/openssl-wasi.conf adds a build target inheriting BASE_unix with - little-endian and 32-bit bignum configuration. It changes configuration, - not OpenSSL's cryptographic source routines. C code is compiled at -O2. - No third-party rand.patch, unconditional entropy-success patch, or custom - random-number generator is applied. A SHA-256 comparison of all 5889 original - regular files against the downloaded OpenSSL archive found no changed or - missing originals after the build; only the separate target configuration - was added. OpenSSL 3.6.5 already 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. - - Build options 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. They avoid - unsupported host capabilities and use static libraries. Disabling the - secure-memory arena means ordinary WASM linear memory is used; it does not - establish secure erasure, locked pages, or protection from delivered JS. - Disabling C stdio does not disable the Rust command's captured output. - - _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply the SDK's process-ID - emulation. getuid/geteuid/getgid/getegid map to getpagesize, following the - common WASI port configuration: the browser module has no real OS user IDs - or privilege boundary. These values are not used as randomness or identity - credentials. This is an explicit portability assumption, not a general - POSIX security emulation layer. - - Rust bindings use the SDK's libclang and WASI sysroot with target - wasm32-wasip1 and -fvisibility=default. Without explicit visibility, the - initial bindgen attempt omitted function declarations. Rust's bundled WASI - libc search path precedes the SDK libraries: reversing that order initially - failed to link __wasi_init_tp. The SDK library path remains available for - the emulated-getpid archive. These failures were toolchain integration - issues; no generated cryptographic bindings were manually 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. - - Those capability denials protect against accidentally connecting the WASI - module to host storage or networking. They do not isolate it from malicious - JavaScript delivered by the same server. Memory, clocks, randomness, and - imported bridge functions remain part of the trusted browser environment. - A test deadline 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 also observes no external requests, cookies, localStorage, - sessionStorage, IndexedDB databases, or service-worker registrations. - Its HTTP responses carry Cache-Control: no-store. This establishes only - this probe's behavior, not that browsers/OSes cannot retain memory, caches, - crash dumps, swap, or restored state. A complete chat session does not exist. - - The native tests, WASI build, and browser test are separate checks. A native - pass cannot stand in for browser execution. A functional pass and a clean - vulnerability scan cannot stand in for a cryptographic or side-channel audit. - -DEPENDENCY POLICY REVIEW - - The RustSec database reports no advisories for the current resolved graph. - No advisory ignore was added. Bans on wildcards, unknown Git/registry sources, - and unreviewed duplicate versions remain enabled. The following existing - upstream API generations cannot be unified merely by cargo update. Duplicate - exceptions match only the exact older version; other versions remain subject - to the duplicate ban. All these packages still undergo advisory and license - checks. Cargo.lock pins both sides of each pair. - - 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, scoped to libloading, a bindgen/clang - dependency. GNU-compatible third-party licenses remain correctly attributed. - cargo-about generates notices for the complete workspace. The static OpenSSL - C library and SDK are not Rust crates; their original licenses and sources - must accompany any distributed browser artifact as appropriate. Source - OpenSSL LICENSE.txt is Apache-2.0. Do not treat the Rust notice page alone - as complete documentation of external C/toolchain distribution obligations. - -EXTERNAL LIBRARY MAINTENANCE - - cargo-deny does not audit the externally built OpenSSL C archive or SDK. - Review OpenSSL's release/advisory pages separately and rebuild when needed: - https://openssl-library.org/news/timeline/ - https://openssl-library.org/news/vulnerabilities-3.6/ - OpenSSL 3.6.5 is the tested security-patch release dated 2026-09-29. A green - Rust audit is not a lifetime security guarantee for this pinned C version. - Choose and 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 build script pins archive versions and hashes, configures static OpenSSL, - installs it under .tools/openssl-wasi, builds the Rust WASI command, and prints - its SHA-256. It does not download/use an unverified precompiled OpenSSL binary. - It may reuse already extracted sources; use a fresh ignored tools/build tree - when independently verifying the source archive rather than trusting a - previously modified extraction. Downloads and 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 removes the specific Rust-backend dependency blocker and demonstrates - the fixed algorithms in a browser. It is not a complete messaging service, - audit of OpenSSL/WASI/JIT timing behavior, LibreJS certification, or proof - against malicious delivery. The file format, authentication, server/database, - quotas, hostile-input handling, pagination/counts, backup recovery, complete - session lifecycle, and browser-state tests still need implementation. diff --git a/docs/CHAT-PRODUCTION.txt b/docs/CHAT-PRODUCTION.txt index 03f5ba4..6c901dc 100644 --- a/docs/CHAT-PRODUCTION.txt +++ b/docs/CHAT-PRODUCTION.txt @@ -16,70 +16,57 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. PRODUCTION CHAT INTEGRATION - Implemented on main. - Building and testing a release does not activate the VPS. This is experimental software without an - independent cryptographic or application security audit. Read the limitations - in CHAT-APPLICATION.txt before using it for private conversations. - - Four services run through rootless Podman: Caddy, cgit, SearXNG, and chat. - The release contains statically linked x86_64 GNU/Linux management and chat - binaries, the WASM browser module, exact corresponding sources, and saved - native container images. The chat image starts FROM scratch: no compiler, - shell, package manager, or first-party Python. Its root filesystem is read - only, capabilities are dropped, and only its private data directory is writable. - - Caddy terminates HTTPS at CHAT_HOST and redirects www.CHAT_HOST while keeping - the requested path. CHAT_UPSTREAM defaults to chat:8080 on the private - container network. Chat publishes no host ports. Production publishes only - the gateway HTTP and TCP/UDP HTTPS ports. Existing host SSH policy is separate. - - The gateway supplies an explicit external origin selected from root .env. - The server accepts that exact HTTPS origin or the dedicated local gateway - origin. Forwarded client headers never select the trusted origin. Origin - checks supplement cryptographic authentication; they are not authentication. + Builds/tests do not activate the VPS. Experimental; + no independent cryptographic or application security audit. Read + CHAT-APPLICATION.txt limitations before private use. + + Rootless Podman runs Caddy, cgit, SearXNG, and chat. Releases contain static + x86_64 GNU/Linux management/chat binaries, browser WASM, exact corresponding + sources, and native images. Chat starts FROM scratch: no compiler, shell, + package manager, or first-party Python. Root filesystem is read-only; + capabilities are dropped; only private data is writable. + + Caddy serves HTTPS at CHAT_HOST and redirects www.CHAT_HOST, preserving paths. + CHAT_UPSTREAM defaults to private chat:8080. Chat publishes no host ports; + only gateway HTTP and TCP/UDP HTTPS are published. Host SSH policy is separate. + + Root .env selects the explicit external origin. Chat accepts exactly that + HTTPS origin or the dedicated local gateway origin. Forwarded client headers + cannot select it. Origin checks supplement cryptographic authentication. BUILD ON THE WORKSTATION - Current releases require an x86_64 GNU/Linux build host and matching target. - Use the pinned Rust toolchain, Podman, Git, GnuPG, a GCC-compatible C compiler, - Linux UAPI headers, libclang, pkg-config, make, Perl, curl, tar, Node >=22.18.0, - and npm. Native - workspace checks need OpenSSL 3.5+ development libraries. Browser testing - additionally needs the pinned Playwright, Chromium, and Firefox. These tools remain - on the workstation; production does not need Cargo or a compiler. + Build host and target must be x86_64 GNU/Linux. Requires pinned Rust, Podman, + Git, GnuPG, GCC-compatible C compiler, Linux UAPI headers, libclang, pkg-config, + make, Perl, curl, tar, Node >=22.18.0, npm, and OpenSSL 3.5+ development libraries. + Browser tests also need pinned Playwright, Chromium, and Firefox. Workstation + tools stay local; production needs neither Cargo nor compiler. cargo stack release - This first builds/checks TypeScript, then runs formatting, strict Clippy, - all workspace tests, rustdoc, cargo-deny, - and cargo-about. It then builds static native binaries, the WASI module, - corresponding sources, the chat/cgit images, and the complete image export. - THIRD-PARTY-LICENSES.html is regenerated from the full workspace before - packaging. Check the archive checksum and test recovery before deployment. + Runs TypeScript build/checks, formatting, strict Clippy, workspace tests, + rustdoc, cargo-deny, and cargo-about. Builds static binaries, WASI, exact + sources, chat/cgit images, and complete image export. Regenerates workspace + THIRD-PARTY-LICENSES.html. Verify archive checksum and recovery before deployment. infra/chat/build-native.sh verifies OpenSSL 3.6.5's original source archive: a2157c2830efdec3788939b00c9b0638306d3f0bbb76dc4832ee503bb397df98 It verifies Alpine musl-dev 1.2.6-r3 headers: 310b51cff12e1d5ce5f135c558a82e2550427da5465bbd16c9ff9b05e63ee3d9 - Sources come from the official OpenSSL GitHub release and Alpine CDN. The - compiler uses isolated musl/GCC built-in headers plus host Linux UAPI headers; - glibc headers are excluded. OpenSSL is static, has no dynamic providers, and - seeds its RNG through getrandom. The native cryptographic compatibility probe - must pass after the build. WASI uses its separately verified SDK and OpenSSL - build recorded in CHAT-OPENSSL-MIGRATION.txt. No crypto algorithm was changed. - - Cached inputs live only under .tools/. Rebuild OpenSSL when its version, - source, or build flags change. An OpenSSL update requires reviewing upstream - security notices, updating every affected URL/hash, native and WASI rebuilds, - rerunning both browsers and cargo-deny, regenerating notices and exact source, - and repeating release recovery. Never approve a dependency advisory merely - to make the build pass. cargo-deny does not audit C/OpenSSL advisories. - - The source download includes first-party code, Cargo-vendored dependencies, - and unmodified OpenSSL source. Toolchain/sysroot development inputs are not - bundled as application sources. Saved images preserve the tested runtime. - An ARM host needs a separately supported native release; do not copy these - x86_64 binaries there and assume they will work. + Sources: official OpenSSL GitHub release and Alpine CDN. Compilation uses + 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-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, + native/WASI rebuilds, both browsers, cargo-deny, regenerated notices/sources, + and release recovery. Never waive advisories to pass. cargo-deny excludes C/OpenSSL. + + Source downloads include first-party code, Cargo-vendored dependencies, and + unmodified OpenSSL, excluding development toolchain/sysroot inputs. Saved + images preserve tested runtime. ARM requires a separately supported native release. LOCAL MODES AND ORIGIN BINDING @@ -94,99 +81,83 @@ 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 - These are distinct browser origins and require separate identity files. The - direct process and production deliberately use the same relative database - name on their respective machines. Do not switch an installation containing - valuable identities between direct development and production. Use a fresh - restored release directory for integration tests. Rebuild the release after - edits to refresh the packaged browser, server, sources, notices, and image. + Distinct origins require separate identity files. Direct development and + production share a relative database path on separate machines; do not switch + valuable installations between them. Integration tests need a fresh restored + release. Rebuild after edits to refresh browser, server, sources, notices, and image. - The single root .env holds public hostnames, ports, upstreams, backup settings, - SEARXNG_SECRET, and CHAT_SETUP_TOKEN. The example is public; actual .env is - ignored and never included in public releases or source downloads. Caddy keys - and chat databases are generated private runtime state under var/data. - Initialization preserves configured values and generates missing secrets. + One root .env holds hostnames, ports, upstreams, backups, SEARXNG_SECRET, and + CHAT_SETUP_TOKEN. Only the example is public; releases/source downloads exclude + actual .env. Caddy keys/chat databases stay under var/data. Initialization + preserves configured values and generates missing secrets. DEPLOY AND CREATE THE REAL OWNER FILE - Use the release upload/activation instructions in OPERATIONS.txt. Extract - the new prebuilt management binary into var/incoming/bootstrap and invoke - it with --root pointing at the current installation. Use the management - binary from that verified release so activation understands its service - schema. It verifies the archive and installs packaged chat artifacts before - starting services. + 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. Public certificate validation requires standard reachable ACME ports. - The systemd user service runs ./bin/stack up --production at boot and - ./bin/stack down on stop. Podman restart policies restart failed containers. - Neither mechanism compiles code or requests a registry pull for a complete - release on the matching architecture. Check all four services after activation: + The supplied systemd user service uses ./bin/run for up --production and down; + the launcher selects an included Guix runtime when present. Podman restarts + 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. The displayed limitations are CHAT-APPLICATION.txt. Chat has its own origin; the main website remains JavaScript-free. Its Message Me links lead to chat. - In the browser on the production HTTPS origin, choose OWNER SETUP. Read the - setup token from the production root .env locally; never publish it or send it - through chat. Choose a unique owner-file password, save the downloaded file, - reopen it, and unlock it. Store that file and password in KeePassXC and keep - a protected backup of the database. The setup token cannot replace this file. - Never copy a development owner file and silently change its pinned origin. + At the production HTTPS origin, choose OWNER SETUP. Read the root .env token + locally; never publish or send it through chat. Choose a unique password; + save, reopen, and unlock the downloaded owner file. Keep file/password in + KeePassXC with a protected database backup. The token cannot replace the file. + Never silently change a development file's pinned origin. BACKUP, RESTORE, AND KEY LOSS A complete encrypted application export: - ./bin/stack backup --include-images - - Download the .gpg and .sha256 files off the VPS. A backup on the same VPS is - vulnerable to loss of that VPS. Existing configured backup recipients apply. - This export includes the single .env, consistent SQLite snapshots for chat - and chat-local, Caddy certificate state, repositories, static binaries, WASM, - sources, and exact container images. SQLite uses its online backup API; chat - is left unpaused so a pending writer cannot block snapshot completion. - The database contains ciphertext and public keys, plus the metadata described - in CHAT-APPLICATION.txt. The owner private file and GnuPG secret key are not - server data; back them up separately. + ./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 + for chat/chat-local, Caddy certificates, repositories, static binaries, WASM, + sources, and exact images. Chat stays unpaused so pending writers can complete. + Database contents are ciphertext/public keys and CHAT-APPLICATION.txt metadata. + 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 - - Restore defaults to local mode and changes container project names to avoid - accidentally replacing another installation. up --production selects the - production database and HTTPS origin again. Keep the same hostname/origin, - point DNS at the replacement host, and use unchanged owner/visitor files and - passwords. A new IP does not change an origin. A changed hostname or HTTPS - port does. Do not expose two restored copies simultaneously under one origin. - Server restart invalidates sessions; reopening identity files authenticates - again. Historical ciphertext and counters persist across restart and recovery. - - Losing the owner private file cannot be repaired by CHAT_SETUP_TOKEN. If its - backups are also lost, reset deliberately deletes every stored conversation - and message. It does not decrypt or recover anything. This backend operation - 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 - - A running server holds a filesystem lock; reset refuses until it stops. The - full fingerprint must match. Root .env and previous exports are preserved. - Create a new production owner file afterward. Old visitor files reject its - changed pinned owner key and need a new chat identity. A reset does not erase - copies already present in exports or external snapshots. + ./bin/run up --production + + Restore defaults to local mode with fresh project names, avoiding replacement + of another installation. up --production reselects production database/origin. + Keep hostname/origin, repoint DNS, and use unchanged identity files/passwords. + IP changes preserve origin; hostname/HTTPS-port changes do not. Never expose + two restored copies under one origin. Restart invalidates sessions; reopening + files authenticates again. Historical ciphertext/counters persist. + + CHAT_SETUP_TOKEN cannot recover a lost private file. If backups are also lost, + reset deletes every conversation/message; it decrypts and recovers nothing. + Reset has no HTTP endpoint and requires filesystem/Podman access: + + ./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 + its changed key and need fresh identities. Exports/external snapshots are not erased. VERIFICATION @@ -202,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. @@ -216,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/CHAT-SECURITY-REVIEW.txt b/docs/CHAT-SECURITY-REVIEW.txt deleted file mode 100644 index 1bb1108..0000000 --- a/docs/CHAT-SECURITY-REVIEW.txt +++ /dev/null @@ -1,123 +0,0 @@ -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/>. - - -CHAT CRYPTOGRAPHY SECURITY REVIEW - - Date: 2026-10-30. Historical review of the initial Rust backend. - The current OpenSSL migration is recorded in CHAT-OPENSSL-MIGRATION.txt. - At that checkpoint, private-message use and deployment were not ready. - This historical review is not the current implementation status; see - CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt for the implemented application. - -FIXED REQUIREMENT - - Encryption must remain RFC 9980 ML-KEM-1024 + X448 using v6 keys. - The compatibility probe pairs it with ML-DSA-87 + Ed448 signatures. - No fallback encryption profile is implemented or approved. - The findings below apply to checkpoint 436e217, before the backend change. - -TEST RESULTS - - Sequoia OpenPGP 2.4.2 with its Rust backend generated password-protected v6 - secret keys, serialized and reimported them, signed UTF-8 content, encrypted - to both participants, and decrypted successfully for both participants. - Wrong passwords, unexpected signers, and modified ciphertext were rejected. - The same probe passed in actual Chromium using Rust compiled to WASM. - - Functional tests are not a cryptographic audit, constant-time analysis, - conformance certification, or demonstration that side channels are absent. - The test uses no user-supplied key files and does not provide chat service. - -RUSTSEC-2026-0387: REACHABLE DEPENDENCY - - Sequoia 2.4.2's crypto-rust backend selects cx448 0.1.1 for both X448 - and Ed448. cx448 depends on crypto-bigint 0.5.5. - - Source paths within these exact published crates: - - sequoia-openpgp/src/crypto/backend/rust/asymmetric.rs - x448_generate_key, x448_derive_public, x448_shared_point - ed448_generate_key, ed448_sign - cx448/src/x448.rs - PublicKey::from(&Secret), Secret::as_diffie_hellman - cx448/src/curve/montgomery.rs - Montgomery scalar multiplication and projective-to-affine conversion - cx448/src/field.rs - elliptic_curve::bigint and constant-modulus Montgomery representation - cx448/src/field/element.rs - field subtraction, multiplication, inversion - cx448/src/field/scalar.rs - scalar arithmetic using crypto-bigint integer operations - - The flagged crate is part of the required curve arithmetic. It cannot be - dismissed as an unused RSA dependency. The advisory covers timing variation - in arithmetic intended to be constant-time and identifies >=0.7.6 as fixed. - cx448 0.1.1 requires the incompatible 0.5 series. Updating Cargo.lock alone - cannot satisfy that requirement with 0.7.6. Source changes or another backend - would require a separate review and tests; no local cryptographic patch has - been invented to bypass this gate. - - This establishes a reachable affected dependency and prevents a claim of - unreachability. It does not prove practical key extraction from this WASM - binary on a particular browser/CPU. No such exploit has been demonstrated. - The hybrid construction's independent component may preserve protection - under some component failures; that is not grounds for ignoring the audit. - - Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0387.html - -RUSTSEC-2023-0071: RSA - - Sequoia's crypto-rust feature also includes rsa 0.9.10. The probe generates - only the specified PQ identities and calls their corresponding primitives; - it does not generate an RSA key or exercise RSA private-key operations. - - That observation applies only to this closed, generated-input probe. It is - not proof that a future importer, certificate verifier, authentication - endpoint, or decryption interface cannot be induced to use another algorithm. - The future application needs an explicit supported-algorithm policy and - hostile-input tests before any narrower exception could be assessed. - - Advisory: https://rustsec.org/advisories/RUSTSEC-2023-0071.html - -BACKEND CHECK - - In the installed Sequoia 2.4.2 source, the Nettle and Botan asymmetric - backends report these required composite algorithms unsupported. The OpenSSL - backend conditionally supports them, depending on the available OpenSSL - version/features. Its browser/WASM build has not been demonstrated here. - Native OpenSSL support alone would not satisfy client-side browser encryption. - - The Rust backend requires allow-experimental-crypto and - allow-variable-time-crypto. These are explicit prototype build opt-ins, - not security approvals or claims that the selected operations are safe. - -OTHER AUDIT GATES - - The new dependency graph also contains multiple crate versions. Its exact - graph is pinned in Cargo.lock. Duplicate bans have not been relaxed, and - no vulnerability advisory has been ignored. License permissions for Sequoia - and its dependencies must remain correctly attributed in generated notices. - - The project check must audit the whole workspace. A root-package-only - cargo-deny invocation misses a workspace member that is not a dependency - of the root CLI. The audit invocation was updated to use --workspace. - -GATE AT THE TIME OF THIS REVIEW - - Retain the fixed algorithms. Resolve the curve-backend dependency through - a reviewed upstream-compatible fix or demonstrate an acceptable alternative - backend in the browser. Repeat the native and browser tests, review imported - hostile inputs, and pass the full dependency audit before private-message use. diff --git a/docs/CONFIGURATION.txt b/docs/CONFIGURATION.txt index fc18c41..edd3d30 100644 --- a/docs/CONFIGURATION.txt +++ b/docs/CONFIGURATION.txt @@ -15,137 +15,129 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. CENTRAL CONFIGURATION - Each installation has one root .env for deployment settings and secrets. - The published .env.example documents every supported setting with public - defaults, private placeholders, and comments. Hostnames are public information; - this arrangement centralizes configuration, not secrecy or access control. - Public values remain visible in generated pages, DNS, certificates, and URLs. - - Existing installations keep their configured secrets and settings. Missing - public defaults are appended to the same .env by cargo stack config, service - initialization, or chat initialization. No second environment file is created. - The example is also the shared Rust configuration library's default source, - so missing public settings remain compatible with older exports. + One root .env holds each installation's deployment settings and secrets. + Public .env.example documents every supported key, public default, private + placeholder, and comment. Hostnames remain public in generated pages, DNS, + certificates, and URLs. Centralization is not secrecy or access control. + + cargo stack config, service initialization, and chat initialization preserve + existing settings/secrets and append missing public defaults to the same .env. + No second environment file. The shared Rust configuration library also reads + defaults from .env.example, keeping older exports compatible. PUBLIC SETTINGS BASE_DOMAIN - Root web/WKD domain. www aliases are derived from each configured host. + Root web/WKD domain. Derive www aliases from each configured host. SITE_HOST, GIT_HOST, SEARCH_HOST, WKD_HOST, CHAT_HOST - Fully qualified DNS names without schemes, paths, or ports. These - configure production HTTPS routes and their canonical www redirects. - CHAT_HOST also selects the exact trusted external chat origin. + Fully qualified DNS names; no scheme, path, or port. Set production + HTTPS routes and canonical www redirects. CHAT_HOST also selects the + exact trusted external chat origin. CHAT_LOCAL_HOST - Dedicated development gateway hostname ending in .localhost. Default: + Development gateway hostname ending in .localhost. Default: chat.localhost. Browser identity files bind to this origin and local port. WEB_BIND - Production host publish address. Default :: serves IPv4 and IPv6. Use - 127.0.0.1 for isolated staging; development mode always uses loopback. + Production publish address. Default :: serves IPv4/IPv6. Use 127.0.0.1 + for isolated staging. Development always binds loopback. HTTP_PORT, HTTPS_PORT - Production Caddy listener and Podman published ports. HTTPS also publishes - UDP for HTTP/3. Defaults are 80 and 443. Generated HTTPS links include a - nonstandard HTTPS port when configured. Ordinary public certificate - validation still needs the CA's standard external validation ports or an - appropriate alternative certificate/routing arrangement. Setting ports - alone does not configure DNS, firewall rules, or external port forwarding. + Caddy listeners and Podman published ports; defaults 80/443. HTTPS also + publishes UDP for HTTP/3. Generated links include nonstandard HTTPS ports. + Public certificate validation still needs standard external CA validation + ports or alternative certificate/routing arrangements. Port settings do + not configure DNS, firewall rules, or external forwarding. LOCAL_HTTP_PORT - Local Caddy HTTP port on 127.0.0.1. HTTPS host port is this value plus one. - Local Caddy still listens on container ports 80/443. --port overrides the - local port for that invocation. Existing installation/project names and - image state remain in var/state.json; an explicit .env local port takes - precedence over the previously saved port. + Local HTTP on 127.0.0.1; host HTTPS port is this value plus one. Caddy + still listens on container ports 80/443. --port overrides one invocation. + var/state.json retains installation/project names and image state. + An explicit .env local port overrides the saved port. CGIT_UPSTREAM, SEARXNG_UPSTREAM, CHAT_UPSTREAM - Proxy connection targets such as cgit:8080, searxng:8080, and chat:8080. These are - addresses, not upstream bind settings. The bundled images' internal 8080 - listeners and health checks remain image contracts. Changing a target - to another host/port requires an actual service at that address. + Proxy targets, e.g. cgit:8080, searxng:8080, chat:8080. Not bind settings. + Bundled internal 8080 listeners and health checks remain image contracts. + A changed target needs an actual service at that host/port. CHAT_BIND, CHAT_ORIGIN Direct development socket and exact browser origin. Defaults: - 127.0.0.1:9080 and http://127.0.0.1:9080. Direct development requires - loopback and an exact HTTP origin match. --bind overrides CHAT_BIND. - The container binds privately to 0.0.0.0:8080; its trusted origin comes - from CHAT_HOST/HTTPS_PORT or CHAT_LOCAL_HOST/LOCAL_HTTP_PORT instead. - Identity files pin their origin; editing these values does not migrate - encrypted identities or grant access on another origin. + 127.0.0.1:9080 and http://127.0.0.1:9080. Require loopback and an exact + HTTP origin match. --bind overrides CHAT_BIND. Containers bind privately + to 0.0.0.0:8080; trusted origin comes from CHAT_HOST/HTTPS_PORT or + CHAT_LOCAL_HOST/LOCAL_HTTP_PORT. Identity files pin their origin. + Editing these settings neither migrates encrypted identities nor grants + access on another origin. BACKUP_RECIPIENT, BACKUP_PUBLIC_KEY - Public OpenPGP fingerprint and project-relative public key path used for - encrypted exports. --recipient and --key override them. The secret GnuPG - key and revocation certificate remain outside the project. + Public OpenPGP fingerprint and project-relative public key path for + encrypted exports. --recipient/--key override them. Keep the secret + GnuPG key and revocation certificate outside the project. PRIVATE SETTINGS SEARXNG_SECRET - Generated on first stack initialization. Existing values are preserved. + Generated at first stack initialization; existing values preserved. CHAT_SETUP_TOKEN - Generated by chat initialization or first stack startup. Used only to authorize first owner - registration, including a fresh registration after an explicit backend - reset. It cannot recover a lost owner private key. + Generated by chat initialization or first startup. Authorizes only + first owner registration, including after an explicit backend reset. + Cannot recover a lost owner private key. RENDER AND APPLY - Edit .env locally. Never commit or publish it. Inspect the commented example - for exact keys. Hostnames, ports, socket addresses, fingerprints, and paths - are validated before use. Diagnostic formatting never dumps the config map. - Invalid environment-file syntax is reported without echoing its raw contents. + Edit .env locally; never commit or publish it. See .env.example for exact + keys. Validate hostnames, ports, sockets, fingerprints, and paths before use. + Diagnostics never dump the config map or echo invalid .env contents. Render public deployment files without starting services: cargo stack config cargo stack config --production - Outputs are var/local/ or var/production/: + Output under var/local/ or var/production/: Caddyfile cgitrc web/ - infra/Caddyfile, infra/Caddyfile.local, infra/containers/cgit/cgitrc, and the - first-party website HTML are templates using {{PUBLIC_KEY}} placeholders. - Do not run Caddy directly on an unrendered template. Podman mounts the generated - files. Public host/origin changes reach Caddy routes, www redirects, website - links, the cgit clone prefix, and the chat page's navigation. Local rendering - uses /git and /search paths and http://CHAT_LOCAL_HOST:LOCAL_HTTP_PORT for chat. + Templates use {{PUBLIC_KEY}} placeholders: infra/Caddyfile, + infra/Caddyfile.local, infra/containers/cgit/cgitrc, first-party website HTML. + Never run Caddy on unrendered templates. Podman mounts generated files. + Public host/origin changes reach routes, www redirects, website links, cgit + clone prefixes, and chat navigation. Local rendering uses /git, /search, + and http://CHAT_LOCAL_HOST:LOCAL_HTTP_PORT for chat. - To apply changes to the existing services: + Apply to existing local services: cargo stack up --local - Production uses its prebuilt management command and images: + Production uses prebuilt management command/images: - ./bin/stack up --production + ./bin/run up --production - Rendered website files can be read by an already running file server, while - listeners, container environment, and loaded Caddy configuration require the - service update above. Restart the local chat server with cargo stack chat to - load its new bind, origin, or public navigation settings. Reopen your identity - file afterward, as with any chat restart. + Running file servers can read rendered website updates. Listener, container + environment, and loaded Caddy changes need the service update above. + Restart direct local chat with cargo stack chat for new bind/origin/navigation + settings. Reopen your identity file afterward, as after any chat restart. PUBLICATION AND LIMITS - The shared stack-config crate uses an explicit public allowlist for template - substitution. A {{CHAT_SETUP_TOKEN}} or other private/unknown placeholder - causes an error rather than publishing its value. Generated public pages and - proxy configuration never receive a full environment dump. Chat receives the public configuration allowlist plus CHAT_SETUP_TOKEN; - other containers receive only their declared service secrets. Caddy uses - rendered public configuration and does not need the .env or private tokens. - - The root .env, runtime files, local render outputs, exports, and tool caches - remain ignored by Git. Source templates, the commented example, validated - defaults, and this configuration implementation are public. Full encrypted - backups include the single .env; source-only releases do not include it. - - Personal content and attribution remain source content: copyright headers, - author names, canonical contact information, public key material/WKD hashes, - project repository metadata, and GNU license links. Changing a deployment - hostname does not create a new email identity or regenerate WKD keys. - Image digests, source hashes, and fixed crypto/protocol/resource bounds remain - reviewed reproducible code/configuration rather than private deployment knobs. + stack-config substitutes only explicitly allowlisted public values. + {{CHAT_SETUP_TOKEN}} and other private/unknown placeholders fail instead of + exposing values. Public pages/proxy config never receive a full environment + dump. Chat receives the public allowlist plus CHAT_SETUP_TOKEN; other + containers receive only declared secrets. Caddy needs rendered public config, + not .env or private tokens. + + Git ignores .env, runtime files, local renders, exports, and tool caches. + Source templates, commented example, validated defaults, and configuration + implementation remain public. Full encrypted backups include the single + .env; source-only releases exclude it. + + Personal content stays in source: copyright, author names, canonical contact, + public keys/WKD hashes, repository metadata, GNU license links. Changing a + hostname creates no new email identity and regenerates no WKD keys. + Image digests, source hashes, fixed crypto/protocol/resource bounds remain + reviewed reproducible code/config, not private deployment knobs. diff --git a/docs/GIT-HISTORY.txt b/docs/GIT-HISTORY.txt deleted file mode 100644 index 410b53b..0000000 --- a/docs/GIT-HISTORY.txt +++ /dev/null @@ -1,84 +0,0 @@ -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/>. - -GIT IDENTITY AND SIGNATURE REWRITE - - Requested to replace the historical ops <ops@localhost> identity with - Doan Luu Duc Tai <doan@luuduct.ai> and sign the history with the canonical - RSA-2048 key 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6. - - Ten historical author identities and their corresponding committer identities - were corrected. All 27 commits reachable from the two published branches - at the time of the rewrite were recreated and signed because rewritten - parents change descendant commit IDs. Every new signature was verified. - - Every corresponding tree, message, original author timestamp/time zone, and - original committer timestamp/time zone was preserved. Merge topology was - preserved. Signature creation times reflect the new signing operation. - At the time, main's file tree was unchanged and chat development was separate. - Chat has since been merged into main; the temporary feature branch was deleted. - - Remote updates use an atomic push and exact force-with-lease expectations - for both branches, preventing overwrite of an unexpected remote update. - Existing objects and reflogs may still contain old IDs: this is an authorship - correction, not a claim of private-data removal or historical erasure. - -EXISTING CLONES - - The active development checkout is already aligned. Other clones must fetch - the new history instead of using an ordinary pull across divergent histories. - Preserve local-only commits and working changes first. A fresh development - clone is also possible; keep private .env and runtime data out of Git. - - For an existing clone with no local-only commits: - - git fetch origin - git switch main - git reset --keep origin/main - - --keep refuses updates that would overwrite conflicting working changes. - Do not blindly reset a clone with independent local work; use the mapping - below to identify the corresponding rewritten base before rebasing that work. - Git changes do not remove or replace ignored .env or var/ data. - -ORIGINAL TO REWRITTEN COMMITS - - 47fcc566884d7078c153cdce9fb62399fcca5d9e c4b2f892a1c6508360a683c90e8c4d596c521336 - 37d5e4a152df67d247349d2ed289bf65859ead53 020d3454cad6619bf25c6ecb0a293502f40364bd - 8a327a41cdce40933b977f7c6662929488bcf83e 64a1b484abb96c91da9aa23e0e45576749f6cdfe - 3a8956f9cb85184ae2b8202d7a733124a0d3f95f 9599590f67c24f70c400b967d162095c63f6af61 - 2f5c323be4a44b80cf9e39d51210776146a77cbd 1632c88ba08c62966c1f0b1578741d8af007e6fd - 118ba081389cfd80b38c9c691fd6d75f6497dcb5 274e70679b83bcac3dee3599bd9389a3332462ec - 38aa5d1f2a8dfc8ea97c0fdce8feae7e37795533 5317fa43b79e4251fcbcff2b4f1243a961ac00fe - 902cfd395fb03d6147813560ac5fcd7146702a1d 463c6f48a06730c29f89b2f376e68dc805df5cc9 - e373f154dbeacc5a0e83b4ec73c3aa316239c029 5386a63bfb2f14cab99874e7c414e1a418b01f67 - 006a1e94075e90fccfe248cd66bf7d0586a86279 84960925108eb2b5950d4c299b15b18893e0c0d0 - 19a0280d7dabd4ca637d8070cc7d3691a0e85bd1 909dce458d4bbef6710c2b4a67cd906902bb7f3f - c06e29dae4bb290fd2849f69da1bfa940c5a8222 5bed750baa5d782427e98550d34124f569e2fff8 - 714866be200b882be437375006180456284bab1c 9c7dd654512b8f51af15d50dd75a515566723be7 - 9f070f7ad0952c61b4ee66d2a01ff1bdf7ade0a8 cad7ab1e1ab1dde8ff4ba349cd5b1a05bbd32b05 - faaac8558431c80a6b8fbcf2c8f17f16d8602ab6 1386bd16b0fd796314d9ad70aede0946bd5c46bd - 92bf96961c09d7df6b1bfea72eb8a107bcf9e221 d546193ee543a5b1a0a097d0a7deca76e81b7de2 - 0a0cdc0c44dd8b4fb44d8a859962c40c593e98eb f196233257d247c94550cf822b91bc3c9d8a7f8f - 1206813d47e8602f08cbeaf27e73453e089e7c52 834a21a7cadce64889856a2a7521440a1519e244 - d611c869eb12e7fff5754be31775f82200ec958b b46ece18a424eaea3f396ddae7e45e91d1c0e742 - 436e217c7858888702d2b6d9e655597ccadb7281 f196c27faf265cd14d8d7accf58e87b4df3c8a91 - 7a0ce2805258e8ac061202904379692d53d5dc95 cf2f0e8c6752c401f152a8e534ba166839d4307e - 774074bb2bbd3bc07f7094cdce0935eca7520725 a0057b13fa4b6bed8aee3132001e79a245efabc8 - 143880db086c617b843340ca7023a879507761f8 0a4da3b86eb69f0f09b802a55e36fa9cf1bbf38c - 556eaa133e9cfe7f4c8e542708119fdfd311357e e6b350a1d48ca0490b7a3447bb8d0a4235514b76 - b7374fe09306c495131cf3673addb76a1dcf017d 3034705b6679bff1802c4180b7b4711b94fed7f9 - be16d00a8a4750028073e018ab2c58d54af192c4 c3b361c45ac8dd898d5c83a161abed5fc138b6c0 - 28a772cab50b1064e317aedbe4689aea286b1ec4 6933ec28be3dac0dc17cd634c663db3b4818caad 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. diff --git a/docs/LINTING.txt b/docs/LINTING.txt index 33084e9..bd1cdac 100644 --- a/docs/LINTING.txt +++ b/docs/LINTING.txt @@ -15,153 +15,129 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. STRICT LINT POLICY - Cargo.toml is the workspace policy; all four packages inherit it. - - Forbid rejects source-level attempts to lower a lint. Deny rejects a - violation but permits a narrower source-level override. Neither prevents - someone from changing the build configuration itself. Compiler/tool versions - are pinned, and the checked build is the supported development path. + Cargo.toml defines workspace policy; all four packages inherit it. + forbid rejects source-level lowering; deny permits narrower source overrides. + Neither prevents build-config edits. Pin compiler/tools; use checked builds. RUST AND CLIPPY - Every previously denied individual Clippy rule and every enabled Clippy - group now uses forbid. There are no blanket Clippy allow entries. This - includes all, correctness, suspicious, cargo, style, complexity, perf, - pedantic, nursery, multiple_crate_versions, and redundant_pub_crate. - All Rustdoc lints in its supported all group are forbidden too. - - Rust safety, documentation, visibility, casts, edition/future compatibility, - underscore bindings, and warnings use forbid. Unused-group members that do - not conflict with generated code are independently forbidden. Additional - restrictions cover panic/unimplemented/unreachable paths, panic inside Result - functions, unsafe allowance attributes, numeric/pointer casts, native-endian - data, string slicing, infinite loops, and test placement. Cargo.toml lists - the precise rules. Contradictory restriction groups are not enabled wholesale. - - The supported workarounds were implemented instead of lowering these rules: - - Clap derives became explicit checked Clap builders. All argument parsing - stays in Clap; application code does not implement a shell parser. - Both command schemas have regression tests for modes, required paths, - listener parsing, global --root, and maintenance conflicts. Help flags - and rejected-option exit codes were compared against the previous binaries. - The unused Clap derive feature and its lockfile dependency were removed. - - Tokio's runtime attribute became an explicit checked runtime builder. - Runtime construction errors propagate through the existing error path. - The broad generated expect/style/complexity allowances are no longer needed. - - Management and server implementations live inside private app namespaces. - Internal interfaces use pub(super), narrower than crate visibility. Only - the documented program entry point is re-exported at the binary root. - This satisfies both redundant_pub_crate and unreachable_pub without - exposing helper modules or allowing either lint. - - Serde containers specify crate = "serde". This supported attribute avoids - the generated extern-crate branch and its unused_extern_crates and - clippy::useless_attribute allowances. File and wire representations retain - their existing field names, ordering, tagging, and unknown-field checks. - - Result-returning tests report failed checks with anyhow::ensure instead - of panicking. This permits panic_in_result_fn without suppressions. + Every formerly denied individual Clippy rule and enabled group uses forbid. + No blanket allows. Groups include all, correctness, suspicious, cargo, style, + complexity, perf, pedantic, nursery; multiple_crate_versions and + redundant_pub_crate also remain forbidden. Forbid Rustdoc's supported all group. + + Forbid Rust safety/docs/visibility/cast/edition/future-compatibility checks, + underscore bindings, and warnings. Independently forbid unused-group members + compatible with generated code. Also restrict panic/unimplemented/unreachable + paths, panic in Result, unsafe allowance attributes, numeric/pointer casts, + native-endian data, string slices, infinite loops, and test placement. + Exact rules: Cargo.toml. Do not enable contradictory restriction groups wholesale. + + Workarounds preserve strictness: + + Explicit checked Clap builders replace derives. Clap still parses all + arguments; no application shell parser. Both command schemas test modes, + required paths, listeners, global --root, and maintenance conflicts. + Help/rejected-option exit codes were compared with previous binaries. + Removed unused derive feature and its lockfile dependency. + + Explicit checked Tokio runtime builder replaces runtime attribute. + Construction failures use existing error path; no broad generated + expect/style/complexity allowances remain. + + Management/server implementations use private app namespaces and + pub(super), narrower than crate visibility. Re-export only documented + entry point at binary root. Enforce redundant_pub_crate/unreachable_pub + without exposing helpers or allowing either lint. + + Serde containers set crate = "serde", avoiding generated extern-crate + branch and unused_extern_crates/clippy::useless_attribute allowances. + Preserve file/wire field names, ordering, tagging, unknown-field checks. + + Result-returning tests use anyhow::ensure instead of panicking, permitting + panic_in_result_fn without suppressions. THREE REMAINING RUST DENY SETTINGS - unused, nonstandard_style, and unused_qualifications remain deny. This is - a generated-code compatibility requirement, not permission to add source - allowances. clippy::allow_attributes and allow_attributes_without_reason - are forbidden for first-party source. - - The pinned Serde derive implementation unconditionally emits allowances for - non_upper_case_globals, unused_attributes, unused_qualifications, and - clippy::absolute_paths in serde_derive/src/dummy.rs. Its generated field - identifier visitor also allows non_camel_case_types. These are generated - attributes even when the first-party type has correct names and no redundant - qualification. They cannot be removed by renaming fields or imports. - - The supported crate-path workaround was applied and all macros were inspected. - Repeating Clippy with -F unused, -F nonstandard_style, or - -F unused_qualifications still rejects the pinned Serde expansion. There is - no supported derive setting to suppress that unconditional wrapper. Further - changes would require forking the generator or replacing the serialization - implementation, including security-sensitive identity/envelope/request formats. - Those are explicit upstream/format migrations, not configuration fixes. - - Every other unused-group member and non_snake_case is independently forbidden. - warnings itself is forbidden; the explicit deny groups provide the narrowly - required effective levels for the generated attributes. No first-party - allow/expect attributes were added. + unused, nonstandard_style, unused_qualifications stay deny for generated-code + compatibility, not permission for source allowances. First-party + clippy::allow_attributes and allow_attributes_without_reason are forbidden. + + Pinned serde_derive/src/dummy.rs unconditionally allows non_upper_case_globals, + unused_attributes, unused_qualifications, clippy::absolute_paths. Generated + field-identifier visitor also allows non_camel_case_types. Correct first-party + names/qualifications cannot remove these generated attributes. + + Applied supported crate-path workaround; inspected all macros. Clippy with + -F unused, -F nonstandard_style, or -F unused_qualifications still rejects + pinned Serde expansion. No supported derive setting removes its unconditional + wrapper. Further changes require a generator fork or serialization replacement, + including security-sensitive identity/envelope/request formats: explicit + upstream/format migrations, not configuration fixes. + + Independently forbid every other unused member and non_snake_case. warnings + remains forbidden; explicit deny groups supply narrow effective levels needed + by generated attributes. Added no first-party allow/expect attributes. DUPLICATE DEPENDENCIES - clippy.toml restricts allowed-duplicate-crates to generic-array, getrandom, - and syn: the names actually diagnosed in this workspace. The Clippy lint - and cargo group remain forbidden for every other dependency. + clippy.toml allowed-duplicate-crates contains only diagnosed names: + generic-array, getrandom, syn. Lint/cargo group forbid all other duplicates. + Pinned crypto/parser/build graph needs incompatible semver lines: - The incompatible lines come from the pinned crypto/parser/build graph: generic-array 0.14.7 and 1.4.5 getrandom 0.2.17 and 0.4.3 syn 2.0.119 and 3.0.9 - These are different semver compatibility lines, not duplicate patch releases. - Unifying them requires upstream API changes or older dependencies. No crypto - algorithm/backend changes or dependency downgrades were made for a lint. - cargo-deny continues to enforce its exact reviewed version exceptions in - deny.toml, including build/development dependencies. Clippy's name-only - configuration does not replace that stronger version-specific audit. + These are not duplicate patch releases. Unification needs upstream API changes + or older dependencies. No crypto/backend changes or downgrades for lint. + cargo-deny enforces exact reviewed deny.toml exceptions, including build/dev + dependencies. Clippy's name-only allowance cannot replace that stronger audit. TYPESCRIPT - The compiler checks strict mode and the additional indexed access, optional - property, unused, return, switch, override, and import checks. noCheck and - allowJs are explicitly false. Both declaration-library skip flags are false. - isolatedModules, erasableSyntaxOnly, and verbatimModuleSyntax remain enabled. - Browser/worker configurations use Bundler resolution with Preserve output; - they contain no runtime imports. Tests and tooling use NodeNext separately. - - eslint.config.ts extends the typed strict and stylistic TypeScript presets. - All diagnostics are errors and any warning fails the check. Inline config - is disabled; suppression directives and unsafe assertions are prohibited. - Additional rules reject ambiguous boolean coercion, unhandled/misused promises, - explicit or leaked any, non-null assertions, unsafe calls/returns/arguments, - eval, unsafe casts, and unchecked discriminated-union switches. Functions - expose checked return types; callback contexts retain their declared contracts. - - UI async tasks now have explicit rejection handlers at the DOM boundary. - Test HTTP handlers also catch rejected work rather than returning unobserved - promises to Node. WASI validates its start export before calling it, and the - shared host is installed as a frozen, non-writable global property. Dynamic - JSON and worker boundaries still validate unknown values at runtime. - - TypeScript 7.0.2 remains the emitting compiler, installed under the explicit - typescript-native npm alias. typescript-eslint 8.72.1 supports TypeScript - >=4.8.4 <6.1.0 and requires the JavaScript compiler API, so TypeScript 6.0.3 - supplies that supported lint API. Sources are checked by both compilers; - no unsupported-peer or ignore-peer-dependency install flags are used. - Every invocation names the intended compiler path to avoid npm bin collisions. - - Isolated declaration emission is a library-distribution constraint, not an - additional application safety check. This project serves classic scripts - for the tested LibreJS path and does not publish a TypeScript library. - moduleDetection=force is used for tests/tools; changing browser scripts to - ESM solely for that setting would change their loading/LibreJS contract. - TypeScript 7 removed allowSyntheticDefaultImports=false, so that obsolete - switch is not used. Typed linting checks unsafe interop at use sites. + Check strict mode, indexed access, optional properties, unused, returns, + switches, overrides, imports. Explicitly disable noCheck/allowJs and both + declaration-library skip flags. Enable isolatedModules, erasableSyntaxOnly, + verbatimModuleSyntax. Browser/worker: Bundler resolution, Preserve output, + no runtime imports. Tests/tools: separate NodeNext configurations. + + eslint.config.ts extends typed strict/stylistic presets. All diagnostics are + errors; warnings fail. Disable inline config; prohibit suppression directives + and unsafe assertions. Reject ambiguous boolean coercion, unhandled/misused + promises, explicit/leaked any, non-null assertions, unsafe calls/returns/ + arguments, eval, unsafe casts, unchecked discriminated-union switches. + Functions expose checked return types; callbacks retain declared contracts. + + DOM async tasks and test HTTP handlers catch rejections; Node receives no + unobserved promises. WASI validates start export before calling it. Shared + host is a frozen, non-writable global. JSON/worker boundaries validate unknown + values at runtime. + + TypeScript 7.0.2 emits through explicit typescript-native npm alias. + typescript-eslint 8.72.1 supports >=4.8.4 <6.1.0 and requires JavaScript + compiler API; TypeScript 6.0.3 supplies it. Both compilers check sources. + No unsupported-peer/ignore-peer-dependency install flags. Name intended + compiler path in every invocation to avoid npm bin collisions. + + Isolated declaration emission constrains library distribution, not application + safety. This app publishes no TypeScript library; classic scripts preserve + tested LibreJS loading. moduleDetection=force applies to tests/tools. Do not + change browser scripts to ESM solely for that setting. TypeScript 7 removed + allowSyntheticDefaultImports=false; omit obsolete switch. Typed lint checks + unsafe interop at use sites. DEVELOPMENT DEPENDENCY ADVISORY - ESLint and typescript-eslint currently request minimatch 10. The public - GHSA-g3hv-75wg-m249 advisory affects minimatch <11.0.1. The root npm override - pins the patched 11.0.3 release instead of ignoring the advisory or applying - npm audit fix --force. Its supported exports and Node engine were reviewed; - typed linting and rule-selection/negative-control checks exercise compatibility. - Remove the override when upstream ranges admit a patched version naturally. + ESLint/typescript-eslint request minimatch 10. GHSA-g3hv-75wg-m249 affects + minimatch <11.0.1. Root npm override pins patched 11.0.3; no advisory ignore + or npm audit fix --force. Reviewed exports/Node engine; typed lint and rule- + selection/negative-control checks exercise compatibility. Remove override + when upstream ranges naturally admit a patched version. - npm audit --audit-level=info is part of npm run check and therefore the Cargo - check/release path. Development dependency findings fail the build too. - Installed tools preserve their original notices and machine SPDX identifiers. - Human documentation calls SPDX MIT the Expat License. No third-party - JavaScript dependency is delivered to visitors. + npm audit --audit-level=info runs in npm run check and Cargo check/release. + Development findings fail too. Tools retain original notices/machine SPDX. + Human docs call MIT the Expat License. Visitors receive no third-party JS. CHECKS AND EXPORTS @@ -169,15 +145,14 @@ CHECKS AND EXPORTS cargo stack check cargo stack release - The first checks both TypeScript compilers, typed ESLint, and npm advisories, - then emits the browser/worker/test artifacts. Cargo also runs formatting, - strict Clippy, all workspace tests, strict Rustdoc, cargo-deny, and cargo-about. - Browser checks and GNU LibreJS controls are documented in TYPESCRIPT.txt. + npm build checks both compilers, typed ESLint, npm advisories; emits browser/ + worker/test artifacts. Cargo adds formatting, strict Clippy, all workspace + tests, strict Rustdoc, cargo-deny, cargo-about. Browser/LibreJS controls: + TYPESCRIPT.txt. - clippy.toml, eslint.config.ts, every tsconfig, and both lockfiles are included - in corresponding sources, releases, and backups. Installed node_modules, - runtime state, and the private .env remain outside public exports. Nothing - introduces another deployment environment file or a production Node runtime. + Corresponding sources/releases/backups include clippy.toml, eslint.config.ts, + every tsconfig, both lockfiles. Public exports exclude installed node_modules, + runtime state, private .env. No extra deployment env file or production Node. PRIMARY REFERENCES diff --git a/docs/MAP.txt b/docs/MAP.txt new file mode 100644 index 0000000..dc4da33 --- /dev/null +++ b/docs/MAP.txt @@ -0,0 +1,192 @@ +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/>. + +FILE 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. + +stack/ Personal service source, build recipes and recovery tooling. +|-- .cargo/ Cargo command aliases. +| `-- config.toml Cargo stack alias. +|-- crates/ Workspace application/configuration crates. +| |-- chat-crypto/ Fixed-suite Rust and browser cryptography. +| | |-- browser/ Browser crypto types and WASI adapter. +| | | |-- protocol.d.ts Shared typed API, crypto, worker, WASI message contracts. +| | | `-- wasi.ts Browser WASI host: memory, I/O, entropy, command execution. +| | |-- src/ Rust crypto library and executable. +| | | |-- application.rs Protected identities, authentication proofs, message encryption/decryption. +| | | |-- lib.rs Public crypto API and fixed-suite compatibility round trip. +| | | `-- main.rs Native/WASI executable: request input and crypto result output. +| | |-- tests/ Real-browser crypto compatibility tests. +| | | `-- browser.ts Real-browser crypto probe, entropy-failure and network-isolation checks. +| | |-- Cargo.toml Crypto library/probe targets, fixed backend features, dependencies. +| | `-- README.txt Crypto suite, native/WASI build, compatibility checks. +| |-- chat-server/ Private-chat backend and browser client. +| | |-- src/ Rust HTTP server and maintenance entry. +| | | |-- app/ Server argument, HTTP, service and storage modules. +| | | | |-- cli.rs Server flags, listener defaults, maintenance argument conflicts. +| | | | |-- http.rs HTTP routes, packaged assets, response security headers. +| | | | |-- maintenance.rs Server file lock, owner fingerprint, destructive owner reset. +| | | | |-- service.rs Authentication challenges/sessions, authorization, chat operations. +| | | | `-- store.rs SQLite owner metadata, conversations, ciphertext history persistence. +| | | |-- app.rs Server initialization, origin/listener checks, command dispatch. +| | | `-- main.rs Chat server executable entry point. +| | |-- tests/ Chat browser integration and loading-state tests. +| | | |-- browser.ts Chat browser flows, owner/visitor access, packaged backup/restore. +| | | `-- progress.ts Crypto-download progress, unknown length, failure and LibreJS checks. +| | |-- web/ Chat markup, styling and TypeScript client. +| | | |-- app.ts Chat browser UI, credentials, API calls, encrypted conversations. +| | | |-- index.html Chat page markup and public-origin template. +| | | |-- javascript.html Browser JavaScript license/source listing. +| | | |-- style.css Chat page layout, controls, status and conversation styling. +| | | `-- worker.ts One-operation crypto worker; returns result/error, then closes. +| | |-- Cargo.toml Chat HTTP server dependencies and workspace lint inheritance. +| | `-- README.txt Server purpose, local startup, browser-test entry. +| |-- config/ Shared environment/configuration crate. +| | |-- src/ Validated configuration and public-rendering implementation. +| | | `-- lib.rs Root .env parsing/validation, defaults, public-only template values. +| | |-- Cargo.toml Shared configuration crate dependencies and lint inheritance. +| | `-- README.txt Configuration crate responsibilities and public rendering boundary. +| `-- 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-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 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. +| |-- POLICY.txt License/header, source-comment, configuration, strictness and portability rules. +| `-- TYPESCRIPT.txt Typed browser/worker/test builds, ESLint policy, compiler/test commands. +|-- infra/ Service definitions, host integration and build recipes. +| |-- chat/ Native/WASI/TypeScript/source build scripts. +| | |-- build-browser.sh Link Rust/OpenSSL browser crypto command into WASI WebAssembly. +| | |-- build-native.sh Build static musl management/server commands and native crypto probe. +| | |-- build-openssl-wasi.sh Fetch verified SDK/OpenSSL sources; build browser crypto library. +| | |-- build-source.sh Archive corresponding source, vendored Rust crates and OpenSSL source. +| | |-- build-typescript.sh Install locked dev dependencies; run typed checks and emit browser JS. +| | `-- openssl-wasi.conf OpenSSL build target settings for WASI. +| |-- containers/ Container build contexts. +| | |-- cgit/ cgit image and its nginx/FastCGI process configuration. +| | | |-- .containerignore Restrict cgit build context to required container files. +| | | |-- Containerfile Build pinned Debian cgit/nginx/FastCGI/supervisor image. +| | | |-- cgitrc cgit repository listing, paths and presentation template. +| | | |-- entrypoint.sh Prepare cgit container runtime and start its supervisor. +| | | |-- nginx.conf Serve cgit HTTP/static assets; route CGI through fcgiwrap. +| | | `-- 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/ 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. +| | |-- containers.conf Guix-only PATH-selected conmon/crun and rootless networking. +| | |-- manifest.scm Guix packages for the portable container/runtime tools. +| | |-- restore.sh Check capsule/host, decrypt safe restore, require images, start production. +| | |-- run.sh Select content-addressed Guix runtime; launch stack with explicit root. +| | |-- test-bootstrap.sh Real bootstrap in isolated filesystem using synthetic closure packs. +| | |-- test-recovery.sh Synthetic release activation, resume, intent/locks and private-state preservation. +| | `-- test.sh Bootstrap, immutable cleanup, integrity, overwrite refusal and launcher fixtures. +| |-- searxng/ Search service configuration. +| | `-- settings.yml SearXNG defaults: private instance, rate limiting, HTML search. +| |-- Caddyfile Production domain routing, HTTPS, WKD and service reverse proxies. +| |-- Caddyfile.local Loopback development routing without public certificate requests. +| |-- host-setup.sh Debian ops-user Podman packages, UID/GID checks, low ports and linger. +| |-- podman.conf Legacy Podman override selecting slirp4netns rootless networking. +| |-- services.toml Service images, mounts, environment allowlists, security and health probes. +| `-- stack.service Systemd user boot unit for ~/stack start/stop. +|-- src/ Management executable source. +| |-- app/ Management command implementation modules. +| | |-- archive.rs Consistent snapshots, encrypted exports, legacy Caddy layout and validated restores. +| | |-- chat.rs Chat build/package artifacts, container settings and owner administration. +| | |-- cli.rs Management subcommands, flags and argument validation. +| | |-- files.rs Safe filesystem/archive operations, manifests, hashes and runtime copies. +| | |-- images.rs Export/import saved images with archive-only policy, platform and content locks. +| | |-- process.rs Run child processes; capture output and report failures/status. +| | |-- project.rs Discover/lock project; settings, Podman commands, service lifecycle. +| | |-- release.rs Build checked release archives; validate/install production artifacts. +| | `-- render.rs Render per-mode Caddy, website and cgit configuration. +| |-- app.rs Management command dispatch, check pipeline and license generation. +| `-- main.rs Management executable entry point. +|-- tests/ Shared first-party browser-test helpers. +| `-- browser-support.ts Shared browser-test response validators, server and browser cleanup helpers. +|-- web/ Main-site template, styles, licenses and public key discovery. +| |-- .well-known/ Standard public discovery endpoints. +| | `-- openpgpkey/ WKD public-key discovery tree. +| | |-- hu/ Direct WKD hashed-user key files. +| | | `-- t76q8xqkeassgy1kr38re1ppj6cpc1dp Owner public key for direct WKD lookup. +| | |-- luuduct.ai/ Advanced WKD namespace for luuduct.ai. +| | | |-- hu/ Advanced WKD hashed-user key files. +| | | | `-- t76q8xqkeassgy1kr38re1ppj6cpc1dp Same owner public key for advanced luuduct.ai WKD lookup. +| | | `-- policy Advanced WKD policy marker for luuduct.ai. +| | `-- policy Direct WKD policy marker. +| |-- THIRD-PARTY-LICENSES.html Generated Rust dependency attribution and full license texts. +| |-- index.html Main-site public links, identity and service-origin template. +| `-- styles.css Main-site visual layout, links and tooltips. +|-- .env.example Public settings template; private-value placeholders. +|-- .gitattributes Whitespace handling for generated third-party license HTML. +|-- .gitignore Exclude secrets, runtime state, dependencies, build output. +|-- Cargo.lock Exact Rust dependency versions and source checksums. +|-- Cargo.toml Management binary, workspace members, dependencies, strict lints. +|-- LICENSE Unmodified GNU AGPLv3 license text; sources allow later versions. +|-- README.txt Project entry: setup, commands, checks, documentation links. +|-- about.hbs HTML template for generated Rust dependency licenses. +|-- about.toml License-page acceptance list and private-package handling. +|-- clippy.toml Reviewed duplicate-crate exceptions for Clippy. +|-- deny.toml Dependency advisory, license, source and duplicate-version policy. +|-- eslint.config.ts Typed ESLint rules and per-environment file scopes. +|-- package-lock.json Exact npm dev dependency versions, integrity and dependency graph. +|-- package.json Browser tooling dependencies and npm check/build/test commands. +|-- rust-toolchain.toml Pinned Rust version, formatting/lint components and target triples. +|-- tsconfig.browser.json Browser main-thread and WASI-host compiler scope. +|-- tsconfig.json Shared strict TypeScript compiler options. +|-- tsconfig.tests.json Node browser-automation compiler scope. +|-- tsconfig.tools.json Node ESLint configuration compiler scope. +`-- tsconfig.worker.json Web-worker compiler scope, separate from DOM globals. + +PRIVATE AND GENERATED PATHS — NOT PUBLIC SOURCE + + .env Sole private root configuration; never commit. + .git/ Local Git objects, refs, hooks and repository settings. + .tools/ Downloaded toolchains, build tools, sources and caches. + bin/ Prebuilt management/chat commands, launcher and Guix pack. + dist/ Locally built release tarballs and checksum files. + node_modules/ Locked npm development dependencies; recreated by npm ci. + target/ Cargo build output, WASM targets and generated rustdoc. + var/ Private/generated project state; excluded from Git. + backups/ Imported backup archives and recovery test destinations. + build/ Generated TypeScript, source/vendor bundles and build work. + data/ Persistent application databases, TLS keys and Git repos. + caddy/ ACME account/certificate keys, Caddy data/configuration. + chat/ Production chat database: owner identity and ciphertext. + chat-local/ Isolated local chat database and server state. + repos/ Served bare Git repositories; separate from root .git/. + searxng/ Runtime search configuration and persistent service files. + exports/ Encrypted application backups and checksum files. + guix/ Unpacked content-addressed runtime packs; old packs retained. + incoming/ Copied release archives and temporary deployment bootstrap. + podman/ Recreated container storage/run state; never transfer as backup. + graphify/ Public-source graph index, visual map and extraction artifacts. + + Other var/ paths may hold generated fixtures or temporary work. This map does + not enumerate private files. Backups must keep .env and var/data; public Git + alone cannot recover services. See OPERATIONS.txt and GUIX.txt. + + 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 2c9fa53..bf675be 100644 --- a/docs/OPERATIONS.txt +++ b/docs/OPERATIONS.txt @@ -13,113 +13,84 @@ 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/>. -STACK OPERATIONS AND RECOVERY +STACK OPERATIONS - Application root: ~/stack - Development machine: local Linux workstation - Production host: ops@23.155.44.67 - Public source: https://git.doan.luuduct.ai/stack - License: GNU Affero General Public License, version 3 or later + Root: ~/stack. Workstation: local Linux. Production: ops@23.155.44.67. + Public source: https://git.doan.luuduct.ai/stack. + Production commands use bin/run, which selects packed Guix tools/config. + Direct bin/stack uses host tools; never mix engines against the same storage. -WHAT YOU NEED TO KEEP +KEEP - A full encrypted export made with --include-images contains public source, - the compiled management command, .env, persistent application data, - certificate state, bare repositories, working Git history if present, and - the exact service images. It includes a file checksum manifest. + 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. - It excludes the container engine's storage, build caches, old exports, - host packages, host SSH keys, firewall rules, disks, and OS configuration. - These are recreated on a new host. No old server filesystem is required. - - The public Git repository alone is not a backup: it excludes .env, live - data, private certificates, and application repositories under var/. - - Your GPG private key and its passphrase are needed to decrypt backups. - Keep the private-key export and revocation certificate in KeePassXC and - keep an independent copy of the database. The server has only the public - key. This guide never needs your private key or password in chat. - - Default backup recipient fingerprint: + 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. -DAILY DEVELOPMENT - - Run these from ~/stack on your workstation: +DEVELOP LOCAL cargo stack dev cargo stack status cargo stack down - Visit http://localhost:8080, http://localhost:8080/git/, and - http://localhost:8080/search/. To use different ports: - - cargo stack dev --port 9080 - - Local mode binds loopback only, rewrites public service links locally, - keeps public DNS untouched, and does not request public TLS certificates. - It automatically creates .env with a random SearXNG secret if absent. - Edit .env to change configured secrets. Do not commit it. + 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. - Public service settings belong in infra/, and website content in web/. - New Rust backends belong in crates/, explicitly added to Cargo.toml. - Services are declared in infra/services.toml. A service receives only the - .env keys listed in its environment array, not every project secret. - - Rust commands use a project-specific Podman store under var/podman/. - A bare 'podman ps' lists your normal store, which may be empty. To inspect - this project's store manually, run 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 - Reuse those two global options before 'logs CONTAINER', 'exec CONTAINER', - or 'images'. Container names are shown by cargo stack status. - Never copy var/podman/ to another machine; export images instead. - -STRICT LOCAL CHECKS + Keep both global options for logs/exec/images. Use bin/run status on production. + Never transfer var/podman as backup; export saved images instead. - Install the two development-only 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 - - Then run: - cargo stack check + cargo run --release --locked -- release - This checks formatting, all workspace Clippy targets/features, tests, - private rustdoc, dependency advisories, duplicate versions, sources, and - licenses. It regenerates THIRD-PARTY-LICENSES.html through cargo-about. - Use cargo fmt --all when formatting needs correction. - - After changing dependency requirements, update Cargo.lock deliberately, - run the complete check, and commit the lockfile and generated license page. - Never disable audit failures merely to get a release out. + 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. -EXPORT AND 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 the VPS: +BACK UP PRODUCTION cd ~/stack - ./bin/stack backup --include-images + ./bin/run backup --include-images - The command briefly pauses Caddy, cgit, and SearXNG while copying their - application files, then resumes them before compression and encryption. - Chat remains unpaused so SQLite online backups can complete during active - writes. Consistent snapshots cover both chat and chat-local databases; raw - SQLite journals and server lock files are excluded. - It uses the published public key, verifies the recipient fingerprint, - checks bare Git repositories, saves exact images, and produces: + Briefly pauses Caddy/cgit/SearXNG during file copy; resumes before compression + 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 - Both files have restrictive permissions. Temporary plaintext staging is - removed on normal completion and handled errors. No scheduled backup job - is installed. You decide when and where to retain exports. - - On your workstation, create a destination under stack and copy the two - actual filenames printed by the command: + Copy both files off the VPS; substitute actual filenames: umask 077 mkdir -p ~/stack/var/backups/production @@ -128,222 +99,118 @@ EXPORT AND BACK UP PRODUCTION cd ~/stack/var/backups/production sha256sum -c stack-TIMESTAMP.tar.gz.gpg.sha256 - Replace TIMESTAMP with the actual timestamp. A checksum detects accidental - corruption; keep the trusted encrypted file and checksum together. - Retain at least one copy on another device or location. + 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. - Once copied and verified, delete only the corresponding VPS export files - if desired. Do not delete var/data or .env. +RECOVER ON LINUX - A smaller export without images is available through ./bin/stack backup. - It needs registry/build access for local startup and is not a standalone - production deployment. Prefer --include-images for disaster recovery. + 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: -RESTORE ON ANOTHER LINUX MACHINE + tar -xzf recovery.tar.gz + sudo ./stack-recovery/bootstrap + ./stack-recovery/restore /home/ops/stack - Install Podman, Git, GnuPG, and normal rootless networking prerequisites. - Rootless Podman needs at least 65536 subordinate UIDs and GIDs for the - target user. Use that distribution's supported Podman installation. - The current ready-made binary/images target x86_64 Linux (linux/amd64). + 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. - Copy the encrypted export and its checksum to the new machine. Verify - the checksum first. Import your secret-key backup if not already present: + A raw format-2 export can be restored for inspection on a host that already + has compatible tools and any required Guix closure: - gpg --import private-key-backup.asc + ./bin/run restore /path/export.tar.gz.gpg /path/stack-restored - GnuPG asks for the key passphrase through its normal local interface. - Never pass it on the command line or put it in .env. + 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. - If you already have a compatible stack command, restore with it: +HOST AND BOOT SETUP - ./bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack-restored - - The destination must not exist. Restore decrypts privately, rejects unsafe - archive paths and links, checks every manifest entry, assigns a unique - project identity, and defaults to local mode. It does not start services. - It refuses to overwrite an existing installation. - - For a new machine with no stack command or Rust toolchain, bootstrap with - standard GnuPG and tar. The shell commands below work in bash and fish: - - 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 - - Only use this bootstrap with your own trusted export; the extracted binary - is executable code. The restore command validates the complete archive. - After successful restoration, delete ~/stack-bootstrap, which contains - the temporary decrypted archive. Keep only the encrypted export as backup. - - Start the recovered stack locally first: + 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 --local --port 8080 - ./bin/stack status - - Saved images load automatically and their identities are checked against - the image lock. No registry pull or compilation is needed on a matching - architecture. Check the homepage, Git repositories, search, and WKD. - Then stop it with ./bin/stack down before configuring production. - - A release archive can be restored by the same command. Releases contain - source, a binary, and images, but no .env or application data. Starting a - restored release creates fresh state; it does not recover lost data. - -SET UP A NEW PRODUCTION HOST - - Restore to the target user's ~/stack, using the steps above. On Debian, - adapt the username 'ops' in infra/host-setup.sh if necessary, review it, - and run: - - sudo sh ~/stack/infra/host-setup.sh - - It installs Podman and networking tools, checks existing subordinate - UID/GID ranges, enables user linger for boot, and sets: - - net.ipv4.ip_unprivileged_port_start=80 - - This allows rootless listeners on ports 80 and above system-wide; ports - below 80 remain privileged. It does not change SSH or your firewall. - If the user lacks subordinate ranges, allocate unused ranges through - the distribution's account administration before rerunning the script. - - On other Linux distributions, install equivalent prerequisites and set - that sysctl and user linger using the host's supported tools. Production - requires a systemd user manager for this project's supplied boot service. - - Allow inbound 22/tcp, 80/tcp, 443/tcp, and 443/udp through the host/provider - firewall, for IPv4 and IPv6 as applicable. Backend container ports must not - be published. Preserve your SSH access and verify the address before DNS - changes. - - Point public A records to the new IPv4 address, and AAAA records only to a - working new IPv6 address. Remove stale AAAA records. Public hostnames are - configured in the root .env. Edit those values and reapply the service - configuration; infra/Caddyfile is now a public rendering template. - See CONFIGURATION.txt for all keys and the render-only commands. - Keep the www aliases and openpgpkey names pointing to the canonical hosts. - - Start production: - - cd ~/stack - ./bin/stack up --production - - Production binds 80/tcp, 443/tcp, and 443/udp. Caddy manages certificates - and renewals automatically, redirects HTTP to HTTPS, and redirects www - aliases to the canonical URLs. Certificate state is in var/data/caddy/. - Public certificate issuance needs working public DNS and connectivity. - - Enable startup at boot: - + ./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 - This unit assumes ~/stack. Edit its paths if deliberately using another - location. User linger keeps it running without an interactive login. - Application containers use an unless-stopped restart policy. - - If offering SSH Git access at the former VPS path, create this optional - host symlink as the administrator: - - sudo install -d -m 755 /srv/git - sudo ln -s /home/ops/stack/var/data/repos/stack /srv/git/stack + 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. - Do not overwrite an existing repository there. The current VPS already - has this link, updated for the new layout. Public HTTPS Git works without - the optional host link. + 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. -MAKE AND DEPLOY A RELEASE - - Develop and run the checks on your workstation, then: - - cd ~/stack - cargo stack release +DEPLOY A CHECKED RELEASE - Release reruns the full check, builds static Rust management and chat - binaries plus the browser WASM module, packages corresponding sources, - builds cgit/chat containers locally, and saves the configured image set. - Outputs are dist/stack-release-TIMESTAMP.tar.gz and its .sha256 file. - No .env, live data, .git history, or previous backup is included. + Review/stage public changes, sign and push the current branch: - Test the release in a fresh local directory before deploying, using the - restore command followed by up --local on an unused port. Inspect the - same four services and then stop the test installation. - - Commit and push reviewed public changes with normal Git: - - git add -A git diff --cached --stat - git commit -m 'Describe the change' - git push origin main - - .gitignore excludes configured secrets and all runtime/artifact trees. - Review the staged files. Deployment uses the release archive, not git pull. + git commit -S3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 -m 'Describe the change' + git push origin HEAD - Copy the actual archive and checksum to production: + 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. - 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/ + Bash on VPS, after that bootstrap; substitute actual TIMESTAMP: - On the VPS: - - 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 the management binary from the verified new release for activation. - Older binaries can reject newer service schemas. The verified release - binary handles activation without installing a compiler or overwriting - private configuration. - - Activation verifies the release and platform, replaces public source and - the binary, loads exact images, preserves .env and var/data, and replaces - the running containers. It does not compile Rust or build container images. - Expect a brief interruption while containers are replaced. Take a verified - export first if a future change could require recovery. - - After verifying production, delete the corresponding incoming archive and - checksum if you do not want release copies left on the VPS. - -RECOVERY LIMITS AND SECRET ROTATION - - Exports capture this application, not an entire bootable VPS image. - Host account, SSH, firewall, and package setup remain host responsibilities. - Changing CPU architecture requires a native release build. Registry access - is needed only when images are omitted or rebuilding for another target. - - Current persistent state includes files, Git repositories, Caddy state, - and encrypted chat records in SQLite. Chat uses the SQLite online backup - API; recovery testing must reopen both participants' unchanged identity - files and decrypt history. See CHAT-PRODUCTION.txt for this release's tests. - Future databases need their own supported snapshot and recovery procedure. - - If changing the OpenPGP backup recipient, publish its public key, update - the command's default fingerprint, and test encryption and restoration. - For one export, use --recipient FULL_FINGERPRINT --key /path/public-key.asc. - Keep old secret keys while any retained export still depends on them. - - The restore command accepts verified format-2 application archives. - -CENTRAL THIRD-PARTY LICENSE PAGE - - cargo-about generates web/THIRD-PARTY-LICENSES.html. Caddy serves the rendered - document at https://doan.luuduct.ai/THIRD-PARTY-LICENSES.html, using the - configured SITE_HOST for other deployments. Website and chat license links - use that same canonical page. Local previews use the local main-site origin. - - The generated file is accessible through cgit's normal Source and Raw views. - Chat's /THIRD-PARTY-LICENSES.html URL redirects to the canonical site page. - Source exports retain license texts for offline use. - - Homepage /message and /message/ URLs redirect to the current chat origin. + 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 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 4c6c6d3..c3dffae 100644 --- a/docs/POLICY.txt +++ b/docs/POLICY.txt @@ -17,89 +17,84 @@ PROJECT CONVENTIONS License: GNU Affero General Public License, version 3 or later. SPDX identifier: AGPL-3.0-or-later. - Copyright holder: Doan Luu Duc Tai <doan@luuduct.ai>, 2026. - - Every nontrivial first-party source, configuration, template, and prose - file carries the AGPL copyright and permission header. LICENSE is the - unmodified license text. Binary public keys and empty WKD policy files - retain their protocol format. Generated Cargo.lock retains Cargo's own - metadata as well as the project notice. JSON npm manifests carry the legal - notice in a top-level // metadata field; compiler JSONC files use the usual - header syntax. Keep generated JavaScript legal markers when compiling. - - Do not add explanatory source comments. Express intent through structure - and names. Legal headers are the required exception. Use .txt for prose - documentation. The generated HTML license page is an explicit exception. - Rust documentation attributes provide crate metadata without comments. - - Keep deployment overrides and configured secrets in the single root .env. - Never commit .env or var/. - Only the root .env.example is published; environment variants and nested - examples are ignored. The commented .env.example publishes supported public - defaults and private placeholders. Public GPG keys, paths, templates, shared - configuration logic, and source belong in Git. Actual public hostnames remain - visible in rendered pages; .env centralization is not an access boundary. - TLS account/certificate private keys, databases, and other generated - private state are persistent data in var/data, not .env configuration. - The GPG secret key and revocation certificate remain outside this project. + Copyright holder/commit author: Doan Luu Duc Tai <doan@luuduct.ai>, 2026. + Sign commits with RSA-2048 key: + 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 + + Every nontrivial first-party source/config/template/prose file needs the AGPL + copyright and permission header. LICENSE stays unmodified. Binary public keys + and empty WKD policy files retain protocol format. Generated Cargo.lock keeps + Cargo metadata plus project notice. JSON npm manifests use top-level // legal + metadata; compiler JSONC uses its usual header syntax. Preserve generated + JavaScript legal markers. + + No explanatory source comments. Express intent through names/structure. + Required legal headers are the exception. Prose uses .txt; generated HTML + license page is an explicit exception. Rust doc attributes supply crate + metadata without comments. + Temporary container import/export policies are generated protocol JSON: its strict + foreign schema rejects legal metadata. AGPL notice stays in the Rust generator. + + One root .env holds deployment overrides/configured secrets. Never commit + .env or var/. Publish only root .env.example; ignore variants/nested examples. + Its comments document public defaults/private placeholders. Public GPG keys, + paths, templates, shared config logic, and source belong in Git. Hostnames + stay visible in rendered pages; .env centralization is no access boundary. + TLS account/certificate private keys, databases, and generated private state + live under var/data, not .env. Keep GPG secret key/revocation certificate + outside the project. STRICTNESS - All enabled Clippy groups and individual rules are forbidden, with no - blanket allow entries. Rust and Rustdoc checks use forbid wherever supported - by generated code. Three Rust settings remain deny for pinned Serde derives; - their unaffected members are independently forbidden. First-party lint - allowances are forbidden. See LINTING.txt for tested workarounds, generated - attribute evidence, and the narrow duplicate-dependency configuration. - Private app namespaces retain narrow internal visibility while both - redundant_pub_crate and unreachable_pub are enforced. - - cargo-deny rejects duplicate crate versions, wildcard requirements, unknown - registries, unknown Git sources, and yanked versions. The chat prototype has - exact-version duplicate exceptions reviewed in CHAT-OPENSSL-MIGRATION.txt. - No RustSec advisories are ignored. First-party AGPL and required dependency - license allowances are scoped to their individual packages. - The dependency allowlist retains the requested permissive licenses and - Zlib; an unused allowance may produce an informational audit warning. - - Browser logic and automation use strict TypeScript configurations with - separate browser/worker/Node environments and checked untrusted responses. - npm dependencies are exact, development-only, integrity-locked, and installed - with lifecycle scripts disabled. Typed ESLint forbids unsafe any/assertions, - unchecked promises, and suppression directives. Both supported compiler APIs - and npm advisories are checked. See TYPESCRIPT.txt and LINTING.txt. - - Human license names follow https://www.gnu.org/licenses/license-list.html. - In particular, display Expat License for SPDX MIT. Do not alter original - copyright notices, legal texts, crate metadata, or machine SPDX identifiers. - cargo-about covers Rust dependencies. Supporting container applications - and their operating-system packages carry additional upstream licenses. + Forbid every enabled Clippy group/individual rule; no blanket allows. + Rust/Rustdoc use forbid where generated code supports it. Three Rust settings + stay deny for pinned Serde derives; unaffected members remain independently + forbidden. No first-party lint allowances. LINTING.txt records tested + workarounds, generated-attribute evidence, and narrow duplicate exceptions. + Private app namespaces keep narrow visibility while redundant_pub_crate and + unreachable_pub both remain enforced. + + cargo-deny rejects duplicate versions, wildcard requirements, unknown + registries/Git sources, and yanked versions. Chat has exact-version duplicate + 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. + + Strict TypeScript separates browser/worker/Node environments and validates + untrusted responses. npm dependencies stay exact, development-only, + integrity-locked; disable installation lifecycle scripts. Typed ESLint rejects + unsafe any/assertions, unchecked promises, and suppression directives. Check + both supported compiler APIs and npm advisories. See TYPESCRIPT.txt/LINTING.txt. + + Human license names follow https://www.gnu.org/licenses/license-list.html: + display Expat License for SPDX MIT. Preserve original copyright, legal text, + crate metadata, and machine SPDX identifiers. cargo-about covers Rust. + Container applications/OS packages retain their additional upstream licenses. PORTABILITY - All first-party project files and state live below stack/. Standard host - integration consists of packages, subordinate UID/GID allocations, one - low-port sysctl setting, user linger, and a systemd user-service link. - Host SSH keys, firewall policy, accounts, and disks are host administration, - not application export contents. + All first-party files/state live below stack/. Standard host integration: + 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. + Guix capsule bootstrap installs canonical /gnu/store packages and + /gnu/.stack-runtimes install records. Private application state stays in stack/. - Releases contain static x86_64 Linux management/chat commands, the WASM - browser crypto module, exact corresponding sources, and native - linux/amd64 container images. They are portable across compatible x86_64 - Linux machines. An ARM machine needs a native release build; the command - rejects mismatched production images. Development mode may fetch/build - native images on an architecture mismatch. Container source and Rust - source are public so new targets can be added without restoring an OS. + Releases include static x86_64 Linux management/chat commands, WASM browser + crypto, exact corresponding sources, native linux/amd64 container images. + Compatible x86_64 Linux hosts can run them. ARM needs a native release build; + production rejects mismatched images. Development may fetch/build native + images on architecture mismatch. Public container/Rust sources permit new + targets without restoring an OS. - Generated third-party legal texts retain upstream whitespace and line endings. - .gitattributes exempts only THIRD-PARTY-LICENSES.html from whitespace errors; - first-party source checks remain strict. Workspace audits include every member. + Preserve upstream whitespace/line endings in generated third-party legal + texts. .gitattributes exempts only THIRD-PARTY-LICENSES.html from whitespace + errors. First-party checks stay strict; workspace audits cover every member. STATIC ASSET CACHING - Main-site stylesheet links include the SHA-256 of web/styles.css as a query - version. Update both website templates when CSS changes. The render test - checks that the published URL matches the current stylesheet, preventing - old cached CSS from being paired with new markup. Tooltip text carries the - native hidden attribute as well as CSS visibility rules, so a stylesheet - failure cannot expose it as ordinary page text. + Main-site stylesheet URLs include web/styles.css SHA-256 as query version. + Update both website templates after CSS changes. Render test checks the + published hash, preventing stale CSS/new markup pairs. Tooltip text uses + native hidden plus CSS visibility, so stylesheet failure cannot expose it. diff --git a/docs/TYPESCRIPT.txt b/docs/TYPESCRIPT.txt index 7fbc5a4..7fa5125 100644 --- a/docs/TYPESCRIPT.txt +++ b/docs/TYPESCRIPT.txt @@ -15,81 +15,75 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. TYPESCRIPT BROWSER TOOLCHAIN - All first-party browser logic and browser automation are TypeScript. - Rust still implements cryptography, the API, backups, and deployment. - Browsers execute JavaScript generated by TypeScript; TypeScript adds - compile-time checks and does not replace validation of untrusted input. + First-party browser logic/automation use TypeScript. Rust handles crypto, + API, backups, deployment. Browsers run generated JavaScript; compile-time + types never replace validation of untrusted input. SOURCE LAYOUT crates/chat-server/web/app.ts - Interface, session state, history, downloads, and API validation. + Interface, session, history, downloads, API validation. crates/chat-server/web/worker.ts - One-shot cryptographic worker; its lifetime ends after each request. + One-shot crypto worker; ends after each request. crates/chat-crypto/browser/wasi.ts - Restricted browser WASI host for the unchanged Rust/OpenSSL module. + Restricted WASI host for unchanged Rust/OpenSSL module. crates/chat-crypto/browser/protocol.d.ts - Shared API and cryptographic request/response contracts. Declarations - emit no JavaScript and are checked for both browser environments. + Shared API/crypto request/response contracts. No emitted JavaScript; + checked in both browser environments. crates/chat-crypto/tests/browser.ts - Real WebAssembly compatibility, entropy, and capability tests. + Real WebAssembly compatibility, entropy, capability tests. crates/chat-server/tests/browser.ts - Complete browser chat and recovery tests, including GNU LibreJS. + Complete browser chat/recovery tests, including GNU LibreJS. crates/chat-server/tests/progress.ts - Download progress and failure tests, including GNU LibreJS. + Download progress/failure tests, including GNU LibreJS. tests/browser-support.ts - Checked fixture parsing and typed browser-automation helpers. + Checked fixture parsing, typed browser-automation helpers. - eslint.config.ts and tsconfig.tools.json supply the typed lint-tool checks. - src/app and crates/chat-server/src/app contain the private Rust CLI - implementations; their parser and runtime changes are recorded in LINTING.txt. + eslint.config.ts/tsconfig.tools.json provide typed lint-tool checks. + Private Rust CLIs: src/app and crates/chat-server/src/app. Parser/runtime + changes: LINTING.txt. - tsconfig.json supplies shared strict checks. The browser, worker, and - test configurations use separate DOM, WebWorker, and Node environments. - Production browser builds cannot accidentally rely on Node APIs. - strict, unchecked indexed access, exact optional properties, unused - declarations/parameters, implicit returns, switch fallthrough, implicit - overrides, and indexed-property access are checked. Type errors prevent - emission. No suppression directives or untyped escape hatches are used. + tsconfig.json supplies shared strict checks. Separate browser/worker/test + configs use DOM/WebWorker/Node environments, preventing accidental production + Node API use. Check strict, unchecked indexed access, exact optional properties, + unused declarations/parameters, implicit returns, switch fallthrough, implicit + overrides, indexed-property access. Type errors prevent emission. + No suppression directives or untyped escape hatches. DEVELOPMENT - Install Node >=22.18.0 and npm on the development machine. Root package.json - and package-lock.json pin TypeScript 7.0.2, Playwright 1.64.1, Node type - declarations, platform compiler packages, and their integrity hashes. - ESLint 10.14.0 and typescript-eslint 8.72.1 provide typed strict linting. - TypeScript 6.0.3 supplies their supported JavaScript API; the pinned native - TypeScript 7.0.2 alias remains the emitting compiler. Both check the sources. - LINTING.txt documents the advisory-driven minimatch override. - There are no npm application/runtime dependencies. TypeScript and - Playwright use the Apache License, Version 2.0; Node declarations and - undici-types use the Expat License. Original notices remain in the - installed packages. SPDX identifiers retain their required spelling. + Development needs Node >=22.18.0/npm. Root package.json/package-lock.json pin + TypeScript 7.0.2, Playwright 1.64.1, Node declarations, platform compilers, + integrity hashes. ESLint 10.14.0/typescript-eslint 8.72.1 supply typed strict + linting. TypeScript 6.0.3 supplies their supported JavaScript API; native + TypeScript 7.0.2 alias emits. Both check sources. LINTING.txt explains the + advisory-driven minimatch override. No npm application/runtime dependencies. + TypeScript/Playwright: Apache License, Version 2.0. Node declarations/ + undici-types: Expat License. Installed packages retain original notices; + machine SPDX spelling stays unchanged. - Build and check all TypeScript, including browser tests: + Build/check all TypeScript, including browser tests: npm run build - infra/chat/build-typescript.sh installs the locked development packages - with lifecycle scripts disabled, checks browser/worker/test/tool configurations, - typed ESLint, both compiler versions, and npm advisories, then - emits JavaScript under var/build/typescript. The npm cache stays under - .tools/npm-cache. node_modules and generated outputs are ignored by Git. - Generated scripts are readable and unminified; no bundler is needed. + infra/chat/build-typescript.sh installs locked dev packages without lifecycle + scripts, checks browser/worker/test/tool configs, typed ESLint, both compilers, + npm advisories; emits JavaScript under var/build/typescript. npm cache: + .tools/npm-cache. Git ignores node_modules/generated output. Scripts stay + readable/unminified; no bundler needed. - cargo stack check includes this build, then the strict Rust checks and - cargo-about license generation. cargo stack release builds TypeScript - before packaging the complete production release. + cargo stack check runs this build, strict Rust checks, cargo-about generation. + cargo stack release builds TypeScript before packaging production artifacts. - Recheck without reinstalling packages after npm run build: + Recheck after npm run build without reinstalling: npm run check - Fetch the pinned Playwright browser versions once: + Fetch pinned Playwright browsers once: PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" node_modules/.bin/playwright install chromium firefox - After building the native chat server and WASI module: + Build native chat server/WASI module, then run: PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:chat @@ -97,42 +91,35 @@ DEVELOPMENT CHAT_LIBREJS=1 PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:chat CHAT_LIBREJS=1 PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:progress - LibreJS variants require the signed Mozilla extension at - .tools/librejs.xpi as documented in CHAT-APPLICATION.txt. The complete - chat test proves LibreJS is active and rejects an unlicensed control. - Run chat variants sequentially because they share the default test port. - npm audit checks development dependencies as part of npm run check and the - Cargo check/release path. It does not audit Rust, browser-delivered code, or - containers; cargo-deny remains the separate strict Rust dependency audit. + LibreJS variants need signed Mozilla extension .tools/librejs.xpi; see + CHAT-APPLICATION.txt. Complete chat test proves LibreJS active and rejects an + unlicensed control. Run chat variants sequentially; they share default port. + npm audit checks dev dependencies in npm run check/Cargo check/release; + it does not audit Rust, delivered browser code, or containers. cargo-deny + remains the separate strict Rust dependency audit. RELEASES, SOURCE, AND LIBREJS - Generated app.js, worker.js, and wasi.mjs keep their existing public URLs. - They preserve the AGPL headers and recognized @license/@license-end - markers. The JavaScript license table links each generated file to its - complete corresponding TypeScript and Rust source archive. Classic scripts - and importScripts remain in use for LibreJS compatibility; the migration - introduces no runtime module loader or third-party browser scripts. - - Local native-server tests serve generated files from var/build/typescript. - The release packages those files into bin/chat/web and bin/chat/wasi.mjs. - Containers serve the packaged files. HTML and CSS are copied explicitly, - so TypeScript source files are not substituted for executable JavaScript. - /SOURCE.tar.gz includes TypeScript, shared declarations, all compiler - configurations, the npm lock, tests and build scripts, together with the - existing Cargo-vendored dependencies and verified OpenSSL source archive. - Node/npm/TypeScript are general-purpose development tools, documented and - pinned here; their installed copies and downloaded browsers are excluded. - - Source exports, releases, and backups carry the root npm manifests and - TypeScript configurations. Release activation and restored prebuilt images - need no Node, npm, TypeScript, or Rust installation on the destination. - They retain the single private root .env and existing persistent data. - The source archive excludes that .env, credentials, runtime databases, - exports, .git, node_modules, and build/cache directories. The root - .env.example remains public. No additional environment file is introduced. - - Test the actual generated JavaScript after any compiler upgrade. Static - types cannot guarantee privacy, security, or LibreJS recognition. Browser - storage restrictions, protocol validation, and algorithm choices continue - to be enforced by the existing runtime and Rust implementation. + Generated app.js/worker.js/wasi.mjs retain public URLs, AGPL headers, + recognized @license/@license-end markers. JavaScript license table links + each file to complete corresponding TypeScript/Rust source archive. + Classic scripts/importScripts preserve LibreJS compatibility. No runtime + module loader or third-party browser scripts added. + + Local native-server tests serve var/build/typescript. Release packages files + into bin/chat/web and bin/chat/wasi.mjs; containers serve packaged files. + Copy HTML/CSS explicitly; never substitute TypeScript for executable JS. + /SOURCE.tar.gz contains TypeScript, shared declarations, every compiler config, + npm lock, tests/build scripts, Cargo-vendored dependencies, verified OpenSSL + archive. Node/npm/TypeScript are general-purpose development tools pinned and + documented here. Exclude installed copies/downloaded browsers. + + Source exports/releases/backups carry root npm manifests/TypeScript configs. + Activation/restored prebuilt images need no destination Node/npm/TypeScript/ + Rust. Preserve single private root .env and persistent data. Source archive + excludes .env, credentials, runtime DBs, exports, .git, node_modules, build/cache + directories. Root .env.example remains public; no extra environment file. + + Test generated JavaScript after every compiler upgrade. Static types cannot + guarantee privacy, security, or LibreJS recognition. Existing runtime/Rust + still enforce browser storage restrictions, protocol validation, algorithms. |
