Files
tefter/README.md
Senad c389bab46e
All checks were successful
ci / test (push) Successful in 3m8s
ci / release (push) Successful in 11m21s
Add version history with rollback and per-note created/synced info
Server (schema v2, auto-migrates): every accepted overwrite snapshots the
superseded revision into note_history (capped at 50 per note); new
GET /api/v1/notes/{id}/history endpoint; compact purges orphaned history.

Client: notes get a synced_at stamp on every confirmed server exchange;
an info footer under the editor and a Ctrl/Cmd+I panel show created/
modified/last-synced plus the revision list. Restoring a revision applies
it as a normal edit through the sync path, so rollback is non-destructive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:20:19 +02:00

135 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
```sh
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 |
| `Ctrl/Cmd+I` | note info (created / last synced) & version history |
## CLI
```sh
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`:
```ini
[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:
```sh
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.
**Version history:** every time an accepted sync overwrites a note, the server keeps the
previous state (last 50 revisions per note). `Ctrl/Cmd+I` (or the “History” button under
the editor) shows a notes revisions; restoring one applies the old text as a new edit,
so the replaced text itself stays in history — rollback is never destructive.
`tefterd compact` drops history along with the purged notes.
## Development
```sh
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).
```sh
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).