* xftp: implementation of XFTP client as web page (rfc, low level functions) * protocol, file descriptions, more cryptogrpahy, handshake encoding, etc. * xftp server changes to support web slients: SNI-based certificate choice, CORS headers, OPTIONS request * web handshake * test for xftp web handshake * xftp-web client functions, fix transmission encoding * support description "redirect" in agent.ts and cross-platform compatibility tests (Haskell <> TypeScript) * rfc: web transport * client transport abstraction * browser environment * persistent client sessions * move rfcs * web page plan * improve plan * webpage implementation (not tested) * fix test * fix test 2 * fix test 3 * fixes and page test plan * allow sending xftp client hello after handshake - for web clients that dont know if established connection exists * page tests pass * concurrent and padded hellos in the server * update TS client to pad hellos * fix tests * preview:local * local preview over https * fixed https in the test page * web test cert fixtures * debug logging in web page and server * remove debug logging in server/browser, run preview xftp server via cabal run to ensure the latest code is used * debug logging for page sessions * add plan * improve error handling, handle browser reconnections/re-handshake * fix * debugging * opfs fallback * delete test screenshot * xftp CLI to support link * fix encoding for XFTPServerHandshake * support redirect file descriptions in xftp CLI receive * refactor CLI redirect * xftp-web: fixes and multi-server upload (#1714) * fix: await sodium.ready in crypto/keys.ts (+ digest.ts StateAddress cast) * multi-server parallel upload, remove pickRandomServer * fix worker message race: wait for ready signal before posting messages * suppress vite build warnings: emptyOutDir, externals, chunkSizeWarningLimit * fix Haskell web tests: use agent+server API, wrap server in array, suppress debug logs * remove dead APIs: un-export connectXFTP, delete closeXFTP * fix TypeScript errors in check:web (#1716) - client.ts: cast globalThis.process to any for browser tsconfig, suppress node:http2 import, use any for Buffer/chunks, cast fetch body - crypto.worker.ts: cast sha512_init() return to StateAddress * fix: serialize worker message processing to prevent OPFS handle race async onmessage allows interleaved execution at await points. When downloadFileRaw fetches chunks from multiple servers in parallel, concurrent handleDecryptAndStore calls both see downloadWriteHandle as null and race on createSyncAccessHandle for the same file, causing intermittent NoModificationAllowedError. Chain message handlers on a promise queue so each runs to completion before the next starts. * xftp-web: prepare for npm publishing (#1715) * prepare package.json for npm publishing Remove private flag, add description/license/repository/publishConfig, rename postinstall to pretest, add prepublishOnly, set files and main. * stable output filenames in production build * fix repository url format, expand files array * embeddable component: scoped CSS, dark mode, i18n, events, share - worker output to assets/ for single-directory deployment - scoped all CSS under #app, removed global resets - dark mode via .dark ancestor class - progress ring reads colors from CSS custom properties - i18n via window.__XFTP_I18N__ with t() helper - configurable mount element via data-xftp-app attribute - optional hashchange listener (data-no-hashchange) - completion events: xftp:upload-complete, xftp:download-complete - enhanced file-too-large error mentioning SimpleX app - native share button via navigator.share * deferred init and runtime server configuration - data-defer-init attribute skips auto-initialization - window.__XFTP_SERVERS__ overrides baked-in server list * use relative base path for relocatable build output * xftp-web: retry resets to default state, use innerHTML for errors * xftp-web: only enter download mode for valid XFTP URIs in hash * xftp-web: render UI before WASM is ready Move sodium.ready await after UI initialization so the upload/download interface appears instantly. WASM is only needed when user triggers an actual upload or download. Dispatch xftp:ready event once WASM loads. * xftp-web: CLS placeholder HTML and embedder CSS selectors Add placeholder HTML to index.html so the page renders a styled card before JS executes, preventing layout shift. Use a <template> element with an inline script to swap to the download placeholder when the URL hash indicates a file download. Auto-compute CSP SHA-256 hashes for inline scripts in the vite build plugin. Change all CSS selectors from #app to :is(#app, [data-xftp-app]) so styles apply when the widget is embedded with data-xftp-app attribute. * xftp-web: progress ring overhaul Rewrite progress ring with smooth lerp animation, green checkmark on completion, theme reactivity via MutationObserver, and per-phase color variables (encrypt/upload/download/decrypt). Show honest per-phase progress: each phase animates 0-100% independently with a ring color change between phases. Add decrypt progress callback from the web worker so the decryption phase tracks real chunk processing instead of showing an indeterminate spinner. Snap immediately on phase reset (0) and completion (1) to avoid lingering partial progress. Clean up animation and observers via destroy() in finally blocks. * xftp-web: single progress ring for upload, simplify ring color * xftp-web: single progress ring for download * feat(xftp-web): granular progress for encrypt/decrypt phases Add byte-level progress callbacks to encryptFile, decryptChunks, and sha512Streaming by processing data in 256KB segments. Worker reports fine-grained progress across all phases (encrypt+hash+write for upload, read+hash+decrypt for download). Progress ring gains fillTo method for smooth ease-out animation during minimum display delays. Encrypt/decrypt phases fill their weighted regions (0-15% and 85-99%) with real callbacks, with fillTo covering remaining time when work finishes under the 1s minimum for files >= 100KB. * rename package --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> Co-authored-by: shum <github.shum@liber.li> Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
4.2 KiB
Coding and building
This file provides guidance on coding style and approaches and on building the code.
Code Security
When designing code and planning implementations:
- Apply adversarial thinking, and consider what may happen if one of the communicating parties is malicious.
- Formulate an explicit threat model for each change - who can do which undesirable things and under which circumstances.
Code Quality Standards
Haskell client and server code serves as system specification, not just implementation — we use type-driven design to reflect the business domain in types. Quality, conciseness, and clarity of Haskell code are critical.
Code Style, Formatting and Approaches
The project uses fourmolu for Haskell code formatting. Configuration is in fourmolu.yaml.
Key formatting rules:
- 2-space indentation
- Trailing function arrows, commas, and import/export style
- Record brace without space:
{field = value} - Single newline between declarations
- Never use unicode symbols
- Inline
letstyle with right-alignedin
Format code before committing:
# Format a single file
fourmolu -i src/Simplex/Messaging/Protocol.hs
Some files that use CPP language extension cannot be formatted as a whole, so individual code fragments need to be formatted.
Follow existing code patterns:
- Match the style of surrounding code
- Use qualified imports with short aliases (e.g.,
import qualified Data.ByteString.Char8 as B) - Use record syntax for types with multiple fields
- Prefer explicit pattern matching over partial functions
Comments policy:
- Avoid redundant comments that restate what the code already says
- Only comment on non-obvious design decisions or tricky implementation details
- Function names and type signatures should be self-documenting
- Do not add comments like "wire format encoding" (Encoding class is always wire format) or "check if X" when the function name already says that
- Assume a competent Haskell reader
Diff and refactoring:
- Avoid unnecessary changes and code movements
- Never do refactoring unless it substantially reduces cost of solving the current problem, including the cost of refactoring
- Aim to minimize the code changes - do what is minimally required to solve users' problems
Document and code structure:
- Never move existing code or sections around - add new content at appropriate locations without reorganizing existing structure.
- When adding new sections to documents, continue the existing numbering scheme.
- Minimize diff size - prefer small, targeted changes over reorganization.
Code analysis and review:
- Trace data flows end-to-end: from origin, through storage/parameters, to consumption. Flag values that are discarded and reconstructed from partial data (e.g. extracted from a URI missing original fields) — this is usually a bug.
- Read implementations of called functions, not just signatures — if duplication involves a called function, check whether decomposing it resolves the duplication.
- Do not save time on analysis. Read every function in the data flow even when the interface seems clear — wrong assumptions about internals are the main source of missed bugs.
Haskell Extensions
StrictDataenabled by default- Use STM for safe concurrency
- Assume concurrency in PostgreSQL queries
- Comprehensive warning flags with strict pattern matching
Build Commands
# Standard build
cabal build
# Fast build
cabal build --ghc-options -O0
# Build specific executables
cabal build exe:smp-server exe:xftp-server exe:ntf-server exe:xftp
# Build with PostgreSQL server support
cabal build -fserver_postgres
# Client-only library build (no server code)
cabal build -fclient_library
# Find binary location
cabal list-bin exe:smp-server
Cabal Flags
swift: Enable Swift JSON formatclient_library: Build without server codeclient_postgres: Use PostgreSQL instead of SQLite for agent persistenceserver_postgres: PostgreSQL support for server queue/notification store
External Dependencies
Custom forks specified in cabal.project:
aeson,hs-socks(SimpleX forks)direct-sqlcipher,sqlcipher-simple(encrypted SQLite)warp,warp-tls(HTTP server)