Please email security@relay.dev. We aim to acknowledge within 48 hours and to ship a fix (or a documented mitigation) within two weeks for issues we accept as valid.
Relay is a multi-user platform where each vault holds live-editable Markdown files. The relevant boundaries are:
- Authentication: bcrypt password hashes, 30-day JWTs.
- Vault authorization: every WebSocket upgrade re-verifies the JWT
and looks up
vault_memberships. Non-members get a socket destroy. - Roles:
owner | editor | viewer, enforced on every REST route viarequireMember(roles). - Filesystem boundary: the server refuses to persist paths that
contain
.., absolute paths, or non-.mdextensions. The disk layer is authoritative — you cannot escapeDATA_DIR/vaults/<vault-id>/regardless of what a client puts in the Y.Map. - Provider keys: kept in server env only. Never sent to clients.
- Viewer write-drop is not yet plumbed through the Yjs channel. Viewers can currently push Yjs updates because y-websocket applies updates before we check the role. REST is role-gated; WS is membership-gated only. Fix pending: wrap the ws handler to drop non-sync-1 messages for viewers.
- JWTs cannot be revoked mid-session. Removing a member closes their next connection, but an already-connected socket stays live until it reconnects. Ship a version bump on the user row and check it on WS message to close in-flight sockets if this matters to you.
- No email verification and no password reset. Self-host tool — you know your users. Wire your own recovery flow if hosting for strangers.
- Rate limiting is in-process. Fine for a single container; move to Redis for multi-instance.
- Provider keys are stored in env vars. Simple, but if your host disk is compromised the keys are readable. Envelope-encrypt them (or keep them in an outside secret store) if that fits your threat model.
The old session-engine version had specific fixes for its own doc-sync model (put-merge role enforcement, provider-secret redaction across the WS wire, invite token leakage). Most of that surface is deleted along with the session engine. The remaining shape is significantly smaller and more auditable.
- Bcrypt (cost 10) for password hashes.
- HS256 JWTs with a server-side secret. Rotate
JWT_SECRETto invalidate every session at once. - TLS is the deployer's job — Relay is deployed behind nginx / Caddy / Traefik.
Relay never trains any model on your vault content. Prompts and outputs pass through the AI provider you choose. Consult that provider's own retention policy for what they do with the data.