A risk matrix and safety planning tool. Designed to help you think through risks and how you can prepare for them.
Security: All data is saved locally unless you use the "share" feature. In that case, all data is end-to-end encrypted (not visible to our server).
Every deploy has to be signed with the YubiKey, or Railway will reject it.
- Make your changes and commit them.
- Plug in the YubiKey.
yarn webcat:sign— enter the PIN, then tap the key when prompted.- Commit the updated
public/.well-known/webcat/files. - Push. Railway builds and deploys.
yarn webcat:verifyto confirm the live site matches.
If a deploy comes back unhealthy, it almost always means step 3 was skipped:
the build no longer matches the signed manifest, so /api/healthz returns 503
and Railway keeps the previous version. Re-sign and push again.
yarn install
yarn devOpen http://localhost:3000.
The local-only experience works out of the box — matrices persist in
localStorage. To exercise the cloud-sync feature in dev, you also need a
local MongoDB:
# Boot a Mongo container (persistent volume; survives restarts).
yarn db:up
# Tell the app where to find it. Copy .env.local.example → .env.local
# and edit. The default MONGO_URL in the example points at yarn db:up.
cp .env.local.example .env.local
yarn dev| Command | What it does |
|---|---|
yarn dev |
Vite dev server (HMR) plus the API. |
yarn build |
Production build: dist/ + dist-server/. |
yarn start |
Serve the production build. |
yarn lint |
ESLint |
yarn test |
Vitest (UI, server route, and static-resolution tests). |
yarn typecheck |
tsc --noEmit |
yarn db:up |
Start the dev MongoDB container. |
yarn db:down |
Stop it (volume preserved). |
yarn db:logs |
Tail Mongo logs. |
yarn db:reset |
Stop AND wipe the dev volume. |
Cloud-saved matrices and link sharing are opt-in per matrix and end-to-end encrypted: the server never sees plaintext, titles, or keys. See THREAT-MODEL.md for the trust assumptions.
The API is served from the same origin as the app, so client requests use
relative URLs. To disable the feature entirely on a deploy that has no
database, set VITE_CLOUD_SYNC_ENABLED=false — all share affordances are
hidden.
index.html SPA entry document (hand-written head)
privacy/index.html Privacy page, its own static document
client/ Entries, app shell, router-less path dispatch
components/risk-matrix/ SPA components, hooks, local repo
lib/cloud/ Server-side: Mongo, route helpers, rate limit
lib/e2ee/ Client-side: XChaCha20-Poly1305 envelope
server/index.ts Serves the static build and the API, one origin
server/routes/ API handlers (Web Request/Response)
server/staticFiles.ts Path resolution, traversal guards, cache policy
public/theme-boot.js Blocking pre-paint theme script (never inlined)
docker-compose.dev.yml Local Mongo for dev
MIGRATION.md Migration plan, decisions, security gate
THREAT-MODEL.md In-scope guarantees and explicit out-of-scope risks
Built with Vite: yarn build produces the static client in dist/ and the
server bundle in dist-server/. One Node process serves both, so the app is
same-origin by construction.
See LICENSE (GNU GPL v3).