Reference server implementation for the Vex encrypted chat platform. NodeJS + SQLite + TypeScript, running the wire protocol defined in @vex-chat/types.
- REST API (Express 5) for auth, registration, users, servers, channels, invites, and file upload.
- WebSocket server (native
ws) for real-time messaging, presence, and push notifications. Frames are msgpack-encoded per the AsyncAPI spec in@vex-chat/types. - SQLite persistence via Kysely + better-sqlite3. Single-file DB, zero external services.
- Runtime validation on every trust boundary: every request body, query string, and WebSocket payload is parsed through a Zod schema before any logic runs.
- Interactive docs — Scalar at
/docsfor the OpenAPI spec, the AsyncAPI web component at/async-docsfor the WebSocket protocol. Both production-gated for security. - Authentication via
@vex-chat/cryptosigning keys plus JWT session tokens. Password hashing via nativenode:cryptoPBKDF2.
From public npm:
npm install @vex-chat/spireOr clone the repo:
git clone git@github.com:vex-protocol/spire
cd spire
npm ciSpire runs directly from source via node --experimental-strip-types — no pre-compile step needed in dev or prod. From a clone:
npm startOr equivalently:
node --experimental-strip-types src/run.tsFrom an npm install, the source ships in the tarball under node_modules/@vex-chat/spire/src/, so you can run it directly:
node --experimental-strip-types node_modules/@vex-chat/spire/src/run.tsSpire reads configuration from environment variables. Use a .env file at the repo root (or wherever you run it from) — dotenv picks it up automatically.
| Variable | Description |
|---|---|
SPK |
Server private key, hex-encoded. Generate one with npm run gen-spk. Used for server identity signing and as the default JWT secret if JWT_SECRET isn't set. |
DB_TYPE |
sqlite, sqlite3, or sqlite3mem. Controls database backend. sqlite3mem runs an in-memory database useful for tests. (MySQL support was removed in 1.0.0; operators on older deploys should migrate out.) |
CANARY |
true to enable canary mode (runs extra runtime assertions). false for standard production. |
| Variable | Default | Description |
|---|---|---|
API_PORT |
16777 |
Port for the REST API and WebSocket server. |
NODE_ENV |
development |
production enables hardened Helmet CSP and disables interactive /docs / /async-docs viewers. |
JWT_SECRET |
SPK |
Override the JWT signing secret. Falls back to SPK if unset. |
SPK=a1b2c3... # generate with `npm run gen-spk`
DB_TYPE=sqlite
CANARY=false
API_PORT=16777
NODE_ENV=productionnpm run build # tsc (sanity check — runtime uses --experimental-strip-types)
npm run lint # eslint strictTypeChecked
npm run lint:fix # eslint --fix
npm run format # prettier --write
npm run format:check
npm test # vitest run
npx type-coverage # type-coverage (≥95%)
npm run license:check # license allowlist gateSee AGENTS.md for the release flow (changesets → publish → deploy-hook) and the rules for writing changesets.