Local-first Markdown notes with a JSON API and a fast web UI.
License · API · Quick Start
- Local notes stored as plain
.mdfiles - Fast tree navigation, split view editing, and live preview
- Tags, mentions, and task parsing with project, mention, due date, and priority markers
- Daily notes support with date picker and templates
- Sheets with
.jshstorage and CSV import/export - Global scratch pad modal stored as
scratch.md(hidden from the tree) - Journal feed stored in
journal/journal.jsonwith inline edit, delete, and archive - Command palette with built-in actions and optional external commands file
- JSON API for all note and folder mutations
- Zero database dependency; the filesystem is the source of truth
Scoli is built for people who want their notes to live on disk, be readable with any editor, and still have a modern web UI. It keeps operations simple: create a file, edit the file, and the app reflects it immediately.
go run ./cmd/scoli serve --notes-dir ./Notes --port 8080Open http://localhost:8080.
Use Ctrl+Alt (Windows/Linux) or Ctrl+Option (macOS):
- Ctrl+Alt+S: Save
- Ctrl+Alt+E: Edit view
- Ctrl+Alt+V: Preview view
- Ctrl+Alt+B: Split view
- Ctrl+Alt+D: Open today's daily note
- Ctrl+Alt+C: Open date picker
- Ctrl+Alt+P: Open scratch pad
- Ctrl+Alt+J: Open journal
- Ctrl+Alt+K: Open command palette
- Ctrl+Alt+I: Open inbox
Press Ctrl+Alt+K (Ctrl+Option+K on macOS) to open the command palette. Use >
to search notes by filename or content (example: > meeting notes).
You can extend commands by setting externalCommandsPath in Notes/settings.json
to a JSON file relative to the notes directory, for example commands.json:
[
{
"label": "Open Scratch",
"keywords": ["scratch"],
"action": "open-note",
"args": { "path": "scratch.md" }
}
]Scoli can send a daily digest and task-due notifications via SMTP (for example, Gmail with an app password).
Configure in Settings → Email:
- Enable notifications
- SMTP host/port/username/app password
- From/To address
- Digest time and Due time
- Send Test Email to verify the setup
Settings are stored in Notes/email-settings.json (created on first access).
Templates live under Notes/email/:
email/digest.templateemail/due.template
Both templates support simple tokens:
{{date}}{{tasks_overdue}}{{tasks_today}}{{tasks_upcoming}}{{tasks_by_project}}{{notes_summary}}{{completed_yesterday}}
To keep secrets out of git, add Notes/email-settings.json to your ignore
rules if your notes directory is tracked.
If you expose the web UI publicly, set a password to gate the UI routes:
export NOLDERMD_UI_PASSWORD="your-password"
export NOLDERMD_UI_COOKIE_SECRET="replace-with-a-long-random-string"When NOLDERMD_UI_PASSWORD is set, the UI is protected by a login page and a
signed session cookie. If NOLDERMD_UI_COOKIE_SECRET is omitted, sessions are
still signed but will reset on server restart.
go build ./cmd/scoli
./scoli serve --notes-dir ./Notes --port 8080docker run --rm -p 8080:8080 sottey/scoli:<version>services:
scoli:
image: sottey/scoli:<version>
ports:
- 8083:8080
user: "1000:1000"
volumes:
- /path/to/notes/folder:/notes
networks: {}The image includes a Tutorial folder. If you mount an empty /notes volume,
Scoli will copy the tutorial notes into it on first start.
Example with a local notes folder:
docker run --rm -p 8080:8080 -v "$HOME/scoli-notes":/notes sottey/scoli:0.1.2Note for Colima on macOS: prefer a path under your home directory (for example,
$HOME/scoli-notes). Colima does not share /tmp by default, which can lead
to permission errors when the container tries to seed notes.
docker build -t scoli:local .services:
scoli:
image: scoli:local
ports:
- 8083:8080
user: "1000:1000"
volumes:
- /path/to/notes/folder:/notes
networks: {}The user entry keeps file ownership aligned with your host user so the app can
create notes and templates inside the mounted /notes folder.
Build metadata (git tag, Docker tag, commit SHA) is injected by the release
workflow and surfaced in Settings. Local builds will show "Unknown/Custom"
unless you pass build args (see docs/RELEASE.md).
Scoli ships as an installable PWA. This gives you a dockable window, offline UI shell, and app shortcuts in supporting browsers.
- Open
http://localhost:8080in a Chromium-based browser or Orion. - Use the browser menu: "Install This Site As An App" / "Install App".
Installed apps expose shortcuts for:
- Inbox (
/?shortcut=inbox) - Daily note (
/?shortcut=daily) - Tasks (
/?shortcut=tasks)
The UI shell is cached for offline launch. If the API server is unavailable, you will see an offline screen with a Retry button.
When a new build is available, an update banner and toast appear. Reload to apply the update.
The MCP server lives in mcp/ and exposes the Scoli API as MCP tools and
resources (stdio transport).
cd mcp
go run ./cmd/scoli-mcp --api-base-url http://127.0.0.1:8080/api/v1To run over HTTP:
cd mcp
go run ./cmd/scoli-mcp --api-base-url http://127.0.0.1:8080/api/v1 --transport http --listen 0.0.0.0:8090There is a separate compose file that runs Scoli and the MCP server together:
docker compose -f mcp/compose.yml up --build- Notes are
.mdfiles under your notes directory. - New note creation appends
.mdif missing. - Files beginning with
._are ignored.
Tasks are parsed on the fly from note contents. A task line looks like:
- [ ] Call Mom +Home #family @alice >2025-01-31 ^2
Markers:
#tagtags (case-insensitive)+projectsingle project (first match wins)@mentionmention>duedue date (multiple formats supported)^prioritypriority 1-5
Special tag:
#somedayremoves a task from Today and surfaces it under Someday
Tags are shown in the sidebar and in the note preview bar for quick navigation.
If a folder contains default.template, new notes created in that folder start
with that content. Templates support date/time placeholders and simple
conditionals based on the note name.
Placeholders:
{{date:YYYY-MM-DD}},{{time:HH:mm}},{{datetime:YYYY-MM-DD HH:mm}}{{day:ddd}}or{{day:dddd}},{{month:YYYY-MM}},{{year:YYYY}}{{title}},{{path}},{{folder}}
Conditionals:
{{if:day=sat}}
- [ ] Saturday review
{{endif}}
- Split view (edit/preview) with a draggable divider
- Tag pills in the preview bar open filtered tag views
- Tasks view includes Today, Someday, Task Filters, project groups, No Project, Completed, and All
- Task Filters are stored in
Notes/task-sets.jsonand selectable from the Task Filters view - Daily notes open from the header date pill or the date picker
- Daily notes show a read-only journal panel with links to edit entries in Journal
- AI view (when enabled) provides chat over all notes with source links
- Notes auto-save shortly after changes (debounced)
Settings live in Notes/settings.json (or the mounted notes directory). Key
fields:
darkMode(bool)defaultView(edit,preview,split)sidebarWidthstartOnToday(bool)showTemplatesshowAiNoderootIcons(map of root node keys to icon paths)
AI settings live in Notes/.ai/ai-settings.json (created on server startup).
Set apiKey there to enable AI chat. The AI index and chat history are also
stored under Notes/.ai/.
To keep secrets out of git, add Notes/.ai/ai-settings.json to your ignore
list.
The app exposes a JSON API under /api/v1 for notes, folders, tasks, tags, and
settings. Deep dive docs live at docs/API.md.
Issues and pull requests are welcome. If you are changing behavior, include a clear description and a minimal reproduction.
See LICENSE.

