Running the server
The backend is a single Go binary. On first start it creates its SQLite database (and parent directory, mode 0700), runs migrations, and binds a port.
Build and run
Build
cd backend
make build # → bin/open-secret-server
Run
The one flag you must set is -server-url, the deployment's canonical public URL (it's bound into session tokens, so it can't be guessed at boot):
./bin/open-secret-server -server-url=https://vault.example.com
For local development, point it at where you open the app, the Vite dev server:
./bin/open-secret-server -server-url=http://localhost:5173
Verify
curl -fsS http://localhost:8080/healthz # → 200 ok
These are not the same, which is why the commands above mix :5173, :8080, and https://vault.example.com:
-addr(default:8080) is the TCP port the server actually listens on. The health check hits it directly (localhost:8080), and clients send their RPCs there.-server-urlis the deployment's canonical public URL, used only as the session-token audience (so a token from one instance can't be replayed against another). It does not have to share a host or port with-addr.
In production a reverse proxy terminates TLS at the public -server-url (https://vault.example.com) and forwards to the server's -addr (:8080). In local dev the web app runs on :5173 and talks to the backend on :8080, so -server-url=http://localhost:5173 is the app's URL while :8080 stays the API port.
A fuller production invocation:
./bin/open-secret-server \
-server-url=https://vault.example.com \
-addr=:8080 \
-db=/data/open-secret.db \
-cors=https://vault.example.com \
-audit-db=/data/audit.db
Flags and environment variables
| Flag | Env var | Default | What it does |
|---|---|---|---|
-server-url | OPENSECRET_SERVER_URL | required | Canonical public URL of this deployment. Bound into the session-token audience so a token can't be replayed against another instance. |
-addr | - | :8080 | HTTP listen address. |
-db | - | data/open-secret.db | SQLite path. Parent dir created 0700 on first run. |
-cors | OPENSECRET_CORS | (localhost dev origins) | Comma-separated browser origin allow-list. See Configuration. |
-audit-db | OPENSECRET_AUDIT_DB | <dir of -db>/audit.db | SQLite path for the audit log. |
-disable-audit | - | false | Turns the audit log off entirely. Intended for dev/test; logged as a warning at boot. |
-auth-mode | OPENSECRET_AUTH_MODE | standalone | idp switches the deployment to SSO onboarding. Immutable after first boot. See Enterprise SSO. |
| - | OPENSECRET_OIDC_* | - | OIDC issuer, client id, provider kind, scopes, and per-provider params (IDP mode only). Full list in Enterprise SSO. |
Unless you pass -disable-audit, the server writes an audit log to audit.db next to your main database. See Configuration → Audit log.
Storage
- Engine, SQLite via
modernc.org/sqlite(pure Go, no CGO), a single connection in WAL mode. - Migrations, applied automatically on startup (Goose). There's no separate migration step to run.
- Backups, because it's a SQLite file, an operational backup is a consistent copy of the DB file(s). (That's distinct from vault recovery, which is covered in Backup & recovery.)
Hardening defaults
The server ships with conservative HTTP limits so you don't have to add them at the proxy:
| Setting | Value | Why |
|---|---|---|
| Request body cap | 16 MiB | Bounds a Create/Update carrying many device shares. |
ReadHeaderTimeout | 10 s | Slow-loris defense. |
ReadTimeout / WriteTimeout | 5 min | Room for large legitimate writes. |
IdleTimeout | 120 s | Reaps idle keep-alives. |
| Signup rate limit | 5 burst, ~1/min per IP | Throttles account-creation abuse. |
Put it behind TLS (a reverse proxy is fine) and point -server-url at the public HTTPS URL.