Two related reliability fixes. 1. routes/chat.py — guard run_generation against uncaught exceptions. run_generation is launched with asyncio.create_task(); any exception raised inside the coroutine is silently swallowed by the event loop, the buffer stays in GenerationState.RUNNING forever, and every subsequent POST /api/chat/conversations/<id>/messages returns 409 'Generation already in progress' — locking the user out of the chat with no log trail. Observed in dev 2026-05-22: assistant message 768 created at 20:36:59 with status=generating, stayed in that state for an hour+, and four follow-up message attempts returned 409 instantly. The generation task hung before any internal log line could fire, so the only diagnostic was the 409 responses themselves. Wrap run_generation in _run_generation_guarded() that catches exceptions, logs with full traceback, transitions the buffer to ERRORED, emits a final 'done' SSE event so any active stream client closes cleanly, and marks the assistant message status=error in the DB. After this, a stuck conversation recovers on its own the next time the user sends a message — no manual DB poke needed. 2. services/curator_scheduler.py — pass last_curator_run_at as 'since' to the curator so each sweep only sees messages added after the previous successful pass. Previously the scheduler called run_curator_for_conversation(conv_id) with no 'since' argument, so the curator defaulted to its 24h lookback window. Within an active journal session that meant every 15-min sweep re-extracted beats from messages already captured on prior sweeps — producing duplicate moments. _candidate_conversations() now returns (conv_id, last_curator_run_at) tuples; _sweep() threads the timestamp through. First-run case (last_curator_run_at IS NULL) falls back to the curator's default 24h window, which is what we want — process recent backlog on first contact, then only deltas after. Manual trigger path (POST /api/journal/curator/run/<conv_id>) is intentionally NOT changed; it still passes since=None so the 24h re-sweep behaviour is preserved for ad-hoc 'reprocess today' clicks from the UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fabled Scribe
A self-hosted second brain and project management application with integrated LLM capabilities. Write, organise, and act on your notes and tasks with the help of a local AI assistant — all running on your own hardware.
Features
Notes and tasks with a Markdown editor, sub-tasks, milestones, and kanban project workspaces. AI chat with streaming responses, RAG over your notes, and tool use (web search, calendar, weather). A daily briefing that digests your tasks, RSS feeds, and weather on a schedule. Knowledge graph, per-user/group sharing, PWA with push notifications, an MCP server for external AI clients, and an Android companion app.
Quick Start
Prerequisites: Docker and Docker Compose. 8 GB+ RAM recommended for LLM inference.
Download docker-compose.quickstart.yml from this repo, then:
# Optional but recommended — set a secret key
export SECRET_KEY=your-random-secret-here
docker compose -f docker-compose.quickstart.yml up -d
Open http://localhost:5000. The first user to register becomes admin. Go to Settings → General to pull an LLM model — qwen3:8b or llama3.1:8b are good starting points.
GPU: Ollama runs CPU-only by default. See the comments in
docker-compose.quickstart.ymlto enable NVIDIA GPU passthrough.
Development: To build from source, see Development.
Documentation
| Doc | Contents |
|---|---|
| Architecture | Stack, design decisions, data models, key services |
| Configuration | Environment variables, Docker Compose, production setup, security |
| Features | Detailed feature breakdown and keyboard shortcuts |
| Development | Dev workflow, CI/CD, migrations, release process |
| API Keys & MCP | API key management and Fable MCP install guide |
| SSO / OAuth | OIDC setup for Authentik, Keycloak, and other providers |
| API Reference | All REST API endpoints |
| Android App | Flutter companion app architecture and feature status |
License
This project is privately maintained.