Duffel is a local-network markdown workspace for people and LLM agents.
It stores notes as ordinary Markdown files, serves them through a small web UI,
indexes them with qmd, and exposes an MCP server so coding agents can search,
read, write, and append journal entries without custom glue scripts.
Screenshots
Notes with related content
Journal history
MCP connector setup
Who this is for
Use Duffel when you want:
- A local notes workspace backed by files you can inspect and version normally.
- A browser UI for reading, editing, archiving, and searching Markdown notes.
- An MCP endpoint that lets LLM tools use the same notes as durable project memory.
- A simple Go + TypeScript app that can run on a laptop, home server, or private network.
Ask an LLM to set it up
Paste this prompt into your coding assistant from a clean machine or server:
Set up Duffel from https://github.com/jibs/duffel.
Requirements:
- Use pnpm, not npm.
- Do not install a global qmd binary; Duffel uses the pinned @tobilu/qmd package from pnpm dependencies.
- Start with a local development run first.
- If this is only a private local demo, run with DUFFEL_AUTH_ENABLED=false.
- If this is a real shared/private-network deployment, keep auth enabled and set DUFFEL_SETUP_TOKEN before first setup.
Steps:
1. Clone the repo and enter it.
2. Run make setup.
3. Create a few sample Markdown files under ./data if the directory is empty.
4. Start the app with make dev for development, or follow ops/deploy/DEPLOY.md for systemd deployment.
5. Open http://localhost:4386.
6. For LLM integration, open http://localhost:4386/#/_mcp and configure the MCP client shown there.
7. Run make ci before proposing any code changes.
Quick start: local demo
Requirements:
- Go
1.26+ - Node.js and pnpm
makecurl
git clone https://github.com/jibs/duffel.git cd duffel make setup mkdir -p data/projects data/research data/journal cat > data/index.md <<'MD' --- type: note --- # Welcome to Duffel This is a filesystem-backed Markdown workspace. Try: - Browse files in the sidebar - Edit this note - Search for `MCP` - Open the MCP Connector page MD cat > data/projects/demo.md <<'MD' --- type: note --- # Demo project Duffel keeps project notes in Markdown and exposes them to LLM agents through MCP. ## Tasks - Capture setup decisions - Store useful research - Append journal updates after changes MD cat > data/research/mcp.md <<'MD' --- type: research --- # MCP notes Use the MCP connector at `#/_mcp` to configure compatible agents. Agents should search first, read only relevant files, and append concise journal entries for durable changes. MD cat > data/journal/worklog.md <<'MD' --- type: journal --- ## 2026-01-01 09:00 Demo: created a sample Duffel workspace. MD DUFFEL_AUTH_ENABLED=false make dev
Open http://localhost:4386.
For a local demo, auth is disabled in the command above. For anything reachable by other people or machines, keep auth enabled and follow the authentication setup below.
Production/private-network setup
Duffel enables auth by default.
Before the first owner setup, set a one-time setup token:
export DUFFEL_SETUP_TOKEN="$(openssl rand -base64 32)" make dev
Open http://localhost:4386/oauth/setup, create the owner account, store the
returned bootstrap token in your secret manager if you need CLI/MCP bootstrap
access, then remove DUFFEL_SETUP_TOKEN from the runtime environment.
For a systemd deployment, use ops/deploy/DEPLOY.md. That guide uses generic hostnames and paths; replace them with your own server values.
LLM and MCP setup
Duffel exposes a streamable HTTP MCP endpoint at /mcp.
In the browser UI:
- Open
http://localhost:4386/#/_mcp. - Create or paste a bearer token if auth is enabled.
- Copy the generated client config for your agent.
Generic MCP config shape:
{
"mcpServers": {
"duffel": {
"type": "streamable-http",
"url": "http://localhost:4386/mcp",
"headers": {
"Authorization": "Bearer ${DUFFEL_MCP_TOKEN}"
}
}
}
}If you started Duffel with DUFFEL_AUTH_ENABLED=false, omit the headers block.
Recommended agent behavior:
- Search before reading broad folders.
- Start with small result limits.
- Read only the files needed for the task.
- Use journal files for concise change history.
- Keep research artifacts under a clear folder such as
research/. - Do not commit secrets, local note data,
.env, generated tokens, or runtime logs.
Configuration
Environment variables:
| Variable | Default | Purpose |
|---|---|---|
DUFFEL_HOST |
empty | Bind address. Empty means all interfaces. |
DUFFEL_PORT |
4386 |
HTTP port. |
DUFFEL_DATA_DIR |
./data |
Markdown workspace root. |
DUFFEL_FRONTEND_DIR |
./src/frontend |
Static frontend directory. |
DUFFEL_AUTH_ENABLED |
true |
Require OAuth/Bearer auth for API and MCP. |
DUFFEL_SETUP_TOKEN |
empty | Required for first owner setup when auth is enabled. |
qmd is provided by the pinned npm dependency @tobilu/qmd@2.1.0; do not
install or depend on a global qmd binary.
API overview
All data APIs are under /api. OAuth and MCP discovery routes are at the root.
Responses are JSON except agent helper script/snippet/version routes and OAuth
HTML forms.
Main routes:
GET|PUT|POST|DELETE /api/fs/*for filesystem-backed notes and directories.POST /api/archive/*andPOST /api/unarchive/*for archive moves.POST /api/journal/*andPOST /api/journal/*/appendfor journal files.GET /api/search?q=<query>for workspace search.GET|POST|DELETE /api/mcp/tokensfor MCP bearer token management.GET|POST /mcpfor the MCP server.GET|POST /oauth/setup,/oauth/authorize,/oauth/token, and/oauth/registerfor auth.
Duffel Lib
Duffel's low-level knowledge workspace capabilities are available as Go
packages under pkg/duffellib.
Use duffellib.Open when another project needs the same safe disk-backed
document behavior as Duffel:
- root-confined path resolution with symlink escape checks
- validated text writes, verified read-after-write, and allowlisted binary image writes
- journal creation and locked journal append
- archive/unarchive operations
- first-class workspace search through
Workspace.Search
Search is part of the library API. The concrete search backend is a Duffel
implementation detail; callers should use duffellib.Workspace and
duffellib/search.SearchOptions rather than shelling out to indexing tools.
Development
Common commands:
make setup # install dependencies make dev # build frontend JS and run the Go server make build # build the backend binary make test # run unit, integration, and e2e tests make lint # run Go and TypeScript linters make fmt # format Go and TypeScript make release-audit # scan tracked files for privacy/secret risks make ci # full local check suite
Before publishing changes, run:
The CI target includes formatting checks, linting, Go vet, TypeScript checks, frontend build, backend tests, e2e tests, and the release audit.
Security model
- Auth is enabled by default.
- First owner setup requires
DUFFEL_SETUP_TOKEN. - API data routes and
/mcprequire Bearer auth when auth is enabled. - OAuth discovery/setup routes and agent helper routes remain public for bootstrap.
- Storage canonicalizes paths and keeps operations under the configured data root.
- Same-origin CORS defaults and mutating cross-origin protections are enforced.
For public or internet-facing deployment, put Duffel behind HTTPS/TLS and normal network controls. Duffel is primarily designed for trusted local or private network use.
Repository hygiene
Tracked source should not include personal note data, generated tokens, .env,
runtime logs, or local machine paths.
Run these before publishing:
make release-audit
trufflehog filesystem --no-update --only-verified .

