Copyright (C) 2026 Doan Luu Duc Tai This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . 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. 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, and tar. Native workspace checks need OpenSSL 3.5+ development libraries. Browser testing additionally needs Node, Playwright, Chromium, and Firefox. These tools remain on the workstation; production does not need Cargo or a compiler. cargo stack release This 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. 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. LOCAL MODES AND ORIGIN BINDING Direct Rust development: cargo stack chat http://127.0.0.1:9080/ var/data/chat Container preview: cargo stack dev http://chat.localhost:8080/ var/data/chat-local Production: ./bin/stack 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. 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. 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. The older three-service management binary cannot parse the new chat service schema; do not use that older binary to activate this release. The new binary verifies the archive and installs packaged chat artifacts before starting services. 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: ./bin/stack 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. 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. Restore to a new directory, verify, then explicitly start production mode: ./bin/stack 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. VERIFICATION The browser harness supports packaged HTTPS installations without Rust or target/ inside them. It creates disposable identities and a separate temporary GnuPG key, takes a complete encrypted export with images during live chat, restores into a new directory, starts the saved images, and reopens the same files to decrypt history. It also exercises wrong passwords, safe text, counters, 0-to-N history, 50-row batches, retry deduplication, origin rejection, browser storage checks, and backend reset guards. It never resets real data. Example after restoring a release to a disposable staging directory and 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 CHAT_TEST_ROOT=/absolute/disposable/stack CHAT_TEST_ORIGIN=https://chat.fixture.localhost:8443 PLAYWRIGHT_BROWSERS_PATH=.tools/browsers node crates/chat-server/tests/browser.mjs Run from the source checkout for the harness and Playwright installation. Add CHAT_LIBREJS=1 for Firefox with the actual GNU LibreJS add-on. That test verifies the add-on is active and blocks an unlicensed control script. Disposable staging needs a fresh database for each complete run. Browser automation permits the fixture's private CA only for functional tests. Separately verify its chain and hostname with curl --cacert and --resolve; test the canonical route and www redirect without disabling 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.