diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/CHAT-APPLICATION.txt | 403 | ||||
| -rw-r--r-- | docs/CHAT-OPENSSL-MIGRATION.txt | 234 | ||||
| -rw-r--r-- | docs/CHAT-PRODUCTION.txt | 222 | ||||
| -rw-r--r-- | docs/CONFIGURATION.txt | 143 | ||||
| -rw-r--r-- | docs/LINTING.txt | 167 | ||||
| -rw-r--r-- | docs/MAP.txt | 175 | ||||
| -rw-r--r-- | docs/OPERATIONS.txt | 347 | ||||
| -rw-r--r-- | docs/POLICY.txt | 121 | ||||
| -rw-r--r-- | docs/TYPESCRIPT.txt | 125 |
9 files changed, 1680 insertions, 257 deletions
diff --git a/docs/CHAT-APPLICATION.txt b/docs/CHAT-APPLICATION.txt new file mode 100644 index 0000000..388c677 --- /dev/null +++ b/docs/CHAT-APPLICATION.txt @@ -0,0 +1,403 @@ +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/>. + + +EXPERIMENTAL PRIVATE CHAT + + Available on main: direct development and prebuilt Podman/HTTPS deployment. + See CHAT-PRODUCTION.txt for release, setup, and recovery. Feature work does + not merge main or activate the VPS. Experimental; no independent messenger audit. + +WHAT EXISTS + + 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 + conversation total, or page backwards for older messages. Counts refresh when + the history input is focused, history is requested, or a send is acknowledged. + Large selections are fetched and verified in batches of at most 50; that is + a per-request resource bound, not a user-visible 50-message selection limit. + Reload, lock, or leaving clears the application's in-memory references and + visible messages. A separate authenticated session expires after 30 minutes. + There is no automatic background polling, notification, or remote logout. + Refresh the owner inbox and load history to fetch newly arrived messages. + +LOCAL PREREQUISITES AND COMMANDS + + 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: + + sh infra/chat/build-openssl-wasi.sh + cargo stack licenses + sh infra/chat/build-source.sh + cargo stack chat --init + + 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 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-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 + + 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: + + cargo stack chat --owner-fingerprint + + Stop the chat server with Control-C in its terminal, then explicitly pass + that full fingerprint to the destructive reset command: + + cargo stack chat --reset-owner PASTE_CURRENT_OWNER_FINGERPRINT_HERE + + 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 + + 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. + + 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 + + OpenPGP v6, ML-KEM-1024 + X448 encryption, ML-DSA-87 + Ed448 signatures, + and AES-256 content encryption. There is no RSA, classical-only, or alternative + post-quantum fallback. Each certificate has exactly the expected two keys and + 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 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 + + Existing compact v1 files with one ciphertext string remain supported: + + stack-chat-file/v1;argon2id-m65536-t3-p1;aes256-gcm + + 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 + are generated for each file; the authentication tag is 16 bytes. These KDF + parameters are fixed by the format, not accepted from an untrusted file. + The outer JSON contains only format, salt, nonce, ciphertext, and tag, encoded + in base64 where applicable. The encrypted payload contains the origin, name, + pinned owner public certificate, and the secret OpenPGP certificate. + Secret key material inside that already encrypted envelope is unprotected + only in process memory. Never extract or save the decrypted payload. + + Passwords must be 12 to 1024 UTF-8 bytes. This is a length floor, not a claim + of sufficient entropy: choose a strong unique passphrase. File parsing and + parameter checks precede the expensive KDF. Import is limited to 256 KiB. + Wrong passwords, malformed files, authentication failures, and unsupported + formats return a generic error. Mutable KDF keys, plaintext envelopes, and + private serialization buffers are zeroized where practical. + + 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, + sender fingerprint, CSPRNG message ID, and UTF-8 text. Receivers compare the verified signer fingerprint to the conversation + record and expected sender, then verify every payload binding before displaying text. Encrypted profile + objects bind kind=profile, origin, conversation, sender, and display name. + The Rust encryptor also enforces the pinned recipient, sender, origin, and + conversation before signing; the server cannot select an arbitrary recipient + by changing a public-key field. Display names remain labels, not proof of a + unique person. The interface shows full conversation fingerprints. + Message and profile formats are distinct to prevent cross-purpose replay. + Compression, unsigned messages, an unexpected content cipher, and malformed + recipient sets are rejected. Private certificates are never sent to the API. + +AUTHENTICATION AND STORAGE + + One JSON API endpoint /api accepts same-origin POST requests only; no CORS, + cookie authentication, URL credentials, or URL conversation identifiers. + A fresh random challenge binds protocol, exact origin, full fingerprint, + random nonce, an expiry, and the pinned owner fingerprint. It expires after 60 seconds and is consumed on + the first verification attempt, including a failed signature attempt. + A detached purpose-specific signature proves possession of the identity. + A random bearer session token then authorizes API requests for 30 minutes. + Session tokens and pending challenges exist only in server and page memory. + The owner can access the inbox and any conversation; a visitor can access + only the conversation indexed by their own verified fingerprint. + + Data lives under var/data/chat/chat.sqlite, in a private 0700 directory with + a 0600 database. SQLite uses foreign keys, FULL synchronous transactions, and + DELETE journaling. Durable data consists of the owner public certificate, + visitor public certificates, an encrypted name/profile, encrypted messages, + sender fingerprints, random message IDs, sequence numbers, and counts derived + from rows. Names and message text are not stored as plaintext. There are no + application access logs or deliberately retained client IP addresses. + SQLite journals contain the same public metadata and ciphertext, not private + keys or message plaintext. OS diagnostics and a future reverse proxy need + their own logging review before deployment. + + 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 + identities, 10000 messages per conversation, and a one-billion-character + total message-ciphertext budget. At most 60 authentication challenges per + global minute window, 128 pending challenges, 256 active sessions, and four + queued cryptographic/database tasks are admitted. API bodies are at most + 256 KiB. Database access is serialized. These are basic bounds, not robust + protection against determined traffic, account creation, CPU, or disk attacks. + Public-key and profile storage, indexes, journals, and backups add overhead to + the ciphertext budget. Monitor actual disk use. No public abuse controls, + deletion UI, in-place key rotation UI, blocklist, or password-change UI are + implemented. Lost-owner recovery uses the explicit backend reset above. + +BROWSER STATE AND LIMITATIONS + + No application cookies, localStorage, sessionStorage, IndexedDB, service + workers, analytics, external scripts, fonts, or third-party requests. Assets + and API responses use no-store. Browser fetches omit credentials. Private + file/password bytes travel only to a local worker and Rust/WASM memory. + Each crypto worker terminates after its operation or a timeout. Lock and + pagehide terminate remaining workers, clear drafts/messages/credentials, + clear input controls, and revoke download URLs. Restored pages clear state. + + This means no intentional persistent application state, not literally zero + traces. Browser password managers, downloads, history, caches outside the + application's control, memory copies, swap, extensions, OS crash dumps, + screenshots, and a restored process can retain data. JavaScript strings, + garbage collection, compiler/library copies, and WASM memory cannot be + guaranteed erased. A 90-second worker timeout and 256 MiB WASM linear-memory maximum bound + each cryptographic instance. Browser/host copies add memory overhead. A malformed file or message can still expose defects in + the Rust, C, WASM, or browser stack. OpenSSL uses C/FFI; Rust's unsafe-code + prohibition does not make dependencies memory-safe. Native server and WASI + OpenSSL updates must both be reviewed and rebuilt when advisories warrant. + + A stolen file enables offline password guessing. A stolen file and password, + or an unlocked identity, enable impersonation and decryption of accessible + historical ciphertext. There is no username recovery, forward secrecy, or + automatic key ratchet. Losing the owner file/password loses owner access to + every conversation encrypted to it. A server rollback can omit messages; + signatures do not prove a complete transcript. Encrypted messages reveal + metadata, lengths, ordering, and interaction timing. The server sees client + addresses during connections. Bearer-token theft permits API use until + expiry or lock, though decrypting or signing still requires the private key. + + 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 + + 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; 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 + var/data/chat/chat.sqlite under the destination root, set directory mode 0700 + and database mode 0600, restore the single root .env privately, and rebuild + the corresponding source/WASM on the development machine. Start locally and + open the unchanged owner or visitor file with its password. Changing the + 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 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 + + 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 + + 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 + previous local license-page URL redirects there. cgit keeps ordinary source + and raw-file views without a custom HTML renderer. + + First-party software is GNU AGPL version 3 or later. Legal license headers + are the exception to the no-comments convention. Documentation is plaintext. + JavaScript has GNU LibreJS @license/@license-end markers and a linked + jslicense-labels1 table, following GNU's JavaScript license web-label format: + + https://www.gnu.org/licenses/javascript-labels.html + https://www.gnu.org/software/librejs/free-your-javascript.html + + The yellow GNU LibreJS-compliant badge links to the GNU project and exposes + a hover/focus explanation distinguishing software freedom from security. + A source archive is built by infra/chat/build-source.sh from an explicit + source allowlist, TypeScript sources/configurations/npm lock, Cargo-vendored + dependencies, and the original verified + OpenSSL archive. It excludes .env, .git, keys, target files, runtime data, + downloaded browser tools, and exports. .env.example is public. The original + dependency license texts are preserved. GNU-friendly human-readable names + such as Expat License are used in the generated third-party license page; + machine-readable SPDX identifiers remain unchanged. + + Rebuild the source archive after any source, lockfile, or notice changes. + Do not publish a binary without its exact corresponding sources. Inspect + tar -tzf var/build/chat-source.tar.gz and the generated license page. + The SQLite bundled C source is provided by libsqlite3-sys in the vendor tree. + The complete unmodified OpenSSL release is included separately. + + Run the normal strict workspace check, then browser tests: + + cargo stack check + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:chat + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:progress + + 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 + workflow passes while an unlicensed external control script is blocked. Set + CHAT_LIBREJS=1 when running the application browser test to reproduce this + variant; it needs that signed extension at .tools/librejs.xpi and Firefox + downloaded into .tools/browsers. The extension download used was: + + https://addons.mozilla.org/firefox/downloads/file/4169076/librejs-7.21.1.xpi + + 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 + cryptography, protected file downloads and reopen, incorrect passwords, + signed sends/replies, safe text display, counts, zero-history, loading all + 54 fixture messages through 50+4 batches, pagination, + lost-response retry deduplication, reload, explicit lock, origin rejection, + live database snapshot restoration with both original identity files, owner + setup/access controls, refusing reset while running or with a mismatched + fingerprint, successful backend reset and fresh registration, rejecting old + visitor key pins, in-flight lock, back/forward restoration, and checks for + plaintext persistence in the database/journals or private credential uploads. + The packaged variant additionally verifies certificate trust/hostname through + curl, restrictive export permissions, and full encrypted recovery with saved + images and no build directories inside the recovered installation. + Native tests cover authorization, challenge reuse, expiry, locked sessions, + quotas, idempotency, and SQLite snapshot recovery. The implemented design and + limits are documented here. CHAT-OPENSSL-MIGRATION.txt records the backend review. diff --git a/docs/CHAT-OPENSSL-MIGRATION.txt b/docs/CHAT-OPENSSL-MIGRATION.txt new file mode 100644 index 0000000..4bb87fb --- /dev/null +++ b/docs/CHAT-OPENSSL-MIGRATION.txt @@ -0,0 +1,234 @@ +Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + + +OPENSSL BACKEND MIGRATION RECORD + + Date: 2026-10-30. Historical compatibility/dependency-security experiment + using disposable identities. For the later messenger and packaged deployment, + see CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt. + +DECISION AND FIXED ALGORITHMS + + Retain Sequoia OpenPGP 2.4.2, RFC 9980 ML-KEM-1024 + X448 encryption, + ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, and AES-256 content encryption. + Only the backend changes; algorithms and wire protocol stay fixed. + Probe assertions reject unexpected backends or key algorithms. + + Previous Rust curve arithmetic reached cx448 0.1.1 -> crypto-bigint 0.5.5; + the backend also included rsa 0.9.10. Neither RustSec issue was ignored; + no mathematical implementation was patched locally. + RUSTSEC-2026-0387 affected required X448/Ed448 curve arithmetic, not unused RSA. + Its fix requires crypto-bigint >=0.7.6; cx448 0.1.1 requires incompatible 0.5. + A lockfile update alone could not fix it. No practical browser key-extraction + exploit was demonstrated; functional tests do not prove constant-time behavior. + The closed probe used no RSA private operations, but that did not establish + unreachability through hostile imports or future application interfaces. + https://rustsec.org/advisories/RUSTSEC-2026-0387.html + https://rustsec.org/advisories/RUSTSEC-2023-0071.html + + Only crypto-openssl is enabled. allow-experimental-crypto and + allow-variable-time-crypto are removed. The resolved graph has no cx448, + crypto-bigint, or rsa crate. Removing those dependencies does not prove + absence of browser side channels or external C defects. + +VERSIONS AND SOURCE PROVENANCE + + Rust toolchain: 1.99.1, pinned in rust-toolchain.toml. + Sequoia OpenPGP: 2.4.2, exact requirement and Cargo.lock. + OpenSSL: 3.6.5, native host library and source-built static WASI libraries. + WASI SDK: 34.0 x86_64 Linux, bundled Clang/libclang 23.1.0-wasi-sdk. + Browser test runner: Playwright 1.64.1, Chromium 156.0.8078.4. + + SDK archive: + https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz + SHA-256 b761e3a0721dbae9c09a0059e5fdb2bf917d1b4a8a7b430fb3b5aafb0984b2c4 + + OpenSSL archive: + https://github.com/openssl/openssl/releases/download/openssl-3.6.5/openssl-3.6.5.tar.gz + SHA-256 a2157c2830efdec3788939b00c9b0638306d3f0bbb76dc4832ee503bb397df98 + + Digests match upstream GitHub release asset metadata; scripts verify them + before extraction. This pins inputs, without independently verifying release + signatures or proving cross-host bit-for-bit builds. Archives, SDK, libraries, + and build products stay under ignored .tools/ and target/. The OS supplies + the native development library. + +WHY WASI + + The Rust proof used wasm32-unknown-unknown and wasm-bindgen glue. OpenSSL uses + a wasm32-wasip1 command and explicit browser WASI bridge, calling the native + tests' compatibility_probe. The browser loads this binary directly and + requires output identifying OpenSSL 3.6.5; stale Rust-backend artifacts cannot + count as OpenSSL evidence. Do not reuse historical browser-build commands. + +BUILD CONFIGURATION AND BOUNDARIES + + infra/chat/openssl-wasi.conf adds a BASE_unix target with little-endian, + 32-bit bignums; cryptographic routines are unchanged. C uses -O2. No rand.patch, + unconditional entropy-success patch, or custom RNG is applied. SHA-256 checks + of all 5889 original regular files against the downloaded archive found none + changed or missing after build; only the target configuration was added. + OpenSSL 3.6.5 supports WASI getentropy in + providers/implementations/rands/seeding/rand_unix.c. + + Configure selects --with-rand-seed=getrandom. WASI libc's entropy request + reaches wasi_snapshot_preview1.random_get. The browser bridge services it + exclusively through crypto.getRandomValues, splitting requests at 65536 + bytes. There is no Math.random, fixed seed, or success-on-failure fallback. + The negative test replaces that API with a failure and requires rejection. + + Static builds disable assembly, async operations, automatic config loading, + DSO/modules, POSIX I/O, secure-memory arenas, shared libraries, sockets, + C stdio, thread pools, threads, and interactive console UI to avoid unsupported + capabilities. Ordinary WASM linear memory provides no secure-erasure, + locked-page, or delivered-JS protection. Rust output remains captured. + + _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply SDK process-ID emulation. + getuid/geteuid/getgid/getegid map to getpagesize, following common WASI port + configuration. The browser module has no real OS user IDs or privilege boundary. + These values supply neither randomness nor identity credentials. This portability + assumption does not emulate general POSIX security. + + Bindings use SDK libclang/WASI sysroot, target wasm32-wasip1, and + -fvisibility=default; without explicit visibility, bindgen omitted declarations. + Rust's bundled WASI libc precedes SDK libraries; reversed order failed to link + __wasi_init_tp. SDK libraries remain available for emulated-getpid. These were + toolchain integration failures; generated cryptographic bindings were not edited. + +BROWSER CAPABILITIES + + The WASI bridge supplies clocks, browser CSPRNG, empty arguments/environment, + EOF on stdin, and bounded in-memory stdout/stderr for this closed test. + It supplies no real files, preopened directories, sockets, or process launch. + Listed filesystem/network operations return NOTCAPABLE or BADF. Unknown + imports fail before instantiation. Standard output is capped at 65536 chars. + + Denials prevent accidental host storage/network access, not malicious JS from + the same server. Memory, clocks, randomness, and bridge imports remain trusted. + The test closes Chromium after 120 seconds if execution stalls. + +VERIFICATION + + Native: the same probe passes using OpenSSL 3.6.5. It checks v6 generation, + exact algorithm selection, protected-secret-key serialization/import, + signed UTF-8 encryption to both participants, decryption by each, wrong + password rejection, unexpected signer rejection, and ciphertext tampering. + + Browser: actual Chromium executes the OpenSSL WASI binary and passes the + same checks, then repeats with a fresh module instance. One observed normal + execution requested 1792 bytes across 72 browser CSPRNG calls. These counts + are observations, not cryptographic requirements or entropy estimates. + Removing secure randomness causes failure rather than a successful result. + A modified module requesting an unknown host import is also rejected before + instantiation. These negative tests require the expected failure reason. + + The harness observed no external requests, cookies, localStorage, + sessionStorage, IndexedDB databases, or service-worker registrations; responses + carry Cache-Control: no-store. This concerns only the probe. Browsers/OSes can + retain memory, caches, crash dumps, swap, and restored state. No complete chat + session existed at this checkpoint. Native tests, WASI build, and browser + execution are separate gates; functional tests and scans are not cryptographic + or side-channel audits. + +DEPENDENCY POLICY REVIEW + + RustSec reports no advisories for this resolved graph; no ignore was added. + Bans on wildcards, unknown Git/registry sources, and unreviewed duplicates remain. + cargo update cannot unify these upstream API generations. Exceptions match only + exact older versions; advisory/license checks still apply. Cargo.lock pins both sides: + + block-buffer 0.10.4 vs 0.12.1 + crypto-common 0.1.7 vs 0.2.2 + digest 0.10.7 vs 0.11.3 + Sequoia's Argon2 0.5.3 -> BLAKE2 0.10 uses the older digest API. + Root SHA-256 and the parser generator's SHA3 use the newer API. + + cpufeatures 0.2.17 vs 0.3.1 + Argon2 uses 0.2; root SHA-256 and parser-generator SHA3 use 0.3. + + generic-array 0.14.7 vs 1.4.5 + Argon2/BLAKE2 digest uses 0.14; SHA1 collision detection uses 1.x. + + getrandom 0.2.17 vs 0.4.3 + Sequoia's unknown-unknown WASM target dependencies declare 0.2; + the root CLI's tempfile uses 0.4. The current browser target is WASI, + whose OpenSSL entropy source is the bridge described above. The + all-target graph is still audited rather than excluding that edge. + + hashbrown 0.15.5 vs 0.17.1 + petgraph uses 0.15 directly and indexmap 2.14 uses 0.17. Both are + brought in by the parser generator at build time. + + itertools 0.13.0 vs 0.15.0 + shlex 1.3.0 vs 2.0.1 + bindgen uses older APIs; lalrpop and cc require the newer ones. + These are build dependencies, not a justification to skip auditing. + + syn 2.0.119 vs 3.0.9 + bindgen/prettyplease and Windows support use syn 2. Root derive + macros and other dependencies use syn 3. Changing that would require + upstream migration or unrelated dependency downgrades. + + The only new license allowance is ISC for bindgen/clang's libloading. + GNU-compatible licenses retain attribution; cargo-about covers the workspace. + OpenSSL C and SDK are outside Cargo. Distributed browser artifacts must carry + their original licenses/sources as required. OpenSSL LICENSE.txt is Apache-2.0. + Rust notices alone do not cover external C/toolchain distribution obligations. + +EXTERNAL LIBRARY MAINTENANCE + + cargo-deny excludes external OpenSSL C and SDK. Review separately; rebuild as needed: + https://openssl-library.org/news/timeline/ + https://openssl-library.org/news/vulnerabilities-3.6/ + Tested OpenSSL 3.6.5 is the 2026-09-29 security-patch release. A Rust audit + does not guarantee this pinned C version's future security. Document a supported + update policy before production deployment. + +REPRODUCE FROM THE STACK ROOT + + Native prerequisites: OpenSSL >=3.5 development headers/library, libclang, + pkg-config, and the pinned Rust toolchain. For the cross build also provide + a Linux x86_64 build host, Perl, make, curl, tar, and sha256sum. No root-owned + packages or system settings are changed by the cross-build script. + + cargo test --locked -p stack-chat-crypto + cargo run --locked -p stack-chat-crypto + sh infra/chat/build-openssl-wasi.sh + npm run build + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" node_modules/.bin/playwright install chromium + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto + cargo stack check + + The script pins versions/hashes, builds static OpenSSL under .tools/openssl-wasi, + builds the Rust WASI command, and prints its SHA-256. It uses no unverified + precompiled OpenSSL. Extracted sources may be reused; independent verification + needs a fresh ignored tools/build tree. Downloads/test artifacts are not secrets. + + Build logs and complete dependency trees are local ignored diagnostics in + .tools/chat-openssl-*.log and .tools/chat-openssl-duplicates.txt. The native + result and the browser result must explicitly identify the OpenSSL backend. + +REMAINING WORK + + Observed WASI artifact SHA-256 on this development machine: + 7fba6b96377619ef41f056d4027c4409107372e9300b2dd69be865e72892df79 + This is a diagnostic identifier, not a cross-host reproducibility promise. + + This checkpoint removed the Rust-backend blocker and demonstrated fixed + algorithms in-browser. It was not a complete messenger, OpenSSL/WASI/JIT timing + audit, LibreJS certification, or proof against malicious delivery. File format, + authentication, server/database, quotas, hostile-input handling, pagination/counts, + recovery, session lifecycle, and browser-state tests remained to implement. diff --git a/docs/CHAT-PRODUCTION.txt b/docs/CHAT-PRODUCTION.txt new file mode 100644 index 0000000..fdb48a6 --- /dev/null +++ b/docs/CHAT-PRODUCTION.txt @@ -0,0 +1,222 @@ +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/>. + + +PRODUCTION CHAT INTEGRATION + + 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 + + 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 + + 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: 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 + + Direct Rust development: + cargo stack chat + http://127.0.0.1:9080/ + var/data/chat + + Container preview: + cargo stack dev + http://chat.localhost:8080/ + var/data/chat-local + + Production: + ./bin/stack up --production + https://CHAT_HOST/ with the configured HTTPS_PORT + var/data/chat + + 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. + + 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 + + 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 restarts + failed containers. Complete matching-architecture releases need no compilation + or registry pull. Check all four services after activation: + + ./bin/stack status + + Check HTTPS, HTTP-to-HTTPS and www redirects, the homepage Services entry, + /LIMITATIONS.txt, /javascript.html, and /SOURCE.tar.gz on the chat origin. + The displayed limitations are CHAT-APPLICATION.txt. Chat has its own origin; + the main website remains JavaScript-free. Its Message Me links lead to chat. + + 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 + + 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 with fresh project names, avoiding replacement + of another installation. up --production reselects production database/origin. + Keep hostname/origin, repoint DNS, and use unchanged identity files/passwords. + IP changes preserve origin; hostname/HTTPS-port changes do not. Never expose + two restored copies under one origin. Restart invalidates sessions; reopening + files authenticates again. Historical ciphertext/counters persist. + + CHAT_SETUP_TOKEN cannot recover a lost private file. If backups are also lost, + reset deletes every conversation/message; it decrypts and recovers nothing. + Reset has no HTTP endpoint and requires filesystem/Podman access: + + ./bin/stack chat --owner-fingerprint + ./bin/stack down + ./bin/stack chat --reset-owner PASTE_CURRENT_OWNER_FINGERPRINT_HERE + ./bin/stack up --production + + Reset requires a stopped server and matching full fingerprint. Root .env and + exports survive. Create a new production owner file; old visitor files reject + its changed key and need fresh identities. Exports/external snapshots are not erased. + +VERIFICATION + + The browser harness supports packaged HTTPS installations without Rust or + target/ inside them. It creates disposable identities and a separate temporary + GnuPG key, takes a complete encrypted export with images during live chat, + restores into a new directory, starts the saved images, and reopens the same + files to decrypt history. It also exercises wrong passwords, safe text, + counters, 0-to-N history, 50-row batches, retry deduplication, origin rejection, + browser storage checks, and backend reset guards. It never resets real data. + + Example after restoring a release to a disposable staging directory and + configuring fixture .localhost hostnames, WEB_BIND=127.0.0.1, HTTP_PORT=8181, + HTTPS_PORT=8443, and LOCAL_HTTP_PORT=8180: + + ./bin/stack up --production + CHAT_TEST_ROOT=/absolute/disposable/stack CHAT_TEST_ORIGIN=https://chat.fixture.localhost:8443 PLAYWRIGHT_BROWSERS_PATH=.tools/browsers npm run test:chat + + Run from the source checkout for the harness and Playwright installation. + Add CHAT_LIBREJS=1 for Firefox with the actual GNU LibreJS add-on. That test + verifies the add-on is active and blocks an unlicensed control script. + Disposable staging needs a fresh database for each complete run. + + Browser automation permits the fixture's private CA only for functional tests. + Separately verify its chain and hostname with curl --cacert and --resolve; + test the canonical route and www redirect without disabling verification. + Production uses ordinary trusted public certificates with no TLS bypass. + Passing these checks demonstrates recovery and functional compatibility; it + does not establish absence of attacks or constitute an independent audit. + +RECORDED RESULTS, 2026-10-31 + + The complete native workspace check passed: formatting, strict Clippy, + 14 tests, private rustdoc, cargo-deny with no ignored advisories, and + cargo-about generation. Static musl and WASI OpenSSL 3.6.5 probes passed + without changing the required RFC 9980 algorithms. + + Direct development passed in Chromium. Packaged HTTPS passed independently + in Chromium and Firefox with GNU LibreJS 7.21.1 active; its unlicensed-script + control was rejected. Both packaged tests recovered a complete encrypted + application export, including saved images, into a new directory, then + reopened the unchanged owner/visitor files and decrypted the history. + Export and checksum permissions were verified as 0600. + + Runtime inspection verified read-only root filesystem, zero effective + capabilities, no chat host ports, and unless-stopped restart policy. Killing + the disposable chat process with SIGKILL caused an automatic restart, and + its owner fingerprint persisted. The supplied systemd user unit passed + systemd-analyze verification; this did not reboot the real VPS. + + curl independently verified the fixture CA chain and hostname for canonical + chat, www redirect, and homepage. The homepage Services limitations link + resolved correctly. The source download matched the packaged source archive + byte-for-byte and contained only .env.example, without actual .env. + + Upgrade testing started the 2026-10-30 three-service release, then activated + the chat-enabled release through its new prebuilt management binary. All + four services started, existing data and SearXNG secret persisted, and the + new owner setup token was generated in the same root .env. These tests used + disposable local data; they did not register a real production owner. diff --git a/docs/CONFIGURATION.txt b/docs/CONFIGURATION.txt new file mode 100644 index 0000000..f0f8457 --- /dev/null +++ b/docs/CONFIGURATION.txt @@ -0,0 +1,143 @@ +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/>. + +CENTRAL CONFIGURATION + + 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. Derive www aliases from each configured host. + + SITE_HOST, GIT_HOST, SEARCH_HOST, WKD_HOST, CHAT_HOST + 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 + Development gateway hostname ending in .localhost. Default: + chat.localhost. Browser identity files bind to this origin and local port. + + WEB_BIND + Production publish address. Default :: serves IPv4/IPv6. Use 127.0.0.1 + for isolated staging. Development always binds loopback. + + HTTP_PORT, HTTPS_PORT + 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 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 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. 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 for + encrypted exports. --recipient/--key override them. Keep the secret + GnuPG key and revocation certificate outside the project. + +PRIVATE SETTINGS + + SEARXNG_SECRET + Generated at first stack initialization; existing values preserved. + + CHAT_SETUP_TOKEN + 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. 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 + + Output under var/local/ or var/production/: + + Caddyfile + cgitrc + web/ + + 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. + + Apply to existing local services: + + cargo stack up --local + + Production uses prebuilt management command/images: + + ./bin/stack up --production + + 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 + + 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/LINTING.txt b/docs/LINTING.txt new file mode 100644 index 0000000..bd1cdac --- /dev/null +++ b/docs/LINTING.txt @@ -0,0 +1,167 @@ +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/>. + +STRICT LINT POLICY + + 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 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, 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 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: + + 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 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 + + 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/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 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 + + npm run build + cargo stack check + cargo stack release + + 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. + + 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 + + https://doc.rust-lang.org/stable/rustc/lints/levels.html + https://doc.rust-lang.org/clippy/lints.html + https://doc.rust-lang.org/clippy/lint_configuration.html + https://serde.rs/container-attrs.html + https://github.com/serde-rs/serde/blob/v1.0.229/serde_derive/src/dummy.rs + https://www.typescriptlang.org/tsconfig/ + https://typescript-eslint.io/users/configs/ + https://typescript-eslint.io/users/dependency-versions/ + https://github.com/advisories/GHSA-g3hv-75wg-m249 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 826c4cf..55dc81c 100644 --- a/docs/OPERATIONS.txt +++ b/docs/OPERATIONS.txt @@ -13,110 +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 the three services while copying consistent - application files, then resumes them before compression and encryption. - 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 @@ -125,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 @@ -169,164 +132,134 @@ 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. There is no need to restore Docker's old generated firewall rules. + 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. - 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 - listed in infra/Caddyfile. Update canonical hosts there if changing domains. - Keep the www aliases and openpgpkey names pointing to the canonical hosts. - - 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 a static Rust binary locally, - builds the cgit container locally, and packages 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 cd ~/stack - ./bin/stack activate var/incoming/stack-release-TIMESTAMP.tar.gz + mkdir -p var/incoming/bootstrap + tar -xzf var/incoming/stack-release-TIMESTAMP.tar.gz -C var/incoming/bootstrap stack/bin/stack + var/incoming/bootstrap/stack/bin/stack --root "$PWD" activate var/incoming/stack-release-TIMESTAMP.tar.gz ./bin/stack status - - 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 is files, Git repositories, and Caddy state. - When adding a database, add its supported flush/dump procedure to the - export workflow and test recovery before relying on that database's backup. - - 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. - - Legacy format-1 exports made before the Rust migration use their embedded - historical Python recovery helper. Keep that helper with those exports. - The current command intentionally accepts only its verified format-2 - archives. Fresh format-2 production backups replace the normal workflow. - -LICENSE PAGE IN CGIT - - The stack repository's About tab renders only - web/THIRD-PARTY-LICENSES.html. Source and Raw links retain the normal cgit - views and preserve revision queries. Other files are not rendered as HTML. - The rendered document is isolated in a sandboxed frame, with scripts and - external resources blocked. The standalone website license page remains - available at /THIRD-PARTY-LICENSES.html. + rm -rf -- var/incoming/bootstrap + + Use verified NEW release binary: old binary may reject new service schema. + Activation verifies archive/platform, replaces public source/binary, loads + exact images, preserves .env/var/data, replaces running containers. No compiler, + Rust build, image build, or private-config overwrite. Expect brief interruption. + Take verified export before changes needing recovery. Verify production; + optionally delete corresponding incoming archive/checksum. + +RECOVERY LIMITS; ROTATE KEYS + + Application export is not bootable VPS image. Accounts, SSH, firewall, + packages remain host duties. Other CPU architecture needs native release. + Registry needed only without saved images or when rebuilding another target. + State: files, Git repos, Caddy, encrypted SQLite chat. SQLite uses online backup. + Recovery proof: reopen BOTH participants' unchanged identities; decrypt history. + See CHAT-PRODUCTION.txt. Future databases need supported snapshot/recovery. + + New backup recipient: publish public key, update default fingerprint, test + encryption/restore. Single export: --recipient FULL_FINGERPRINT --key /path/public-key.asc. + Retain old secret keys while any retained export needs them. + Restore accepts verified format-2 application archives. + +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 592269b..dea595b 100644 --- a/docs/POLICY.txt +++ b/docs/POLICY.txt @@ -17,59 +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. - - 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 configured secrets only in the root .env. Never commit .env or var/. - Only the root .env.example is published; environment variants and nested - examples are ignored. .env.example lists required keys with placeholders. Public hostnames, - public GPG keys, paths, configuration, and source belong in Git. - 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 - Preserve the requested Rust and Clippy deny groups, including pedantic - and nursery. The one additional exception is redundant_pub_crate: its - advice to make internal binary items public conflicts with unreachable_pub, - which remains denied. Internal interfaces remain explicitly crate-private. - No unsafe code, unwrap, expect, todo, debug output, arithmetic shortcuts, - or broad warning suppression is introduced. - - cargo-deny rejects duplicate crate versions, wildcard requirements, unknown - registries, unknown Git sources, and yanked versions. There are no duplicate - version skip exceptions. Its AGPL allowance is scoped to this package. - The dependency allowlist retains the requested permissive licenses and - Zlib; an unused allowance may produce an informational audit warning. - - 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. - - Releases currently contain a static x86_64 Linux command 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. + 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 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. + + 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 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 new file mode 100644 index 0000000..7fa5125 --- /dev/null +++ b/docs/TYPESCRIPT.txt @@ -0,0 +1,125 @@ +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/>. + +TYPESCRIPT BROWSER TOOLCHAIN + + 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, history, downloads, API validation. + crates/chat-server/web/worker.ts + One-shot crypto worker; ends after each request. + crates/chat-crypto/browser/wasi.ts + Restricted WASI host for unchanged Rust/OpenSSL module. + crates/chat-crypto/browser/protocol.d.ts + Shared API/crypto request/response contracts. No emitted JavaScript; + checked in both browser environments. + crates/chat-crypto/tests/browser.ts + Real WebAssembly compatibility, entropy, capability tests. + crates/chat-server/tests/browser.ts + Complete browser chat/recovery tests, including GNU LibreJS. + crates/chat-server/tests/progress.ts + Download progress/failure tests, including GNU LibreJS. + tests/browser-support.ts + Checked fixture parsing, typed browser-automation helpers. + + 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. 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 + + 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/check all TypeScript, including browser tests: + + npm run build + + 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 runs this build, strict Rust checks, cargo-about generation. + cargo stack release builds TypeScript before packaging production artifacts. + + Recheck after npm run build without reinstalling: + + npm run check + + Fetch pinned Playwright browsers once: + + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" node_modules/.bin/playwright install chromium firefox + + 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 + PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:progress + 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 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/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. |
