Skip to content

Syncing

Franciszek Ryszka edited this page Aug 15, 2026 · 7 revisions

Syncing across computers

(added in v2.2.0)

SnipVault can keep one library in sync across several computers through a small server you host yourself (a homeserver, NAS, or spare PC). Each desktop app keeps its own local database and reconciles with the server, so your snippets are always available — even offline — and every machine converges on the same set.

Syncing is entirely opt-in and per-machine. With no server configured, SnipVault behaves exactly as before: a purely local library, no network, no account.

How it works

Every snippet has a stable id (a uuid) and a last-modified time, so the same snippet is recognised across machines. On each sync the app makes a single request that pushes its records and pulls the server's, and both sides converge:

  • Both directions merge. Snippets that exist on only one side are copied to the other — the result is the union of everything.
  • Newest edit wins. If the same snippet was changed on two machines, the one with the most recent updated_at is kept (the older edit is overwritten). Since v4.0.0, the overwritten version isn't lost: it's saved to that prompt's History, and after the sync a notice tells you how many prompts were updated from another device — so you can open History and recover the earlier text if you need it.
  • Deletes propagate. Deleting a snippet marks it as removed (a tombstone) rather than erasing it; that flag syncs, so the snippet disappears everywhere and can't be silently resurrected by a machine that still had a copy.

Sync runs automatically on app startup (when a server is configured) and on demand via Settings → Sync now. Between syncs you keep working against your local copy.

See Data Storage for the uuid/deleted schema and API and Commands for the /api/sync endpoint and the desktop sync commands.

