docs: reorganise documentation; derive header version from package.json; release v5.7.2
CodeQL Analysis / Analyze CodeQL (push) Waiting to run
Deploy Application / deploy (push) Waiting to run
Mirror to Codeberg / mirror (push) Waiting to run
Mirror to PrivacyGuides / mirror (push) Waiting to run

No protocol or message-protection changes.

The version in the application header was a literal and had fallen behind,
showing v5.6.0 while running 5.7.1. It now comes from package.json, and a test
fails if a hard-coded one reappears or if meta.json, the README badge, the
changelog and the docs disagree about the release.

Documentation reorganised so that everything technical lives in doc/ with an
index, and the root keeps only what belongs there by convention: README,
SECURITY, CHANGELOG and LICENSE.

- SECURITY.md rewritten. It listed a supported release line three major versions
  out of date and made claims the software does not make. It now states what is
  guaranteed, what is not, and how to report a problem.
- SECURITY_DISCLAIMER.md and RESPONSIBLE_USE.md merged into doc/USE-POLICY.md,
  which says what the software cannot protect against rather than listing
  generic advice.
- doc/SECURITY-ARCHITECTURE.md renamed to doc/ARCHITECTURE.md and rewritten
  around the session lifecycle, what verification gates, and how recovery works.
- doc/CRYPTOGRAPHY.md rewritten: key schedule, the Double Ratchet, framing, and
  memory handling, with values taken from the source rather than restated.
- doc/CONFIGURATION.md rewritten with the real file-type policy, ICE and TURN
  guidance, and the deployment caching rules that matter.
- docs/webrtc-config.md moved to doc/CALLS.md and rewritten; the obsolete
  docs/webrtc-audit.md, a working document full of stale line numbers, removed
  along with the docs/ directory.
- doc/CONTRIBUTING.md records what the recent regressions taught us about
  writing tests that can actually fail.
- doc/README.md added as an index.

