Skip to content

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.sh changed and ships inside the image.
  • No manual migrations. The mfa_token_attempts collection (with automatic expiry) is created on its own, and existing webhook events remain valid.
  • Behind a reverse proxy, set FORWARDED_ALLOW_IPS to 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_KEY if 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/.env has BACKEND_CORS_ORIGINS as 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-provider requires 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 strict mode. With loose, 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_IPS variable (localhost only by default): X-Forwarded-For is 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_ORIGINS as a JSON list, which the backend turned into invalid origins. Both formats are accepted.
  • external-mongodb mode: 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), empty SENTRY_DSN (a sample value enabled Sentry) and FRONTEND_URL on port 5173. ACCESS_TOKEN_EXPIRE_MINUTES has 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.md and 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_KEY is required.
  • More Material extensions: content tabs, keys, task lists, breadcrumbs and an "edit this page" button.
  • SECURITY.md and AGENTS.md aligned 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.