Connecting a desktop app to a server

  1. Open Settings (gear icon), or choose Connect to a sync server on the first-run screen of a fresh install.
  2. Enter the server URL (e.g. http://192.168.1.50:3000) and the access token.
  3. Test & save. The app verifies the server, stores it, and runs a first sync — pulling the server's snippets into this machine's local library.

Repeat on every machine with the same URL and token. To stop syncing, use Remove server in Settings; your local library is left untouched.

Running the server

The server is the SnipVault web app in a container.

One command (v3.5.5) — for a single-user server with a generated token, no checkout needed:

curl -fsSL https://raw.githubusercontent.com/FranciszekRyszka/Snippet-Vault/main/install.sh | sh

install.sh pulls the prebuilt image, generates a strong token, writes a .env + docker-compose.yml into ./snipvault/, starts the container, and prints the URL and token to paste into the desktop app. Re-running is safe — it keeps your token and data and just pulls the latest image (that's also how you update). On a Portainer/Unraid host, add a Stack with the minimal image-based compose from docs/self-hosting.md instead.

Or with a full checkout (needed for TLS, multiple users, or building locally):

git clone https://github.com/FranciszekRyszka/Snippet-Vault.git
cd Snippet-Vault
cp .env.example .env      # set SNIPVAULT_TOKEN to a long random value
docker compose up -d

It listens on port 3000 and stores its database in the snipvault-data volume. A prebuilt image is also published to ghcr.io/franciszekryszka/snippet-vault (see Continuous Integration).

Full instructions — a bare Node + systemd alternative, TLS via a reverse proxy, and security notes — are in docs/self-hosting.md in the repo.

Multiple users, private vaults (added in v3.0.0)

By default a server is single-user: one SNIPVAULT_TOKEN guards one shared library. To let several people share the same server while keeping their snippets private to each of them, set SNIPVAULT_TOKENS instead — a comma-separated list of user:token pairs:

# in .env — one token per user, e.g. openssl rand -base64 32
SNIPVAULT_TOKENS=alice:ALICE_TOKEN,bob:BOB_TOKEN

Each user gets their own database file at data/users/<user>/snippets.db, keyed by their token. Isolation is structural — every request is routed to the caller's own file — so one user physically cannot read or sync another's data.

  • Nothing changes in the desktop app — each person just enters the server URL and their own token in Settings → Sync server.
  • If both SNIPVAULT_TOKENS and SNIPVAULT_TOKEN are set, the multi-user list wins.
  • Adding or removing a user is an .env edit plus a restart. The ./data volume already persists every user's file.
  • With several people on one box, use HTTPS (see below) and give each user a strong, unique token.

Monitoring the server

The docker-compose.yml is set up so you can see the server's health and read its logs without extra configuration.

Health check

The container runs a built-in health check against /api/health (which also confirms the database is readable). Docker reports the result in docker ps under STATUS as (healthy) / (unhealthy):

docker ps
# ... snipvault ... Up 2 minutes (healthy) ...

# just the health state:
docker inspect --format '{{.State.Health.Status}}' snipvault

Details of the check (interval 30s, a 20s start-up grace period, 3 retries):

  • Because /api/health sits behind the token gate, the check sends the same SNIPVAULT_TOKEN from the container's environment.
  • It uses Node (always present in the image) rather than curl/wget, which the slim runtime image doesn't include.

For richer, per-user monitoring, v3.0.0 adds a token-gated GET /api/status that returns the library size, last-write time, and server version (in multi-user mode it reflects the caller's own vault).

Logs

The app logs to stdout using Docker's json-file driver with rotation (max-size: 10m, max-file: 3), so logs stay bounded and are readable by any Docker log viewer:

docker logs -f snipvault          # follow the logs
docker compose logs -f snipvault  # same, via compose

Dozzle and similar web log viewers work out of the box — they read the Docker socket, so once running they'll list the snipvault container and stream its logs. docker-compose.yml includes an optional, commented-out dozzle service: uncomment it to run Dozzle alongside SnipVault, then open http://<this-host>:8080. (You typically run one Dozzle per host, so skip it if you already have one.)

Access control

Access is protected by a bearer token you choose (SNIPVAULT_TOKEN, or SNIPVAULT_TOKENS for multiple users). A token is required on every /api request, checked in constant time by proxy.ts — and in multi-user mode the whole list is scanned in full so timing can't reveal which token matched. Always set a token on a real deployment — with none set, a production server refuses every request rather than exposing the library.

Since v3.0.0, repeated failed auth attempts from one client are rate-limited with a 429 (a sliding window keyed by client IP) to blunt brute force on a weak token; valid clients are never throttled (tune via SNIPVAULT_RATELIMIT_MAX / SNIPVAULT_RATELIMIT_WINDOW_MS).

HTTPS is optional. By default the server runs on plain HTTP (port 3000) with no reverse proxy — a bare docker compose up -d is a fully working sync server, and on a trusted home LAN that's usually all you need. HTTPS is an opt-in extra, not a requirement.

To expose the server more widely — or once several users share it — you can terminate HTTPS in front. docker-compose.yml ships ready-to-enable, commented services for three reverse proxies — Caddy, nginx, and Traefik — so you can use whichever you prefer (Caddy and Traefik auto-provision Let's Encrypt certificates; nginx uses certs you supply). They're off unless you uncomment one; enable it, then point the app at the https:// URL. Details and setup steps for all three are in docs/self-hosting.md.

Good to know

  • Keep clocks roughly in sync. "Newest wins" compares each machine's clock; a badly-wrong clock could let a stale edit win. On a normal LAN this is a non-issue.
  • Editing the same snippet on two offline machines before they next sync keeps the most recent version as the live one; the other edit is preserved in that prompt's History (since v4.0.0) rather than lost. Sync often to keep things tidy.
  • First launch of v2.2.0 upgrades the local database in place (adds the uuid/deleted columns and backfills). This is automatic and lossless, but it means a v2.2.0 database shouldn't be opened by an older v2.1.0 build afterward.
  • History: v2.1.0 shipped a live "remote mode" (the app talked to the server directly, no local copy). v2.2.0 replaced it with the local-first two-way sync described here.

Clone this wiki locally