Internal security review notes are excluded from the repository via .gitignore.
Those describe attack paths against specific releases in enough detail to
reproduce them, which is useful privately and harmful in public while users are
still updating.
This commit is contained in:
lockbitchat
2026-08-05 23:54:50 -04:00
parent 27279ae7c6
commit 3212138a0d
24 changed files with 1147 additions and 832 deletions
+122 -34
View File
@@ -1,12 +1,13 @@
# Configuration Guide
# Configuration
## Requirements
- modern browser with WebRTC and Web Crypto support
- Node.js 18+ for local development
- TURN service only when relay-only privacy mode is required
- A browser with WebRTC and Web Crypto support
- Node.js 18 or later, for building
- A TURN service, if you need relay-only privacy mode or you expect users behind
strict NAT
## Local setup
## Building and running
```bash
npm install
@@ -14,50 +15,137 @@ npm run build
npm run serve
```
## ICE server configuration
`npm run build` compiles the CSS, bundles the JavaScript into `dist/`, and
regenerates `meta.json` with a build stamp. That stamp is what the update
mechanism compares, and it is also written into `sw.js` so the browser reinstalls
the service worker on each release. A deployment that skips `post-build` will not
notify anyone that an update exists.
SecureBit.chat keeps existing STUN support for ordinary WebRTC connectivity. Deployments that require relay-only privacy must provide their own TURN service credentials through deployment configuration; public TURN credentials are intentionally not bundled.
The application is static. Any web server can host it, and there is no backend to
run.
## ICE and TURN
WebRTC needs to discover network paths between the two browsers. STUN is enough
to learn a public address; TURN is needed when a direct path cannot be
established, and is the only way to keep peers from seeing each other's IP
addresses.
Configuration comes from three places, in order of precedence:
1. User settings under Advanced network settings, stored in IndexedDB per device
2. `config/ice-servers.js`, an operator override loaded before the application
3. Built-in public STUN defaults
`config/ice-servers.js` is not committed, because it is where operator TURN
credentials would go. Use `config/ice-servers.example.js` as the template. The
Docker image copies `config/ice-servers.prod.js` into place at build time.
```js
window.SECUREBIT_ICE_SERVERS = [
{ urls: 'stun:stun.example.org:3478' },
{
urls: [
'turn:turn.example.org:3478?transport=udp',
'turn:turn.example.org:3478?transport=tcp',
'turns:turn.example.org:443?transport=tcp'
],
username: '...',
credential: '...'
}
];
```
Offering several transports is worth the extra lines. UDP is the most widely
usable, TCP covers networks that block UDP, and TURNS on 443 gets through
firewalls that only allow HTTPS. Some browsers also fail to resolve STUN and TURN
hostnames inside their WebRTC layer even when ordinary page DNS works, so listing
a raw IP alongside the hostname is a reasonable fallback.
Any TURN credential shipped to a browser is public by definition, because the
browser has to be able to read it. Treat it as a shared resource and apply quotas
on the TURN server rather than relying on the credential staying secret.
### User-supplied servers
Users can paste their own STUN and TURN servers. Input is validated against an
allowlist before it reaches `RTCPeerConnection`: only the `stun`, `stuns`, `turn`
and `turns` schemes, a hostname or bracketed IPv6 address with an optional port,
and an optional `transport=udp` or `transport=tcp` query. At most 10 servers with
8 URLs each. Anything else is rejected with a specific reason.
### Privacy modes
| Mode | Behavior | IP privacy |
| Mode | Behaviour | IP exposure |
| --- | --- | --- |
| default | standard WebRTC candidate gathering | direct candidates may expose IP addresses |
| relay-only | `iceTransportPolicy: "relay"` | requires TURN and avoids direct peer candidates when configured correctly |
| Default | Standard candidate gathering | Direct candidates can reveal addresses to the peer |
| Relay-only | Sets `iceTransportPolicy: "relay"` | Requires TURN; peers see only the relay |
### Operational rules
STUN is not a substitute for TURN here. It reveals your public address to the
peer by design. Relay-only mode without a configured TURN server cannot connect
at all, and the interface warns when TURN is missing.
- STUN is not a privacy substitute for TURN.
- Relay-only mode without TURN cannot establish a working relay connection.
- The UI warns users when TURN is missing.
- Validate TURN deployment with browser WebRTC diagnostics before production rollout.
Validate a TURN deployment with `chrome://webrtc-internals` before relying on it.
A relay candidate should appear in the gathered set; if none does, the credentials
or the ports are wrong.
## Verification flow
### When connections fail
Protocol `4.1` requires interactive SAS verification:
Candidate gathering finishes only when every configured server has replied or
timed out. Behind a VPN or a restrictive firewall that may never happen, and the
console will show `701` errors for each unreachable server.
1. both peers derive the same SAS from shared session material
2. users compare the code out of band
3. each user enters the matching code manually
4. the chat unlocks only after both confirmations succeed
The application handles this: it proceeds as soon as there are usable candidates
and only keeps waiting while there are none, up to 25 seconds. Host candidates
alone are often enough on a local network. If nothing at all is gathered, the
message names the likely causes, which in practice are a VPN binding the browser
to an interface that cannot reach the servers, or a firewall dropping UDP.
Three failed local attempts disconnect the session.
## File transfer policy
## File-transfer policy
Incoming transfers are validated before the consent prompt and require explicit
approval.
Incoming file requests are validated before the consent prompt and require explicit user approval.
| Category | Extensions | Size limit |
| --- | --- | --- |
| Images | `.jpg` `.jpeg` `.png` `.gif` `.webp` `.bmp` `.ico` | 25 MB |
| Documents | `.pdf` | 50 MB |
| Text | `.txt` | 10 MB |
| Archives | `.zip` | 100 MB |
| Voice | `.webm` `.ogg` `.oga` `.opus` `.m4a` `.mp4` `.mp3` `.wav` | 20 MB |
Allowed categories:
Overall ceiling is 100 MB per file.
- common raster images
- PDF
- plain text
- ZIP archives
Blocked outright: `.exe` `.bat` `.cmd` `.sh` `.js` `.msi` `.dmg` `.app` `.jar`
`.scr` `.ps1` `.vbs` `.html` `.svg`
Blocked examples:
The extension list is the security boundary. MIME type is treated as advisory,
because it is client-supplied, varies between browsers and operating systems, and
is frequently absent. An allowed extension is accepted when the MIME type is
absent, generic, or one of the recognised types, but a clearly contradictory MIME
type is rejected as a spoofing signal.
- `.exe`, `.bat`, `.cmd`, `.sh`, `.js`
- `.msi`, `.dmg`, `.app`, `.jar`, `.scr`
- `.ps1`, `.vbs`, `.html`, `.svg`
Voice notes are the one transfer accepted without a prompt, so they are checked
more strictly. The receiver decides, not the sender: a genuine audio MIME type,
at most 4 MB, and a 64 MB budget for the whole session. A transfer that fails
those checks is not rejected, it simply loses the shortcut and appears as a normal
file with the usual prompt.
Both MIME type and extension must be acceptable. Missing or unknown MIME types are treated as unsafe unless explicitly covered by policy.
## Deployment notes
The repository includes an nginx configuration (`deploy/nginx.conf`) and an
Apache one (`.htaccess`). Both set the same policy, and the important parts are:
- `index.html`, `sw.js`, `manifest.json`, `meta.json` and `config/ice-servers.js`
must not be cached. A stale `meta.json` breaks update notification, and a stale
`sw.js` freezes the service worker.
- `dist/` bundles are query-versioned, so `no-cache` with revalidation is enough
and avoids re-downloading unchanged bundles.
- `CDN-Cache-Control` is set separately, because a CDN reads it independently of
the browser directive and will otherwise happily serve a stale app shell.
- `frame-ancestors` and HSTS have to be sent as headers. The rest of the content
security policy is a meta tag in `index.html`.
- `.jsx` and `.mjs` must be served as JavaScript, or module loading fails.
Asset requests should return 404 when a file is missing rather than falling back
to the HTML shell. A missing script served as HTML fails in a confusing way.