Tefter

Self-hosted, single-user notes with the classic Notational Velocity interaction model: one omnibar that searches and creates, instant filtering on every keystroke, no save buttons, no dialogs, everything on the keyboard. Markdown editing with preview, offline-first sync across Linux/macOS/Windows/Android/web.

One Go binary (tefterd) + one SQLite file. The web app (PWA) is embedded in the binary; the desktop app is the same frontend in a Wails shell.

Quick start (server)

make server            # builds frontend + tefterd into build/tefterd
./build/tefterd init   # creates tefter.db, prints your auth token — save it
./build/tefterd        # serves on :8420

Open http://localhost:8420, click → set server URL + token → Save.

Keyboard model

Key Action
type in omnibar filter notes instantly (title > prefix > contains > body; diacritic-insensitive)
Enter open selected note / create note titled with the query
move list selection (editor live-previews)
Esc editor → omnibar; omnibar → clear
Ctrl/Cmd+L focus omnibar, select text
Ctrl/Cmd+Delete delete note (undo toast, no confirm)
Ctrl/Cmd+Shift+P toggle markdown preview ([[wiki-links]] open/create notes)
Ctrl/Cmd+K cycle tag filter
Ctrl/Cmd+J / +Shift next / previous note while editing

CLI

tefterd                              # serve (default), flags: --listen :8420 --db tefter.db
tefterd init                         # create db + print token
tefterd token rotate                 # new token (old one stops working)
tefterd import simplenote export.zip # idempotent Simplenote import (also in the web UI)
tefterd compact --days 90            # purge old tombstones
tefterd backup /backups/tefter.db    # consistent snapshot (VACUUM INTO)
tefterd version

Environment: TEFTER_DB, TEFTER_LISTEN override the flag defaults. No config file.

Deployment

tefterd listens on plain HTTP; put TLS in front of it.

systemd/etc/systemd/system/tefterd.service:

[Unit]
Description=Tefter notes server
After=network.target

[Service]
User=tefter
ExecStart=/usr/local/bin/tefterd --db /var/lib/tefter/tefter.db --listen 127.0.0.1:8420
Restart=on-failure

[Install]
WantedBy=multi-user.target

Caddy (two lines):

notes.example.com {
    reverse_proxy 127.0.0.1:8420
}

Backup = one file:

tefterd backup /backups/tefter-$(date +%F).db --db /var/lib/tefter/tefter.db

Clients

  • Web / Android: open the server URL, install as PWA (Chrome: “Add to home screen”). Fully offline-capable; edits sync on reconnect.
  • Desktop: tefter-desktop (Wails). Single instance, closes to tray, global shortcut Ctrl+Shift+Space (change with --hotkey / TEFTER_HOTKEY; --quit-on-close disables the tray behavior). Works with no server at all — configure sync later under .

Sync model

Pull-then-push, last-write-wins with conflict copies (Simplenote-style). Concurrent edits of the same note never silently overwrite: the loser comes back as a new note tagged conflict with “(conflicted copy …)” in its title. Delete-vs-edit: the edit wins. Every client keeps a full offline copy in IndexedDB; a crash mid-sync loses nothing.

Development

make dev        # vite dev server (proxies /api to :8420)
make test       # go vet + go test (uses notes.zip for a real-import test when present)
make server     # build/tefterd for this machine
make server-all # linux/amd64, linux/arm64, darwin/arm64, windows/amd64
make desktop    # Wails build (needs wails CLI + GTK/WebKit dev packages on Linux)

CI / Releases (Gitea Actions)

.gitea/workflows/ci.yml runs tests on every push and, on a v* tag, builds tefterd for all four platforms plus the Linux desktop app and attaches them to a Gitea release. Requirements: Actions enabled on the repo and one Linux runner whose ubuntu-latest label maps to a Debian/Ubuntu-based image (the default catthehacker/ubuntu:act-latest works).

git tag v0.1.0 && git push origin v0.1.0   # cut a release

macOS/Windows desktop shells need a native machine: install Go + Node + the Wails CLI there and run make desktop (the result lands in desktop/build/bin/). The server binaries for those platforms come out of the Linux runner via cross-compilation.

Repo layout: frontend/ (Svelte + TS, all app logic incl. offline store + sync engine), server/ (Go, stdlib HTTP + modernc.org/sqlite, no CGO), desktop/ (Wails shell).

Description
No description provided
Readme 211 KiB
v0.2.0 Latest
2026-07-26 17:29:26 +02:00
Languages
Go 41.8%
Svelte 31%
TypeScript 15.8%
JavaScript 5.8%
CSS 1.8%
Other 3.8%