0.8.4 (2026-10-19)¶
Security and configuration fixes, and complete documentation on the published site: besides installation and release notes, there is now a user guide, self-host operations, development and security, in Spanish and English, checked against the code. Writing that documentation against the code surfaced the issues this release fixes. No new features.
⬆️ Upgrade notes¶
- Upgrade soon: this release includes security fixes (AI chat, MFA sign-in, Stripe webhooks and Mermaid rendering).
- Rebuild the images (
docker compose up -d --build):start.shchanged and ships inside the image. - No manual migrations. The
mfa_token_attemptscollection (with automatic expiry) is created on its own, and existing webhook events remain valid. - Behind a reverse proxy, set
FORWARDED_ALLOW_IPSto your proxy's IP or network so rate limits and the audit log see the real client IP. See Production. Don't use*. - Don't change your
AI_ENCRYPTION_KEYif you already have one: it encrypts AI keys, integration secrets and TOTP secrets. The installer now generates it on new installs and keeps an existing one. - If your
backend/.envhasBACKEND_CORS_ORIGINSas a JSON list, it now works; the recommended format is comma-separated. - A Stripe webhook that fails to process now answers 500 (was 200) so Stripe retries it.
🔒 Security¶
- AI chat: each session belongs to its owner only. Six
/chat-sessions/{id}endpoints (read, rename, delete, change model, delete a message and change its status) required no session and checked no owner, and sending a message didn't check the session was yours. Every chat route now requires a session and answers 404 for another user's session or a message from another session. POST /ai/test-providerrequires a session.- Stripe webhooks are truly idempotent. The handler used the object id instead of the event id and recorded nothing. Each event is now claimed by its
event.id(unique index): a duplicate is not re-applied, a failure releases the claim so Stripe retries, and an abandoned claim is taken over after 10 minutes. Each claim carries its own lease: a delivery that lost its claim can no longer complete or release the new owner's. - MFA sign-in: 5 attempts per temporary token. The counter was never incremented. Attempts are now reserved atomically before the code is checked (also under concurrent requests), the token is unique per login (
jti) and single-use after a successful sign-in, and reaching the limit is audited (login_failed,mfa_max_attempts). - Mermaid in
strictmode. Withloose, HTML labels in a diagram were not sanitized while Mermaid drew it, before the SVG reached the page (shared links included). They now go through DOMPurify during rendering. - Real client IP behind a proxy. New
FORWARDED_ALLOW_IPSvariable (localhost only by default):X-Forwarded-Foris accepted only from those addresses. Behind a proxy, every user used to share the proxy's IP in rate limits, account lockout and the audit log. - Shared-link throttling uses the common limiter, which purges old IPs (it used to keep them forever), and the 429 response carries
Retry-After.
🔧 Fixes¶
- CORS: the templates shipped
BACKEND_CORS_ORIGINSas a JSON list, which the backend turned into invalid origins. Both formats are accepted. external-mongodbmode: the backend listened on port 8000 while the compose file published 5172.- Installer: generates
AI_ENCRYPTION_KEY, without which AI, integrations and TOTP failed, and keeps an existing one also when reinstalling over the previous directory (the database survives, and a new key would make its secrets undecryptable). - Environment templates: no
VERSION=1.0.0(it overrode the real version), emptySENTRY_DSN(a sample value enabled Sentry) andFRONTEND_URLon port 5173.ACCESS_TOKEN_EXPIRE_MINUTEShas no effect and is documented as such. - Setup wizard: asked for 8 characters without a special character; it now enforces the real policy (12, upper, lower, digit and special character). The frontend no longer accepts a backtick as the special character, which the server rejected.
STRIPE_QUICKSTART.mdand the deploy READMEs (Kroki) updated.
📚 Documentation¶
- Site organized by reader. Each language now has five sections grouped in the sidebar: User guide, Self-host, Development, Security and Release notes (grouped by minor version), plus an FAQ.
- User guide (10 pages): getting started, the editor, Mermaid/PlantUML/D2/DBML with examples and styling options, whiteboard and sketch → Mermaid, projects and folders, export and import, sharing, presentation mode, AI assistant (all five providers) and account (MFA, recovery codes, Google).
- Self-host (7 pages): environment variable reference, production (proxy, SSL, hardening), email/Stripe/Google integrations, operator-defined plans and limits, backups and upgrades, and troubleshooting.
- Development (9 pages): architecture with diagrams, the 12 backend modules, frontend, REST API map, testing, how to extend it (modules, AI providers, pages, texts), versioning and contributing.
- Security (4 pages): security model, authentication and sessions, protections and audit log.
- Home page fixed. It listed only Mermaid and PlantUML, four AI providers and features that don't exist (presentation annotations, autocomplete, prompt history as a user feature); it now reflects the 0.8.x product.
- The installation guide includes Kroki in both deployment modes and states that
AI_ENCRYPTION_KEYis required. - More Material extensions: content tabs, keys, task lists, breadcrumbs and an "edit this page" button.
SECURITY.mdandAGENTS.mdaligned with the code.
🧪 Tests¶
- Backend: 479 tests, 65% coverage (45% floor); new tests for chat session ownership, webhook idempotency, the MFA attempt limit (concurrency included), IP behind a proxy, shared-link throttling and CORS settings. Frontend: 25 Vitest tests.