Skip to content

Repository files navigation

Vesper

A voice-journaling PWA. Phone records, a GPU at home transcribes, an Obsidian vault is the only place the words end up.

Private, self-hosted only. It's an actual journal, so there's no public link and no demo instance. Everything below is what you'd need to run your own.

I wanted to journal by talking, one-thumb on my phone, without a third-party app holding my diary or a database I'd have to keep in sync with my notes by hand. The frontend is an installable iOS PWA: it records audio, shows a calendar of which days already have an entry, and queues a failed upload for retry so a bad connection on the road doesn't lose what I just said. The backend is FastAPI running faster-whisper on GPU, and it also serves the built frontend as static files, so the whole thing is one origin with no CORS to reason about.

A finished recording becomes two files: a plain-text transcript and its audio, named NN - H.MMpm.txt and paired, dropped straight into the day's folder in the vault. There is no database. Those files are the only source of truth, indexed into memory when the server starts and rebuilt on demand through POST /api/reindex. I moved off an earlier SQLite design to get here, on purpose: a database that can drift from the vault is a second place for the truth to live, and I'd rather lose the index and rebuild it in a second than reconcile two stores by hand.

Access control lives in the network, not the app. tailscale serve proxies the backend to an HTTPS hostname on a private tailnet, on its own sidecar node so nothing else on the host can hijack the URL. There's no login screen and no API key: being a device on the tailnet is what grants access.

The GPU is a GTX 1080 Ti, and Pascal doesn't support efficient float16. Loading Whisper in the wrong precision crashes on model load, not later, so WHISPER_COMPUTE_TYPE=int8_float32 is pinned as a hard invariant, not a default someone can quietly bump. A watchdog scheduled task keeps the container up unattended and a daily job backs up the transcript index, because the point of a journal is that it's there the next time I open my phone.

CLAUDE.md holds the guidance I give Claude Code when it works in this repo, including the vault schema and that GPU invariant. I develop with agents heavily, and it's the project's memory for this codebase.

Running it

You'll need a machine with an NVIDIA GPU (or CPU, with a smaller model and slower transcription) and Docker, a Tailscale account with HTTPS certificates enabled for your tailnet, and an Obsidian vault folder to bind-mount as the recordings directory.

cp backend/.env.example backend/.env
cd frontend && npm install && npm run build && cd ..
docker compose up -d
tailscale serve --bg 8000
curl http://localhost:8000/health

backend/.env.example carries every setting that matters:

  • WHISPER_MODEL, WHISPER_DEVICE, WHISPER_COMPUTE_TYPE — the model size and the compute type for your GPU generation (float16 on RTX 20-series and newer, int8_float32 on a Pascal card like mine, int8 on CPU).
  • WHISPER_INITIAL_PROMPT, WHISPER_SUBSTITUTIONS — optional priming and a narrow find/replace list for names Whisper still mishears.
  • VAULT_RECORDINGS_DIR — where inside the container your vault's recordings folder is mounted; point it somewhere empty and Vesper starts empty.
  • VAULT_TZ — filenames use local wall-clock time while timestamps are handled in UTC, so the recording timezone has to be explicit.
  • MAX_UPLOAD_MB, RATE_LIMIT_API_PER_MIN, RATE_LIMIT_TRANSCRIBE_PER_MIN — hardening knobs, sane defaults included; no API key is required anywhere.

Open the tailnet HTTPS URL in Safari on your phone and Add to Home Screen. The Windows watchdog and backup task setup is in ops/windows/.

Layout

frontend/       Vite + React + Mantine PWA (build output -> frontend/dist)
backend/        FastAPI + faster-whisper; data lives in the Obsidian vault, not a DB
scripts/        generate_icons.py, programmatic app icons
ops/windows/    watchdog + backup scheduled tasks
docker-compose.yml
SECURITY.md     threat model and hardening notes

Status

Live on my own tailnet, used daily. This is a snapshot of a private working repo, history isn't published here.

License

MIT

About

Voice journal PWA: record on a phone, transcribe on a home GPU, file into an Obsidian vault.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages