A self-hosted, database-less note-taking web app where a note's title is its path — built for Obsidian vaults and nested markdown trees.
globnotes is a fork of flatnotes by Adam Dullage. flatnotes deliberately keeps every note in one flat directory; globnotes keeps everything else about its spirit (zero-config, single container, distraction-free) and changes one thing: notes can live in subdirectories, and a note's title is its relative path.
data/
├── dad/
│ ├── recipes/
│ │ └── soup.md → note at /dad/recipes/soup
│ └── assets/
│ └── broth.jpg → served at /dad/assets/broth.jpg
└── ideas.md → note at /ideas
globnotes renders the markdown features Obsidian users rely on — and the set grows over time.
Supported today:
- Wikilinks
[[wikilink]]with|aliasand#headinglinks - Image and file embeds
![[image.png]],![[doc.pdf]] - Callouts
> [!note],> [!warning], … - Hidden comments
%%comments%% - Highlight
==mark== - Mermaid diagrams
- YAML frontmatter (title, aliases, and more)
- KaTeX math
- Automatic URL linking
Not yet (roadmap): note transclusion (![[Note]]), block references/transclusion, task lists, footnotes, abbreviations/definition lists/emoji/sub/superscript, Dataview-style queries, and graph view.
Markdown is supposed to be app-independent. If your notes already live in folders — an Obsidian vault, a git repo, a Syncthing share — globnotes gives you a clean web view (and editor) over exactly that structure, without flattening anything. Mount whatever you like as subdirectories:
volumes:
- /srv/dad-notes:/data/dad
- /srv/mom-notes:/data/momFolders are never "managed": creating a/b/c makes the directories, renaming a/b → x/y moves the file, and empty directories are pruned away (git-style).
- Real-path notes — the URL path IS the vault path:
/dad/recipes/soupis the note,/dad/assets/broth.jpgis its image. Relative links (,[x](../other.md)) work exactly like in Obsidian — no rewriting, no magic. - Obsidian-flavored rendering —
[[wiki-links]](with|aliasand#heading),![[image embeds]],==highlights==,> [!callouts],%%comments%%, YAML frontmatter, mermaid diagrams, KaTeX math. - Sidebar folder tree — Obsidian-style nested tree with expand/collapse, active-note highlight, filter textbox, and hover shortcuts to folder views.
- Search modal — one modal that jumps and searches: fuzzy-match a note by display title, alias, or path and open it, or take the pinned footer row into the full-text page. Matched characters are highlighted inline in the title/path of each row (filename hits outrank folder hits); top 10 results in a sticky footer'd list. top 9 results, every one with a
Ctrl/Cmd+Nshortcut;Ctrl/Cmd+Enteropens the full search with the query. Type#to complete tags; empty query shows this session's recently-opened notes. - 16 themes — light, dark, system auto-detect, Catppuccin, Dracula, Gruvbox, Nord, Solarized, Tokyo Night, and more. Pick one in the menu, preview it behind the panel, no page reload.
- Full-text search and
#tagsacross the whole tree, scoped to a folder or recursive, with real-time filtering. - New-note flow —
Untitled Nprefills in the current folder context; dead wiki-links offer a one-click "Create note" affordance. - Attachment-aware renames — moving a note across folders prompts you to move its referenced files along too, or just fix the links.
- First-run setup wizard — no auth env vars? globnotes asks on first launch: set a password or explicitly disable auth. Each choice is deliberate.
- Agent-friendly — raw markdown and files over plain HTTP (see below).
docker run -d \
--name globnotes \
-p 8080:8080 \
-v /path/to/your/notes:/data \
alexindigo/globnotes:latestOpen http://localhost:8080 and complete the first-run setup: create a password, choose read-only (browse and search, no editing), or disable auth entirely (trusted networks only).
Or with docker compose:
services:
globnotes:
image: alexindigo/globnotes:latest
container_name: globnotes
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./notes:/data
# Optional: mount additional sources as subdirectories
# - /srv/dad-notes:/data/dad
# - /srv/mom-notes:/data/mom
environment:
# Optional. Leave unset for the first-run setup wizard.
# GLOBNOTES_AUTH_TYPE: "none" # trusted home network only!The image is published to both Docker Hub (alexindigo/globnotes) and GHCR (ghcr.io/alexindigo/globnotes) — same tags (latest, 1.0, 1.0.1, …), same content; use whichever registry you prefer.
| What | URL |
|---|---|
| Notes | /dad/recipes/soup — the whole root space |
| Vault files | /dad/assets/broth.jpg — same tree |
| App pages | /_/login, /_/new, /_/search |
| API, health, swagger | /_/api/* |
| Built assets | /_/assets/* |
The only reserved top-level segment is _ — don't name a vault folder that. Everything else is yours.
GLOBNOTES_PATH_PREFIX is respected for multi-instance deployments (e.g. one instance at /dad/ and another at /mom/ behind one host) — note pages, files, API and assets all live under the prefix, and relative links keep working.
| Variable | Default | Description |
|---|---|---|
GLOBNOTES_PATH |
/data (in container) |
Root directory of the notes tree. Required outside docker. |
PUID / PGID |
1000 / 1000 |
User the app runs as (container). Set to your host user's ids (id -u / id -g) so note edits can write. globnotes never chowns your vault — it only creates/owns the .globnotes index dir. |
GLOBNOTES_INDEX_BATCH_SIZE |
200 |
Notes indexed per commit batch during the initial background sync. Lower it on very constrained hosts. |
GLOBNOTES_INDEX_BATCH_DELAY |
0.1 |
Seconds to sleep between index batches (CPU throttle). 0 disables. |
GLOBNOTES_SCAN_CACHE_TTL |
15 |
Seconds the vault file listing is cached (large vaults: raise it). |
GLOBNOTES_AUTO_ENABLE_PLUGINS |
true |
Default for new plugins in the settings UI (per-browser switches override). |
GLOBNOTES_RENDER_WORKERS |
2 |
Sandboxed Deno Workers per plugin for rendering (heartbeats + auto-respawn). |
GLOBNOTES_AUTH_TYPE |
(unset → first-run wizard) | none, read_only, password or totp. Env always wins over the wizard's stored choice. |
GLOBNOTES_USERNAME / GLOBNOTES_PASSWORD |
— | Login credentials (for password/totp). If unset, taken from the wizard's stored config. |
GLOBNOTES_SECRET_KEY |
— | JWT signing key. If unset, taken from the wizard's stored config. |
GLOBNOTES_TOTP_KEY |
— | TOTP secret (for totp). |
GLOBNOTES_SESSION_EXPIRY_DAYS |
30 |
Login session length. |
GLOBNOTES_HOST / GLOBNOTES_PORT |
0.0.0.0 / 8080 |
Listen address (container). |
GLOBNOTES_PATH_PREFIX |
— | Serve under a sub-path, e.g. /mom (multi-instance reverse proxies). |
GLOBNOTES_QUICK_ACCESS_* |
— | HIDE, TITLE, TERM, SORT, LIMIT for the home page quick-access section. |
GLOBNOTES_BRAND_NAME / GLOBNOTES_BRAND_ACCENT |
— | White-label branding (see Branding). Env wins over the Branding dialog's stored values. |
GLOBNOTES_AUTH_TYPE=none turns globnotes into a home-wide knowledge source: anyone (and any agent) on the network can read and write. read_only is the middle ground — open browsing, no writes ("family wiki; editing happens in Obsidian"). Either way, everything in the tree becomes reachable, so keep it to networks you trust. A warning is logged at startup when auth is off.
With token auth (or no auth at all), your notes are plain HTTP:
# Raw markdown
curl -H "Authorization: Bearer $TOKEN" https://notes.example/_/api/files/dad/recipes/soup.md
# Search (add folder=dad/recipes to scope to a subtree, nested=false for root-only)
curl -H "Authorization: Bearer $TOKEN" "https://notes.example/_/api/search?term=soup"
# Drop a file into a vault
curl -H "Authorization: Bearer $TOKEN" \
-F "file=@photo.jpg" -F "directory=dad/recipes" \
https://notes.example/_/api/files- Rename
FLATNOTES_*env vars toGLOBNOTES_*(same names otherwise). - Your
/dataworks as-is: flat notes keep their titles, and the index is rebuilt automatically (.globnotesreplaces.flatnotes; both are hidden and safe to delete). - The special
attachments/directory is gone as a concept — existingattachments/x.jpglinks keep working (it's now just a directory, served like any other). New uploads land beside the note being edited.
Rendering is a markdown-it pipeline extended by plugins — each running in its own permission-narrowed Deno Worker (no network/env/write unless the manifest asks). The built-ins (globnotes-autolinks, -callout, -comments, -embeds, -mark, -mermaid) produce the Obsidian-flavored rendering out of the box; drop your own into <vault>/.globnotes/plugins/<id>/ and they join the pipeline. Per-plugin switches and the auto-enable default live in the menu → Plugins dialog. See docs/plugins.md for the authoring guide.
Components communicate over a mitt-based client-side event bus — publishers announce facts like "note renamed" or "theme changed," consumers decide what to refresh. 59 topics cover note lifecycle, sidebar, menus and modals, edit sessions, themes, plugins, files, search, and the editor action channel (editor:* / app:*). See docs/event-bus.md for the topic registry and usage guide.
Keyboard shortcuts are a switchable layer — navbar menu → Keybindings opens a panel whose left rail picks one of five named layers (Legacy Flatnotes, Obsidian, Notion, Typora, VS Code-lite) plus a Custom layer, with a cheat sheet of the active layer's bindings beside it.
- Cross-layer constants:
Ctrl+Alt+N(new note) andCtrl+Alt+H(home) work everywhere — every app's own new-note key is browser-reserved. - Save: Legacy keeps
Ctrl/Cmd+Enter; the other layers useMod+SwithCtrl/Cmd+Enterkept as an alias. Legacy also restores Flatnotes'/for the quick switcher. - Honest no-ops: layers only remap actions globnotes has — Notion's
/(slash menu) or Obsidian'sMod+P(command palette) are bound but do nothing. - Both editors, one channel: keys reach the editors as
editor:*actions on the event bus, so source mode and WYSIWYG handle the same layer — formatting actions that WYSIWYG applies natively are applied in source mode as markdown-syntax edits (Typora-style). The layer also fixes latent keymap conflicts:Mod+Entersaves instead of inserting a blank line,Mod+Itoggles italic, and Escape collapses a multi-cursor selection before exiting. - Custom layer: starts as a copy of Legacy; in the cheat sheet, click any binding and press a new key to remap it (marked entries reset with ↺). Overrides are stored per-browser in
localStorage, same as the theme choice.
Make an instance yours without touching code: navbar menu → Branding sets the brand name, an accent color, and uploads a logo / icon (SVG, PNG, JPG, WebP, GIF or ICO). Everything lives inside the vault — name and accent in .globnotes/config.json, files in .globnotes/brand/ — so branding travels with the vault like everything else.
- The accent recolors the Globnotes Light/Dark themes; themes with a brand color of their own keep it.
- The brand name replaces "globnotes" in the browser tab, the web manifest, and the navbar wordmark.
- A custom
icon.svgdoubles as the browser-tab favicon; drop dedicated favicon files to go further.
Favicon files are file-drop only — no dialog fields. Drop them into <vault>/.globnotes/brand/:
| File | Replaces |
|---|---|
favicon.ico |
the shortcut icon |
favicon-16x16.png / favicon-32x32.png |
tab favicons |
apple-touch-icon.png |
iOS home-screen icon |
safari-pinned-tab.svg |
Safari pinned tab |
site.webmanifest |
the PWA manifest (generated from the brand config when absent) |
GLOBNOTES_BRAND_NAME / GLOBNOTES_BRAND_ACCENT (#rrggbb) are the env equivalents; env always wins over the dialog.
See FutureDevelopment.md — note transclusion, unresolved-link styling, backlinks/graph, compat hats, and more.
# Install everything (all dependencies resolve from deno.json)
deno install
# Server — tests, lint, type check
deno task test # 121 integration + unit tests
deno task lint
deno task check
# Client build & unit tests (via Deno npm compat — no npm/node required)
deno task build:client
deno task test:client
# Client dev server (Vite under Deno)
deno task dev:clientglobnotes is a fork of flatnotes by Adam Dullage, who built the excellent foundation this project stands on. GNU Lesser General Public License v3.0 licensed (see LICENSE); upstream flatnotes code remains under the MIT License (see THIRD-PARTY-NOTICES.md). Full attribution for upstream, dependencies, and the community that shaped the design lives in THIRD-PARTY-NOTICES.md.
