summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoan Luu Duc Tai <doan@luuduct.ai>2026-10-31 17:54:29 -0500
committerDoan Luu Duc Tai <doan@luuduct.ai>2026-10-31 17:54:29 -0500
commit5b89c370d5974f057306a52089f14c337565fd1d (patch)
treed75b3360ec074337c50194874dc2426543d6f764
parent536ead7243a4d2d8d75eb13159a3d00be4624cae (diff)
docs: shorten guides and map repository filesHEADmain
-rw-r--r--README.txt108
-rw-r--r--crates/README.txt11
-rw-r--r--crates/chat-crypto/README.txt52
-rw-r--r--crates/chat-server/README.txt12
-rw-r--r--crates/config/README.txt7
-rw-r--r--docs/CHAT-APPLICATION.txt298
-rw-r--r--docs/CHAT-OPENSSL-MIGRATION.txt198
-rw-r--r--docs/CHAT-PRODUCTION.txt183
-rw-r--r--docs/CHAT-SECURITY-REVIEW.txt123
-rw-r--r--docs/CONFIGURATION.txt156
-rw-r--r--docs/GIT-HISTORY.txt84
-rw-r--r--docs/LINTING.txt243
-rw-r--r--docs/MAP.txt175
-rw-r--r--docs/OPERATIONS.txt354
-rw-r--r--docs/POLICY.txt141
-rw-r--r--docs/TYPESCRIPT.txt153
16 files changed, 1002 insertions, 1296 deletions
diff --git a/README.txt b/README.txt
index 516f09b..c680f43 100644
--- a/README.txt
+++ b/README.txt
@@ -15,85 +15,63 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
STACK
- Personal web services, developed and managed through a root Cargo workspace.
- Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai>
- GNU Affero General Public License, version 3 or later. See LICENSE.
+ Four services: Caddy, cgit, SearXNG, Rust private chat.
+ Rust management CLI; strict TypeScript browser code; GNU LibreJS checks.
+ SearXNG and cgit's upstream supervisor use third-party Python. No first-party Python.
+ License: GNU Affero General Public License, version 3 or later. See LICENSE.
-GETTING STARTED
+START LOCAL
- Install Rust through rustup, Podman, Git, and GnuPG on a Linux development
- machine, plus Node >=22.18.0 and npm for the TypeScript browser build.
- Chat builds also need a GCC-compatible C compiler, Linux UAPI
- headers, libclang, pkg-config, make, Perl, curl, tar, and OpenSSL 3.5+
- development libraries for native workspace checks. The pinned toolchain
- includes rustfmt, Clippy, and the static x86_64 Linux target.
- See docs/CHAT-PRODUCTION.txt for the native/WASI release build.
-
- From this directory:
+ Linux tools: rustup, Podman, Git, GnuPG, Node >=22.18.0, npm.
+ Native chat checks: GCC-compatible C compiler, Linux UAPI headers, libclang,
+ pkg-config, make, Perl, curl, tar, OpenSSL >=3.5 development libraries.
+ Pinned Rust includes rustfmt, Clippy, static x86_64 Linux target.
+ Native/WASI release builds: docs/CHAT-PRODUCTION.txt.
cargo stack dev
cargo stack status
cargo stack down
- Browse http://localhost:8080, /git/, /search/, and http://chat.localhost:8080/.
- A fresh checkout generates a private .env automatically.
- Local mode binds only loopback and does not request public certificates.
-
-DEVELOPMENT AND PRODUCTION
-
- src/main.rs forwards to the private Rust management implementation in
- src/app.rs and src/app/. Future Rust application backends
- belong in crates/. Add each crate to the root workspace explicitly and
- inherit its strict workspace lints.
-
- infra/ holds service definitions, proxy configuration, container sources,
- and the production user service. web/ holds the public website and WKD.
- docs/ contains plaintext operations and policy documentation.
+ Open http://localhost:8080, /git/, /search/, http://chat.localhost:8080/.
+ Fresh checkout creates private .env. Local mode: loopback only, no public TLS.
- The single root .env holds deployment settings and configured secrets.
- The commented .env.example documents hostnames, ports, upstreams, chat,
- backup recipients, and secret-setting names. See docs/CONFIGURATION.txt.
- var/ contains private persistent data, container storage, temporary work,
- and exports. Generated TLS private keys are runtime data under var/.
- .env, var/, .tools/, target/, bin/, dist/, and node_modules/ are excluded
- from Git. Browser code and tests are TypeScript; see docs/TYPESCRIPT.txt.
+FILES AND STATE
- The four services are Caddy, cgit, SearXNG, and the Rust chat server. The management
- command is Rust. SearXNG and the cgit container's upstream supervisor retain
- their third-party Python implementation. No first-party Python is needed.
+ Full file tree and uses: docs/MAP.txt. Rules: docs/POLICY.txt.
+ CLI: src/main.rs, src/app.rs, src/app/. Backends: crates/.
+ Add crates explicitly to root workspace; inherit strict workspace lints.
+ infra/: services, proxy, containers, user service. web/: site and WKD.
+ docs/: .txt guides. Browser code/tests: TypeScript; docs/TYPESCRIPT.txt.
- Build and test on the development machine. Deploy prebuilt release
- archives. The production host needs Podman, Git, and GnuPG, but no Rust
- toolchain, Cargo, Node, npm, or TypeScript.
+ Exactly one root .env: settings and configured secrets.
+ One public .env.example: hosts, ports, upstreams, chat, backup keys/placeholders.
+ Generated databases and TLS keys: var/data. More: docs/CONFIGURATION.txt.
+ Git ignores .env, var/, .tools/, target/, bin/, dist/, node_modules/.
-CHECKS AND LICENSES
+CHECK AND RELEASE
cargo install --locked --version 0.20.2 cargo-deny --root .tools
cargo install --locked --version 0.9.3 --features cli cargo-about --root .tools
cargo stack check
cargo stack release
- Check builds and checks TypeScript, then runs Rust formatting, Clippy, tests,
- rustdoc, cargo-deny, and cargo-about. npm development dependencies are pinned
- in package-lock.json. Typed ESLint, both compiler checks, and npm audit are
- included. See docs/LINTING.txt for policy choices and tested exceptions.
- THIRD-PARTY-LICENSES.html is generated from Cargo.lock and about.hbs.
- Human-readable license headings use GNU's names. SPDX identifiers and
- original third-party legal texts retain their exact spelling.
-
- Detailed manual backup, recovery, deployment, and host setup instructions
- are in docs/OPERATIONS.txt. Read docs/POLICY.txt before changing conventions.
-
-EXPERIMENTAL CHAT
-
- main includes the experimental Rust/WASI chat application. Start with
- docs/CHAT-APPLICATION.txt for setup, protected files, tests, backup/restore,
- source licensing, and security limitations. cargo stack chat --init appends
- chat configuration to the existing single root .env; cargo stack chat runs
- the direct local service. docs/CHAT-PRODUCTION.txt describes its packaged
- HTTPS gateway, release deployment, production owner setup, and recovery.
- Building a release does not activate production.
-
- SQLite snapshots use the bundled SQLite C library. Release scripts build
- static OpenSSL against hash-verified musl headers and the Rust musl target.
- Runtime binaries remain static; no compiler is required on the VPS.
+ Check: TypeScript build, both compilers, typed ESLint, npm audit; Rust fmt,
+ Clippy, tests, private rustdoc, cargo-deny, cargo-about. npm lock: package-lock.json.
+ Policy and tested exceptions: docs/LINTING.txt.
+ Cargo.lock + about.hbs generate THIRD-PARTY-LICENSES.html.
+ Display GNU license names; preserve exact SPDX identifiers and upstream legal text.
+
+ Build/test locally. Deploy prebuilt archives. VPS needs Podman, Git, GnuPG;
+ no Rust/Cargo/Node/npm/TypeScript. Backup, restore, host setup, deployment:
+ docs/OPERATIONS.txt. Building a release does not activate production.
+
+CHAT — EXPERIMENTAL
+
+ cargo stack chat --init
+ cargo stack chat
+
+ --init adds settings to existing root .env; chat starts direct local service.
+ Setup, protected files, tests, recovery, licensing, limits: docs/CHAT-APPLICATION.txt.
+ Packaged HTTPS, owner setup, recovery: docs/CHAT-PRODUCTION.txt.
+ SQLite snapshots use bundled SQLite C library. Static releases use OpenSSL,
+ hash-verified musl headers, Rust musl target. VPS needs no compiler.
diff --git a/crates/README.txt b/crates/README.txt
index 296f1a9..f724a28 100644
--- a/crates/README.txt
+++ b/crates/README.txt
@@ -15,10 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
RUST BACKENDS
- New first-party web backends belong in subdirectories here. Add their
- paths to the workspace members in the root Cargo.toml. Use edition 2024,
- AGPL-3.0-or-later, and workspace lints. Keep configured secrets in the one
- root .env and pass only the required keys to each service.
-
- The current stack has no blog or comment backend. This directory reserves
- a place for those features without adding an unused service or database.
+ New web backends belong here. Add paths to root Cargo.toml workspace members.
+ Use edition 2024, AGPL-3.0-or-later, workspace lints.
+ Keep configured secrets in one root .env. Pass each service only required keys.
+ No blog/comment backend or unused service/database exists.
diff --git a/crates/chat-crypto/README.txt b/crates/chat-crypto/README.txt
index 114544d..0fb26e7 100644
--- a/crates/chat-crypto/README.txt
+++ b/crates/chat-crypto/README.txt
@@ -13,34 +13,27 @@ 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/>.
-
PRIVATE CHAT CRYPTOGRAPHY
Fixed encryption: RFC 9980 ML-KEM-1024 + X448, OpenPGP v6.
Signing: ML-DSA-87 + Ed448, OpenPGP v6.
- Backend: Sequoia 2.4.2 with OpenSSL 3.6.5. No algorithm substitution or
- vulnerability-advisory exception was used. The original RustCrypto backend
- is historical and no longer selected.
-
- This crate implements purpose-specific identity creation, Argon2id/AES-256-GCM
- protected files, detached authentication proofs, signed two-recipient messages,
- bounded verified history batches, and the original compatibility probe.
- Native Rust and browser WASI share the implementation. The standalone command
- accepts one JSON request on stdin; empty input runs the compatibility probe.
- Its browser WASI host grants no filesystem or network access. Private request
- data must remain in local memory and must never be submitted to the server.
-
- See ../../docs/CHAT-APPLICATION.txt for implemented formats, local setup,
- tests, backups, and limitations. See ../../docs/CHAT-PRODUCTION.txt for
- packaged HTTPS integration. Work remains experimental;
- compatibility tests are not an application security audit.
-
- ../../docs/CHAT-OPENSSL-MIGRATION.txt records the complete backend decision,
- pinned source hashes, build flags, FFI/capability limitations, and reviewed
- dependency-policy exceptions. ../../docs/CHAT-SECURITY-REVIEW.txt records
- the superseded RustCrypto findings without advisory ignores.
-
-CHECKS FROM THE WORKSPACE ROOT
+ Sequoia 2.4.2 + OpenSSL 3.6.5. No algorithm substitution or advisory exception.
+ Superseded RustCrypto backend is not selected.
+
+ Provides purpose-specific identities, Argon2id/AES-256-GCM protected files,
+ detached authentication proofs, signed two-recipient messages, bounded
+ verified history batches, compatibility probe. Native Rust and browser WASI
+ share code. Standalone CLI: one JSON request on stdin; empty stdin runs probe.
+ Browser WASI has no filesystem/network access. Private requests stay in local
+ memory; never send them to server.
+
+ ../../docs/CHAT-APPLICATION.txt: formats, setup, tests, backups, limits.
+ ../../docs/CHAT-PRODUCTION.txt: packaged HTTPS.
+ ../../docs/CHAT-OPENSSL-MIGRATION.txt: backend decision, source hashes,
+ build flags, FFI/capability limits, reviewed dependency exceptions.
+ Experimental. Compatibility tests are not an application security audit.
+
+CHECK FROM WORKSPACE ROOT
cargo test --locked -p stack-chat-crypto
cargo run --locked -p stack-chat-crypto
@@ -48,9 +41,8 @@ CHECKS FROM THE WORKSPACE ROOT
PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto
cargo stack check
- Native builds require OpenSSL >=3.5 development files and libclang.
- The cross-build script supports an x86_64 GNU/Linux development host.
- Installed browser tooling is excluded from source archives and releases.
- The TypeScript tests and locked toolchain declarations are public.
- The probe uses public fixture credentials and creates temporary identities
- in memory. It does not open the root .env or use the canonical RSA key.
+ Native build: OpenSSL >=3.5 development files, libclang.
+ Cross-build host: x86_64 GNU/Linux. Browser tooling excluded from archives
+ and releases; TypeScript tests and locked toolchain declarations remain public.
+ Probe uses public fixture credentials and temporary in-memory identities.
+ It never opens root .env or uses canonical RSA key.
diff --git a/crates/chat-server/README.txt b/crates/chat-server/README.txt
index 0562385..1cd97d5 100644
--- a/crates/chat-server/README.txt
+++ b/crates/chat-server/README.txt
@@ -13,11 +13,11 @@ 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/>.
+PRIVATE CHAT SERVER — EXPERIMENTAL
-EXPERIMENTAL PRIVATE CHAT SERVER
+ cargo stack chat --init
+ cargo stack chat
- See docs/CHAT-APPLICATION.txt at the workspace root for local setup,
- file formats, API/security limits, backups, recovery, source licensing,
- and tests. Start with cargo stack chat --init, then cargo stack chat.
- See docs/CHAT-PRODUCTION.txt for the prebuilt Podman/HTTPS deployment,
- production owner setup, and full encrypted export/recovery procedure.
+ From workspace root: local setup, formats, API/security limits, backup,
+ recovery, licensing, tests: docs/CHAT-APPLICATION.txt.
+ Prebuilt Podman/HTTPS, production owner, encrypted export: docs/CHAT-PRODUCTION.txt.
diff --git a/crates/config/README.txt b/crates/config/README.txt
index 8648852..dccfbf4 100644
--- a/crates/config/README.txt
+++ b/crates/config/README.txt
@@ -13,8 +13,7 @@ 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/>.
-SHARED STACK CONFIGURATION
+SHARED CONFIGURATION
- Public defaults are documented in the root .env.example. Each installation
- overrides them in the single root .env alongside private settings. Rendering
- uses an explicit public allowlist. See docs/CONFIGURATION.txt for details.
+ Root .env.example: public defaults. Single root .env: overrides and secrets.
+ Rendering uses explicit public allowlist. See docs/CONFIGURATION.txt at root.
diff --git a/docs/CHAT-APPLICATION.txt b/docs/CHAT-APPLICATION.txt
index 7ebac77..388c677 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.
+ Available on main: direct development and prebuilt Podman/HTTPS deployment.
+ See CHAT-PRODUCTION.txt for release, setup, and recovery. Feature work does
+ not merge main or activate the VPS. Experimental; no independent messenger audit.
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/stack chat --owner-fingerprint and ./bin/stack chat --reset-owner in an
+ isolated maintenance container. First stop with ./bin/stack down; restart
+ with ./bin/stack up --production. No VPS Rust required. Read CHAT-PRODUCTION.txt
+ 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,27 @@ 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.
+ Bundled SQLite snapshots require a musl C compiler for portable CLI builds.
+ The root CLI ran as x86_64 static PIE; VPS SQLite packages are unnecessary.
+ Local checks compiled C with Alpine musl 1.2.6 headers/host compiler and
+ linked with Rust's self-contained musl runtime.
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 +363,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 +379,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
diff --git a/docs/CHAT-OPENSSL-MIGRATION.txt b/docs/CHAT-OPENSSL-MIGRATION.txt
index 8c9ee28..4bb87fb 100644
--- a/docs/CHAT-OPENSSL-MIGRATION.txt
+++ b/docs/CHAT-OPENSSL-MIGRATION.txt
@@ -16,28 +16,33 @@ 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.
+ Date: 2026-10-30. Historical compatibility/dependency-security experiment
+ using disposable identities. For the later messenger and packaged deployment,
+ see CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt.
DECISION AND FIXED ALGORITHMS
- Retain Sequoia OpenPGP 2.4.2 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.
+ Retain Sequoia OpenPGP 2.4.2, RFC 9980 ML-KEM-1024 + X448 encryption,
+ ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, and AES-256 content encryption.
+ Only the backend changes; algorithms and wire protocol stay fixed.
+ Probe assertions reject unexpected backends or key algorithms.
+
+ Previous Rust curve arithmetic reached cx448 0.1.1 -> crypto-bigint 0.5.5;
+ the backend also included rsa 0.9.10. Neither RustSec issue was ignored;
+ no mathematical implementation was patched locally.
+ RUSTSEC-2026-0387 affected required X448/Ed448 curve arithmetic, not unused RSA.
+ Its fix requires crypto-bigint >=0.7.6; cx448 0.1.1 requires incompatible 0.5.
+ A lockfile update alone could not fix it. No practical browser key-extraction
+ exploit was demonstrated; functional tests do not prove constant-time behavior.
+ The closed probe used no RSA private operations, but that did not establish
+ unreachability through hostile imports or future application interfaces.
+ https://rustsec.org/advisories/RUSTSEC-2026-0387.html
+ https://rustsec.org/advisories/RUSTSEC-2023-0071.html
+
+ Only crypto-openssl is enabled. allow-experimental-crypto and
+ allow-variable-time-crypto are removed. The resolved graph has no cx448,
+ crypto-bigint, or rsa crate. Removing those dependencies does not prove
+ absence of browser side channels or external C defects.
VERSIONS AND SOURCE PROVENANCE
@@ -55,36 +60,29 @@ VERSIONS AND SOURCE PROVENANCE
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.
+ Digests match upstream GitHub release asset metadata; scripts verify them
+ before extraction. This pins inputs, without independently verifying release
+ signatures or proving cross-host bit-for-bit builds. Archives, SDK, libraries,
+ and build products stay under ignored .tools/ and target/. The OS supplies
+ the native development library.
WHY WASI
- The 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.
+ The Rust proof used wasm32-unknown-unknown and wasm-bindgen glue. OpenSSL uses
+ a wasm32-wasip1 command and explicit browser WASI bridge, calling the native
+ tests' compatibility_probe. The browser loads this binary directly and
+ requires output identifying OpenSSL 3.6.5; stale Rust-backend artifacts cannot
+ count as OpenSSL evidence. Do not reuse historical browser-build commands.
BUILD CONFIGURATION AND BOUNDARIES
- infra/chat/openssl-wasi.conf adds a 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.
+ infra/chat/openssl-wasi.conf adds a BASE_unix target with little-endian,
+ 32-bit bignums; cryptographic routines are unchanged. C uses -O2. No rand.patch,
+ unconditional entropy-success patch, or custom RNG is applied. SHA-256 checks
+ of all 5889 original regular files against the downloaded archive found none
+ changed or missing after build; only the target configuration was added.
+ OpenSSL 3.6.5 supports WASI getentropy in
+ providers/implementations/rands/seeding/rand_unix.c.
Configure selects --with-rand-seed=getrandom. WASI libc's entropy request
reaches wasi_snapshot_preview1.random_get. The browser bridge services it
@@ -92,28 +90,23 @@ BUILD CONFIGURATION AND BOUNDARIES
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,
+ Static builds disable assembly, async operations, automatic config loading,
DSO/modules, POSIX I/O, secure-memory arenas, shared libraries, sockets,
- C stdio, thread pools, threads, and interactive console UI. 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.
+ C stdio, thread pools, threads, and interactive console UI to avoid unsupported
+ capabilities. Ordinary WASM linear memory provides no secure-erasure,
+ locked-page, or delivered-JS protection. Rust output remains captured.
+
+ _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply SDK process-ID emulation.
+ getuid/geteuid/getgid/getegid map to getpagesize, following common WASI port
+ configuration. The browser module has no real OS user IDs or privilege boundary.
+ These values supply neither randomness nor identity credentials. This portability
+ assumption does not emulate general POSIX security.
+
+ Bindings use SDK libclang/WASI sysroot, target wasm32-wasip1, and
+ -fvisibility=default; without explicit visibility, bindgen omitted declarations.
+ Rust's bundled WASI libc precedes SDK libraries; reversed order failed to link
+ __wasi_init_tp. SDK libraries remain available for emulated-getpid. These were
+ toolchain integration failures; generated cryptographic bindings were not edited.
BROWSER CAPABILITIES
@@ -123,11 +116,9 @@ BROWSER CAPABILITIES
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.
+ Denials prevent accidental host storage/network access, not malicious JS from
+ the same server. Memory, clocks, randomness, and bridge imports remain trusted.
+ The test closes Chromium after 120 seconds if execution stalls.
VERIFICATION
@@ -144,25 +135,20 @@ VERIFICATION
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.
+ The harness observed no external requests, cookies, localStorage,
+ sessionStorage, IndexedDB databases, or service-worker registrations; responses
+ carry Cache-Control: no-store. This concerns only the probe. Browsers/OSes can
+ retain memory, caches, crash dumps, swap, and restored state. No complete chat
+ session existed at this checkpoint. Native tests, WASI build, and browser
+ execution are separate gates; functional tests and scans are not cryptographic
+ or side-channel audits.
DEPENDENCY POLICY REVIEW
- 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.
+ RustSec reports no advisories for this resolved graph; no ignore was added.
+ Bans on wildcards, unknown Git/registry sources, and unreviewed duplicates remain.
+ cargo update cannot unify these upstream API generations. Exceptions match only
+ exact older versions; advisory/license checks still apply. Cargo.lock pins both sides:
block-buffer 0.10.4 vs 0.12.1
crypto-common 0.1.7 vs 0.2.2
@@ -196,23 +182,20 @@ DEPENDENCY POLICY REVIEW
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.
+ The only new license allowance is ISC for bindgen/clang's libloading.
+ GNU-compatible licenses retain attribution; cargo-about covers the workspace.
+ OpenSSL C and SDK are outside Cargo. Distributed browser artifacts must carry
+ their original licenses/sources as required. OpenSSL LICENSE.txt is Apache-2.0.
+ Rust notices alone do not cover external C/toolchain distribution obligations.
EXTERNAL LIBRARY MAINTENANCE
- cargo-deny does not audit the externally built OpenSSL C archive or SDK.
- Review OpenSSL's release/advisory pages separately and rebuild when needed:
+ cargo-deny excludes external OpenSSL C and SDK. Review separately; rebuild as needed:
https://openssl-library.org/news/timeline/
https://openssl-library.org/news/vulnerabilities-3.6/
- 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.
+ Tested OpenSSL 3.6.5 is the 2026-09-29 security-patch release. A Rust audit
+ does not guarantee this pinned C version's future security. Document a supported
+ update policy before production deployment.
REPRODUCE FROM THE STACK ROOT
@@ -229,12 +212,10 @@ REPRODUCE FROM THE STACK ROOT
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.
+ The script pins versions/hashes, builds static OpenSSL under .tools/openssl-wasi,
+ builds the Rust WASI command, and prints its SHA-256. It uses no unverified
+ precompiled OpenSSL. Extracted sources may be reused; independent verification
+ needs a fresh ignored tools/build tree. Downloads/test artifacts are not secrets.
Build logs and complete dependency trees are local ignored diagnostics in
.tools/chat-openssl-*.log and .tools/chat-openssl-duplicates.txt. The native
@@ -246,9 +227,8 @@ REMAINING WORK
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.
+ This checkpoint removed the Rust-backend blocker and demonstrated fixed
+ algorithms in-browser. It was not a complete messenger, OpenSSL/WASI/JIT timing
+ audit, LibreJS certification, or proof against malicious delivery. File format,
+ authentication, server/database, quotas, hostile-input handling, pagination/counts,
+ recovery, session lifecycle, and browser-state tests remained to implement.
diff --git a/docs/CHAT-PRODUCTION.txt b/docs/CHAT-PRODUCTION.txt
index 03f5ba4..fdb48a6 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.
+ Implemented on main. 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-OPENSSL-MIGRATION.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
@@ -98,36 +85,31 @@ LOCAL MODES AND ORIGIN BINDING
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. Extract the verified release's new management binary
+ into var/incoming/bootstrap; invoke with --root set to the current installation.
+ The new binary understands its release schema, verifies the archive, and
+ installs chat artifacts before startup.
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 down on stop. Podman restarts
+ failed containers. Complete matching-architecture releases need no compilation
+ or registry pull. Check all four services after activation:
./bin/stack status
@@ -136,57 +118,48 @@ DEPLOY AND CREATE THE REAL OWNER FILE
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.
+ 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
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.
+ 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.
- 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:
+ 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/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.
+ 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
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..f0f8457 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
- 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/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..6f5e13d
--- /dev/null
+++ b/docs/MAP.txt
@@ -0,0 +1,175 @@
+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, including this map.
+ 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-OPENSSL-MIGRATION.txt OpenSSL crypto migration evidence, dependencies, reviewed exceptions.
+| |-- CHAT-PRODUCTION.txt Chat release packaging, HTTPS activation, owner setup, production recovery.
+| |-- CONFIGURATION.txt Supported root settings, validation, public/private state boundaries.
+| |-- 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.
+| |-- 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 and validated directory 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 and hashes.
+| | |-- images.rs Export/import exact container images; enforce 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 and packaged chat assets.
+ 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.
+ 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.
diff --git a/docs/OPERATIONS.txt b/docs/OPERATIONS.txt
index 2c9fa53..55dc81c 100644
--- a/docs/OPERATIONS.txt
+++ b/docs/OPERATIONS.txt
@@ -13,113 +13,85 @@ 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. Develop on local Linux; production: ops@23.155.44.67.
+ Public source: https://git.doan.luuduct.ai/stack.
+ GNU Affero General Public License, version 3 or later.
-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.
+ Full --include-images encrypted export: public source, compiled CLI, .env,
+ persistent data, certificates, bare repos, working Git history if present,
+ exact service images, checksum manifest. No old server filesystem needed.
+ Excludes engine storage, caches, old exports, host packages/SSH keys/firewall,
+ disks, OS configuration. Recreate host setup separately.
+ Public Git alone cannot recover .env, live data, TLS keys, or var/ repos.
- 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.
+ Decryption needs GPG private key + passphrase. Keep key export/revocation
+ certificate in KeePassXC; keep independent database copy. Server holds public
+ key only. Never send private key/password through chat.
+ Default recipient: 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6.
- The public Git repository alone is not a backup: it excludes .env, live
- data, private certificates, and application repositories under var/.
+DEVELOP LOCAL
- 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:
- 3346AA3150F8F16828BB0D14F8C66DF77AFECEA6
-
-DAILY DEVELOPMENT
-
- Run these from ~/stack on your workstation:
+ From ~/stack on workstation:
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:
+ Open http://localhost:8080, http://localhost:8080/git/,
+ http://localhost:8080/search/. Alternate port:
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.
-
- 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.
+ Local mode: loopback only, local links, unchanged public DNS, no public TLS.
+ Missing .env: create with random SearXNG secret. Edit secrets there; never commit.
+ infra/: service settings. web/: content. crates/: new backends; add workspace members.
+ infra/services.toml declares services. Each gets only listed environment keys.
- 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:
+ Project engine storage: var/podman/. Bare 'podman ps' uses another store.
+ Inspect project containers from ~/stack:
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.
+ Keep both global options for 'logs CONTAINER', 'exec CONTAINER', 'images'.
+ cargo stack status gives names. Never migrate var/podman/; export images.
-STRICT LOCAL CHECKS
+CHECK LOCAL
- Install the two development-only tools once:
+ Install development audit tools once:
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
- 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.
+ Checks formatting, all workspace Clippy targets/features, tests, private
+ rustdoc, advisories, duplicate versions, sources, licenses. cargo-about
+ regenerates THIRD-PARTY-LICENSES.html. Format with cargo fmt --all.
+ Dependency changes: update Cargo.lock deliberately; run full check; commit
+ lockfile and license page. Never disable audit failures to ship.
-EXPORT AND BACK UP PRODUCTION
+BACK UP PRODUCTION
- On the VPS:
+ On VPS:
cd ~/stack
./bin/stack 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. Snapshots include
+ chat and chat-local; exclude raw journals and server locks.
+ Verifies public recipient fingerprint, checks bare repos, saves exact images:
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:
+ Restrictive permissions. Removes temporary plaintext on success/handled errors.
+ No scheduled backup. Choose retention and off-host copies yourself.
+ On workstation, copy actual filenames printed above:
umask 077
mkdir -p ~/stack/var/backups/production
@@ -128,43 +100,31 @@ 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.
-
- Once copied and verified, delete only the corresponding VPS export files
- if desired. Do not delete var/data or .env.
+ Replace TIMESTAMP. Checksum detects corruption; keep trusted export + checksum.
+ Keep another copy on separate device/location. After verification, optionally
+ delete matching VPS export files only. Never delete var/data or .env.
+ ./bin/stack backup without images needs registry/build access for local startup;
+ not standalone production recovery. Prefer --include-images.
- 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.
+RESTORE ON LINUX
-RESTORE ON ANOTHER LINUX MACHINE
-
- 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).
-
- Copy the encrypted export and its checksum to the new machine. Verify
- the checksum first. Import your secret-key backup if not already present:
+ Host tools: Podman, Git, GnuPG, rootless networking prerequisites.
+ Target user needs >=65536 subordinate UIDs/GIDs. Use distro-supported Podman.
+ Prebuilt binary/images: x86_64 Linux (linux/amd64).
+ Copy export + checksum; verify checksum first. If needed, import key:
gpg --import private-key-backup.asc
- GnuPG asks for the key passphrase through its normal local interface.
- Never pass it on the command line or put it in .env.
-
- If you already have a compatible stack command, restore with it:
+ GnuPG prompts locally. Never pass passphrase through arguments or .env.
+ Existing compatible CLI:
./bin/stack restore /path/to/stack-TIMESTAMP.tar.gz.gpg ~/stack-restored
- 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.
+ Destination must not exist. Restore decrypts privately; rejects unsafe paths
+ and links; checks every manifest entry; assigns unique project identity;
+ defaults local; starts nothing; never overwrites existing installation.
- 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:
+ No CLI/toolchain yet: bootstrap trusted export with GnuPG/tar. Bash/fish:
umask 077
mkdir -p ~/stack-bootstrap
@@ -172,125 +132,98 @@ RESTORE ON ANOTHER LINUX MACHINE
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:
+ Bootstrap executes extracted code: use only your trusted export.
+ Rust restore validates full archive. After success, delete ~/stack-bootstrap:
+ it holds decrypted archive. Retain encrypted backup.
+ Test restored stack locally first:
cd ~/stack
./bin/stack up --local --port 8080
./bin/stack status
- 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.
+ Saved images load automatically; image lock verifies identities. Matching
+ architecture needs no registry pull/build. Check homepage, Git, search, WKD.
+ ./bin/stack down before production setup.
+ Same restore accepts release archives: source/binary/images, no .env/live data.
+ Starting restored release creates fresh state; cannot recover lost data.
- 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.
+NEW PRODUCTION HOST
-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:
+ Restore into target user's ~/stack. Debian: review infra/host-setup.sh;
+ replace 'ops' if needed; 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:
+ Installs Podman/network tools, checks existing subordinate ranges, enables
+ user linger, 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.
+ System-wide rootless ports >=80; ports <80 stay privileged. SSH/firewall unchanged.
+ Missing ranges: allocate unused ranges through distro account tools; rerun.
+ Other distros: equivalent packages, sysctl, linger. Supplied boot unit needs
+ systemd user manager.
- Start production:
+ Host/provider firewall: allow 22/tcp, 80/tcp, 443/tcp, 443/udp for applicable
+ IPv4/IPv6. Never publish backend container ports. Preserve SSH; verify address
+ before changing DNS. A: new IPv4. AAAA: working new IPv6 only; remove stale AAAA.
+ Hosts live in root .env; edit and reapply rendered settings. infra/Caddyfile
+ is public template. Keys/render-only commands: CONFIGURATION.txt.
+ Preserve www aliases and openpgpkey names pointing to canonical hosts.
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:
+ Production binds 80/tcp, 443/tcp, 443/udp. Caddy issues/renews certificates,
+ redirects HTTP to HTTPS and www to canonical URLs. State: var/data/caddy/.
+ Public issuance needs working public DNS/connectivity.
+ Enable boot startup:
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.
+ Unit assumes ~/stack; edit paths for other location. Uses bin/stack.
+ Linger keeps user service alive
+ without login. Containers use unless-stopped restart policy.
- If offering SSH Git access at the former VPS path, create this optional
- host symlink as the administrator:
+ Optional former VPS SSH Git path; administrator only:
sudo install -d -m 755 /srv/git
sudo ln -s /home/ops/stack/var/data/repos/stack /srv/git/stack
- 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.
+ Never overwrite existing repo. Current VPS already has this layout link.
+ Public HTTPS Git needs no host symlink.
-MAKE AND DEPLOY A RELEASE
+BUILD AND DEPLOY
- Develop and run the checks on your workstation, then:
+ Develop/check on workstation:
cd ~/stack
cargo stack 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.
-
- 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:
+ Release reruns full check; builds static Rust CLI/chat + browser WASM;
+ packages matching source; builds cgit/chat containers; saves configured images.
+ Output: dist/stack-release-TIMESTAMP.tar.gz + .sha256.
+ Excludes .env, live data, .git history, previous backups.
+ Restore into fresh local test directory; up --local on free port; inspect
+ four services; stop test. Commit/push reviewed public changes:
git add -A
git diff --cached --stat
- git commit -m 'Describe the change'
+ git commit -S3346AA3150F8F16828BB0D14F8C66DF77AFECEA6 -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.
-
- Copy the actual archive and checksum to production:
+ Review staged files. .gitignore excludes secrets/runtime/artifacts.
+ Use RSA-2048 signed commits; author/key: POLICY.txt.
+ Deploy archive, never git pull. Copy actual filenames:
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/
- On the VPS:
+ On VPS:
cd ~/stack/var/incoming
sha256sum -c stack-release-TIMESTAMP.tar.gz.sha256
@@ -301,49 +234,32 @@ MAKE AND DEPLOY A RELEASE
./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.
+ Use verified NEW release binary: old binary may reject new service schema.
+ Activation verifies archive/platform, replaces public source/binary, loads
+ exact images, preserves .env/var/data, replaces running containers. No compiler,
+ Rust build, image build, or private-config overwrite. Expect brief interruption.
+ Take verified export before changes needing recovery. Verify production;
+ optionally delete corresponding incoming archive/checksum.
+
+RECOVERY LIMITS; ROTATE KEYS
+
+ Application export is not bootable VPS image. Accounts, SSH, firewall,
+ packages remain host duties. Other CPU architecture needs native release.
+ Registry needed only without saved images or when rebuilding another target.
+ State: files, Git repos, Caddy, encrypted SQLite chat. SQLite uses online backup.
+ Recovery proof: reopen BOTH participants' unchanged identities; decrypt history.
+ See CHAT-PRODUCTION.txt. Future databases need supported snapshot/recovery.
+
+ New backup recipient: publish public key, update default fingerprint, test
+ encryption/restore. Single export: --recipient FULL_FINGERPRINT --key /path/public-key.asc.
+ Retain old secret keys while any retained export needs them.
+ Restore accepts verified format-2 application archives.
+
+LICENSE PAGE; REDIRECTS
+
+ cargo-about generates web/THIRD-PARTY-LICENSES.html. Caddy serves canonical
+ https://doan.luuduct.ai/THIRD-PARTY-LICENSES.html; other deployments use SITE_HOST.
+ Website/chat links share page; local preview uses local main-site origin.
+ cgit Source/Raw expose generated file; chat /THIRD-PARTY-LICENSES.html redirects
+ to canonical page. Source exports retain offline license texts.
+ Homepage /message and /message/ redirect to current chat origin.
diff --git a/docs/POLICY.txt b/docs/POLICY.txt
index 4c6c6d3..dea595b 100644
--- a/docs/POLICY.txt
+++ b/docs/POLICY.txt
@@ -17,89 +17,80 @@ 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.
+
+ 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-OPENSSL-MIGRATION.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.
- 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.