# Prompt sync server Small Go service that stores one JSON blob per sync group, keyed by an 8-digit code (`1234-5678`). There is no authentication: the code is the shared secret, so anyone who knows it can read and overwrite that group. It is meant to be self-hosted for personal use, not exposed as a public service. ## Requirements - Postgres (any recent version; developed against 18) - A host both Macs can reach — a VPS, a home server, or a Tailscale node ## Run it ```bash createdb prompt_sync cd Server PROMPT_SYNC_DATABASE_URL="postgres://localhost:5432/prompt_sync?sslmode=disable" \ go run . # or: go build -o prompt-sync . && ./prompt-sync ``` | Env var | Default | | --- | --- | | `PROMPT_SYNC_ADDR` | `:8080` | | `PROMPT_SYNC_DATABASE_URL` | `postgres://localhost:5432/prompt_sync?sslmode=disable` | The schema is created on startup. Every request is logged as `METHOD PATH STATUS DURATION`; SIGTERM/SIGINT shut down gracefully so an in-flight push is not cut off mid-write. ## Run with Docker `docker-compose.yml` starts Postgres and the server together: ```bash cd Server POSTGRES_PASSWORD=$(openssl rand -hex 16) docker compose up -d docker compose logs -f server # request log ``` Or build the image on its own and point it at an existing Postgres: ```bash docker build -t prompt-sync Server/ docker run -d --name prompt-sync -p 8080:8080 \ -e PROMPT_SYNC_DATABASE_URL="postgres://user:pass@host:5432/prompt_sync?sslmode=disable" \ prompt-sync ``` The image is a static binary on `distroless` (non-root, no shell, ~6 MB binary), so its `HEALTHCHECK` calls `prompt-sync -healthcheck`, which pings the local `/healthz` and exits non-zero on failure — no curl needed in the image. That is what `docker ps` reports as the container health. ## Deploy to a Linux host ```bash # from the repo root, on the Mac cd Server GOOS=linux GOARCH=arm64 go build -o prompt-sync . # amd64 for x86 hosts scp prompt-sync :/tmp/ && ssh 'sudo mv /tmp/prompt-sync /usr/local/bin/' # on the host: create the database and role, then sudo cp Server/prompt-sync.service /etc/systemd/system/ sudo systemctl edit --full prompt-sync # set PROMPT_SYNC_DATABASE_URL sudo systemctl enable --now prompt-sync journalctl -u prompt-sync -f # verify it is listening ``` Put it behind TLS if it is reachable from the internet — the code is the only secret, and it travels in the URL path. Both plain HTTP and HTTPS work from the app; an HTTP URL is often fine over a LAN or Tailscale. ## Endpoints | Method | Path | Behaviour | | --- | --- | --- | | `POST` | `/api/codes` | Generates a unique code, inserts an empty group | | `GET` | `/api/sync/{code}` | `{"version":N,"updatedAt":…,"data":{…}\|null}`, 404 if unknown | | `GET` | `/api/sync/{code}/version` | `{"version":N}` — cheap poll used by the app | | `PUT` | `/api/sync/{code}` | Upserts the payload, returns the incremented version | | `GET` | `/healthz` | Liveness (200) | Payloads are opaque `JSONB`; the server never parses note contents. Bodies are capped at 50 MB, which is generous enough for base64 photos. ## Backups The whole state is one table, so `pg_dump prompt_sync` is a complete backup: ```bash pg_dump prompt_sync | gzip > prompt_sync-$(date +%F).sql.gz ```