PrivateChat
PrivateChat is a two-part application:
- An Angular 21 + Bootstrap frontend for authentication, peer discovery, and direct chat.
- A Fastify + WebSocket backend for JWT-based authentication, Redis-backed sessions, and initial WebRTC signaling.
Once two clients discover each other through the backend, text, JSON payloads, and file transfers move over a direct WebRTC data channel. Signaling is only allowed for clients with an active authenticated backend session.
Features
- Register and log in with JWTs that are bound to active Redis sessions.
- Sign in with either a password or a registered WebAuthn access key.
- Persist users in a local SQLite database with encrypted credential storage.
- Persist the JWT signing secret in the SQLite database, encrypted at rest.
- Allow authenticated users to register multiple WebAuthn access keys.
- Discover online peers through a signaling WebSocket.
- Establish direct WebRTC peer connections.
- Exchange plain text messages, structured JSON payloads, and files.
- Keep signaling on the backend and user content on the peer-to-peer data channel.
Run locally
Requirements:
- Node.js 20+ recommended.
- npm 10+.
- A local Redis instance reachable at
redis://127.0.0.1:6379/0by default.
Install the root runner dependency:
npm install
Start both apps:
npm run dev
Configuration defaults now live in the repo root .env. The backend loads that file with dotenv at startup, and the frontend generates client/public/env.js from the same file before ng serve and ng build.
To serve the prebuilt Angular app from Fastify and run only one server:
npm run build
npm run start --prefix server
The backend serves client/dist/client/browser on the same origin as the API and WebSocket endpoints.
Default endpoints:
- Frontend:
http://localhost:4200 - Backend:
http://localhost:3000
The backend automatically creates:
server/data/privatechat.sqlitefor users and encrypted secret material.server/data/master.keyifPRIVATECHAT_MASTER_KEYis not supplied.
The frontend lets you override the backend URL from the login screen if you need a different host or port.
Apple client
The repo also includes a multiplatform SwiftUI client in apple-client/ for macOS and iOS/iPadOS.
- The native Settings UI manages backend URL, password auth, passkey auth, and access-key registration.
- The main chat surface embeds the Angular client bundle in a WKWebView, so the WebRTC chat workspace stays aligned with the web app.
- Generate the Xcode project with
xcodegen generate --spec apple-client/project.yml --project-root apple-client. - A build of the Apple app automatically rebuilds the Angular client into
apple-client/WebApp/before bundling it.
The backend also exposes the latest Angular browser build through an API that native clients can sync into their local WKWebView bundle cache:
GET /api/web-app/manifest: Returns a bundle manifest with a stablebundleId, latestgeneratedAttimestamp, and a file list containing relative paths, SHA-256 hashes, MIME types, sizes, and download URLs.GET /api/web-app/files/<relative-path>: Streams an individual file fromclient/dist/client/browserwithETagandLast-Modifiedheaders for native caching.
If the Angular build does not exist yet, those endpoints return 404.
Backend environment
The backend accepts these environment variables:
PORT: HTTP and WebSocket port. Default3000.REDIS_URL: Local Redis connection string. Defaultredis://127.0.0.1:6379/0.SESSION_TTL_SECONDS: Redis session TTL. Default43200.SQLITE_PATH: Optional SQLite database path.PRIVATECHAT_DATA_DIR: Base directory for generated local storage.PRIVATECHAT_MASTER_KEY: Optional master key for encrypting SQLite secret material and user credentials.PRIVATECHAT_MASTER_KEY_PATH: Optional file path for the generated master key.PRIVATECHAT_WEB_DIST_DIR: Directory containing the prebuilt Angular browser bundle. Defaultclient/dist/client/browser.CORS_ORIGIN: Optional comma-separated browser-origin allowlist. If omitted, the server accepts request origins. The specialnullorigin from embeddedfile://webviews is accepted.WEBAUTHN_ORIGIN: Browser origin allowed to register access keys. Defaulthttp://localhost:4200.WEBAUTHN_RP_ID: WebAuthn RP ID. Default hostname ofWEBAUTHN_ORIGIN.WEBAUTHN_RP_NAME: Friendly RP name for browser access-key prompts. DefaultPrivateChat.WEBAUTHN_USER_VERIFICATION: WebAuthn user-verification policy for access-key registration. Supported values:discouraged,preferred,required. Defaultpreferred.WEBAUTHN_CHALLENGE_TTL_SECONDS: Pending access-key registration challenge TTL in Redis. Default300.
Frontend environment
PRIVATECHAT_CLIENT_SERVER_URL: Default backend URL preloaded into the Angular app before local storage overrides apply.
Production note
This project uses the Fastify server only for auth and signaling. The chat payload itself stays peer-to-peer over WebRTC. For production deployment, replace the local master key strategy with your preferred secret-management system, keep Redis durable enough for your session policy, and configure TURN infrastructure if peers cannot connect with STUN alone.