nodummd/nodum
nodummd/nodum has 39 GitHub stars and 8 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-15 06:35 UTC. Last push 2026-11-13.
Measured star growth
- in 24h
- 0
- in 7 days
- Collecting history
- in 15 days
- Collecting history
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
Open-source, web-based knowledge base — Obsidian-style linked markdown notes, backlinks and a GPU-rendered knowledge graph. FastAPI + Next.js, multi-tenant and self-hostable, with real-time collaboration, canvas, publishing and a Web Clipper.
Web & AppsREADME.md
Original
Nodum
An open-source, web-based knowledge base.
Linked markdown notes, backlinks, and a GPU-rendered knowledge graph — everything
Obsidian does in core, in the browser: multi-tenant, self-hostable, no lock-in.
nodum — Latin for "knot, node." Notes are the knots; the value is in the rope between them.
Why
Obsidian is a superb local-first markdown knowledge base built on
plain files, [[wikilinks]], backlinks and a graph view. It is also closed-source and
desktop-first.
Nodum is the open-source web equivalent. Sign up, create vaults, write linked markdown, and explore your thinking as a living graph — from any browser, on any machine. Your notes stay plain markdown the whole time: drop in an Obsidian vault as a zip, get a folder-true zip back whenever you want to leave.
Status — main is the released line (v3.2.0); dev is integration. See
Coming next for what is landing in the next release.
Features
✍️ Writing
- Live Preview editor — CodeMirror 6, the same engine Obsidian uses. Syntax hides as
you write and reveals at the cursor: sized headings, bold/italic/strike,
==highlights==, inline code, clickable task checkboxes, accent bullets, code fences. - Three modes — Live Preview, raw Source, and Reading view (⌘E cycles).
- Callouts — all 13 Obsidian types plus aliases, with icons, colours and folding, in both Live Preview and Reading view.
- Rich markdown — KaTeX math (inline and block), Mermaid diagrams, GFM tables, footnotes, blockquotes, syntax-highlighted code blocks.
- Attachments — paste or drag-and-drop straight into the editor; files land in
S3/MinIO and embed as
![[image.png]], with hover preview cards (images inline, PDFs scrollable). 5 MB cap, magic-byte MIME verification, no.svg/.html. - Autosave — debounced, with optimistic-concurrency conflict detection.
🔗 Linking & the graph
- Wikilinks everywhere —
[[Note]],[[path/Note]],[[Note|alias]], autocomplete on[[, click-to-follow. Clicking an unresolved link creates that note instantly and the backlink resolves on the spot. - Backlinks pane — linked mentions with context snippets, unlinked mentions (plain-text occurrences of the title), and outgoing links.
- GPU knowledge graph —
@cosmos.gl/graphon WebGL2: force-simulated and draggable, node size by degree, ghost nodes for unresolved links (click to create), search and tag filters, live force sliders, floating labels that fade with zoom. Global and local graph, with a guard so a lost WebGL context can never take the workspace down. - Semantically related notes — pgvector cosine similarity over note embeddings, surfacing connections you never linked by hand.
🔍 Finding
- Full-text search — Postgres FTS (tsvector + GIN) with
path:file:tag:operators, quoted phrases and-exclusions. - Quick switcher (⌘O) — fuzzy title jump that creates the note when nothing matches.
- Command palette (⌘P) — every workspace command, searchable.
- Tags — inline
#tagsand frontmatter, nested (#a/b) with prefix matching, tag pane with counts, click through to search. - Bookmarks, outline pane, and per-pane back/forward navigation history.
🗂️ Organising
- Vaults and folders — many vaults per user, nested folders, drag-and-drop moves, and an Obsidian-parity context menu (open in new tab / to the right / new window, duplicate, move, merge, bookmark, copy path, version history, rename, delete).
- Folder colours inherited down the tree.
- Daily notes & templates — configurable date format, folder and template, with
{{title}}{{date}}{{time}}{{date:FORMAT}}variables. - Version history — every save snapshots server-side; browse and restore.
- Canvas — freeform whiteboard boards alongside your notes.
- Import / export — Obsidian-compatible vault zips both ways (with root-unwrapping,
.obsidianconfig detection,.txt/.markdownsupport and PDF text extraction), or import a folder directly. Links resolve across the whole import batch.
👥 Sharing & collaborating
- Real-time collaboration — Yjs CRDT over websockets (
pycrdt), per note, with presence. - Publish — public share links per note and a public site view per vault.
🧭 Getting your bearings
- First-run tour — a spotlight walk through the real interface, skippable, re-runnable from Help.
- Demo Workspace — 200 linked notes with coloured folders and graph groups, one click, so you can explore before you write.
- Documentation at
/docs— every button and panel explained, with screenshots captured from the app itself.
🤝 Editing together
- Live collaboration that works across API workers — one shared document seed, presence everywhere, your own undo.
- Tables you type into: cells, rows and columns, paste a grid from a spreadsheet, move rows, per-cell undo.
🤖 AI, your key
- An assistant you bring your own key for — Claude, OpenAI, Gemini or Qwen, encrypted at rest — that searches, reads, creates and extends notes in your vault, streams its replies, keeps per-vault chat history, and can use a different key per vault.
🧩 Extending
MCP server — Nodum speaks the Model Context Protocol: point Claude Code, Claude Desktop or Cursor at
/api/v1/mcpwith a token and the AI can create vaults, write and link notes, colour folders, search, import and export — 36 tools, same rules as the app, progress on long imports. A stdio bridge lives inpackages/nodum-mcp.Public REST API — everything above for programs you write: scoped API keys (read / write / delete / ai) from Settings → API keys, plain JSON at
/api/public/v1(notes, search, links & unlink, tags, graph, attachments, AI ask), and an interactive Scalar reference with a try-it client at/api-reference.Plugins — a capability-scoped API inside an opaque-origin sandboxed iframe (
sandbox="allow-scripts", noallow-same-origin,connect-src 'none'), permission checks enforced host-side, registry UI in Settings.Web Clipper — an MV3 Chrome extension in
clipper/, backed by scoped, hashed, revocable tokens that can only create notes — revoking one never touches your session.Versioned REST API at
/api/v1with OpenAPI docs at/docs.
🔐 Platform
- Multi-tenant auth — argon2id hashing, JWT access + refresh rotation with reuse defence (and a grace window so racing tabs don't log you out), httpOnly first-party refresh cookie, rate-limited auth endpoints.
- Email verification — six-digit code on signup, stored as an HMAC and never in
the clear, with attempt limits and a resend cooldown. Delivery walks an ordered
provider chain (Brevo → Mailjet → Resend → Mailgun → any SMTP relay), which both
survives one provider's bad hour and stacks their free tiers. Outside production
nothing is mailed and the code is always
123456. - Account recovery and closure — forgotten-password reset by mailed code (which drops every existing session, since that is what a reset is for), password change from settings, and account deletion behind its own emailed code, taking the vaults, notes and stored files with it. Codes are scoped by purpose, so a reset code cannot delete an account.
- Built to scale — async I/O end to end, Redis caching with targeted invalidation, Celery for import/export and background scans, WebGL rendering, CDN-friendly frontend.
- Operationally honest — structured logging, request-id middleware, security headers,
CORS, deep
/health, and an API that refuses to boot on placeholder secrets.
Stack
| Layer | Tech |
|---|---|
| Backend | FastAPI · SQLAlchemy 2 (async) · PostgreSQL 16 + pgvector · Redis 7 · Celery (+beat) · MinIO |
| Frontend | Next.js 16 · React 19 · Tailwind v4 · CodeMirror 6 · @cosmos.gl/graph · shadcn/radix · TanStack Query · Zustand |
| Realtime | Yjs · pycrdt over websockets |
| Deploy | Docker Compose (dev / test / staging / prod) + Caddy edge proxy with automatic TLS — Docker or Podman |
Quick start
git clone [email protected]:nodummd/nodum.git
cd nodum/deploy
cp .env.example .env # then edit the values (every one is commented)
./compose.sh dev up -d # postgres + redis + minio + api + web
- Web → http://localhost:3000 · API docs → http://localhost:8000/docs
Or from the repo root: make dev-up, make dev-logs, make dev-down
(make help lists everything).
The repo ships a nodum CLI in cli/ — install it with
pip install -e cli (or uv pip install -e cli) for editable dev use, then
run nodum stack up / nodum stack down / nodum stack ps /
nodum stack logs -f api / nodum stack exec api <cmd...> /
nodum stack migrate / nodum stack clean instead of calling compose.sh
directly.
nodum stack up --env prod targets production (needs deploy/.env.prod);
nodum stack up --no-build skips the image rebuild. Run nodum stack --help
for the full command list.
Self-hosting
cp deploy/.env.prod.example deploy/.env.prod # fill it in, then chmod 600
cd deploy && ./compose.sh prod up -d --build
./smoke.sh https://your-domain # signup → note → attachment round-trip
The stack is fully containerised, edge proxy included. Caddy terminates TLS
(certificates provisioned and renewed automatically), proxies /api to the API and /s3
to MinIO for presigned attachment URLs, and is the only container that binds a host port —
Postgres and Redis aren't even on its network. Schema changes run in a one-shot migrate
container that everything else waits on, so no two containers ever race Alembic.
Staging is the same stack, from the same compose layer, with its own credentials and ports so it can share a host:
cp deploy/.env.staging.example deploy/.env.staging
cd deploy && ./compose.sh staging up -d --build
The API refuses to start on placeholder or known-default credentials, in staging as well
as production. Deployment and backup/restore runbooks live in
docs/deploy.md and docs/backup.md.
Testing
cd back && uv run pytest tests -q # backend (bring infra up first)
make e2e-up # bring up the stack the e2e suite needs
make e2e # Playwright (make e2e-up again after `make verify`)
v3.2.0 ships green with 87 backend tests (70 integration + 17 unit) and 77 Playwright end-to-end tests, plus a full production-compose boot check that drives signup → graph → export through the real prod proxy chain. All of it runs in CI on every push, alongside gitleaks secret scanning.
Repository layout
back/ FastAPI backend — app/{api,services,models,schemas}, alembic, tests
web/ Next.js frontend — app router, workspace UI, CM6 editor, graph, Playwright e2e
clipper/ MV3 Chrome extension (Web Clipper)
deploy/ compose stacks (dev/test/staging/prod), Caddy config, smoke test, .env examples
docs/ deploy & backup runbooks, collab notes, Obsidian-parity research specs
tasks/ master plan, audit & roadmap, backlog — the decision documents
Roadmap
Tracked in tasks/nodum-audit-and-roadmap.md and
tasks/nodum-master-plan.md (the single source of truth for
project state). Near-term: the AI assistant release, a properties/frontmatter editing UI,
live-preview math, and trash/restore.
Contributing
- Branch model —
main(production) ←dev(integration) ← work branches, merged back with--no-ff. Branches are named<kind>/<N>.<slug>_<contributor>_<DDMMYYYYHHMM>—<kind>isfeature,hotfix,choreorbug;<N>numbers the branch in its chain; the timestamp is 24-hour — and are cut as a chain: the first fromdev, each next from the previous one (dev → feature/1.demo-workspace_maqbool_190820260150 → feature/2.onboarding_maqbool_190820260430). Conventional commits. - Green before commit —
uv run pytest tests/unitanduv run ruff check .for the backend;npm run buildandnpm run lintfor the web app. - Never commit secrets.
.envis gitignored; only.env.examplewith placeholders ever lands in the repo, and CI runs gitleaks on every push.
Backend keeps routers thin and services fat (the ServiceResponse.unwrap() pattern),
responses are {"data": ...} and errors {"error": {"code", "message"}}; migrations are
numbered (0001_...). Frontend keeps workspace state in Zustand, server data in TanStack
Query, and all API access behind web/src/lib/api/.
Contributors
![]() Maqbool Thoufeeq T Creator & maintainer @maqboolthoufeeq |
![]() Zius The nodum CLI
@ziuus |
Dependency bumps arrive on their own from Dependabot, and every one still goes through CI and review like any other pull request.
The table above is generated from the repository's own contributor list by
scripts/update-contributors.mjs, so it
never has to be remembered: merge something and your avatar appears here on the
next run. The one thing an API cannot know is what someone worked on, so those
lines live in .github/contributor-roles.json
— and a contributor without an entry is still shown, just without the line.
Pick something off the roadmap or open an issue first if the change is large, read Contributing for the branch model and the pre-commit gate, and send a pull request. Questions reach the maintainer at [email protected].
License
MIT © 2026 Maqbool Thoufeeq T

