Files
bookorbit/docs/SERVER_CONNECTION_IDENTITY.md
T
neon fbaf0b63df feat(server): publish a stable installation identity for client connections
The iOS app lets an account reach one server through several addresses, such as a home network
IP and a public name. Before it sends credentials to an alternative address it needs to know the
address leads to the same installation, so expose a public, credential-free GET /server-identity
returning a persisted UUID, and add the same optional instanceId to app-info with the
server-connection-identity feature flag.

The UUID lives in a singleton server_identity row shared by every replica, created on first use
and cached per process. An unreadable identity omits instanceId instead of failing app-info,
which clients rely on for feature detection. Existing routes and bodies are unchanged and the
web client does not call the new route; the iOS app does.

The migration uses IF NOT EXISTS because an unreleased build created the same table as 0109.
2026-10-13 19:37:52 -06:00

37 lines
2.3 KiB
Markdown

# Server connection identity
The iOS client supports several user-entered connection addresses for one
BookOrbit installation. `GET /api/v1/server-identity` is public and returns only
`product: "bookorbit"`, `protocolVersion: 1`, and a persisted UUID `instanceId`.
It sends `Cache-Control: no-store`. The authenticated `/api/v1/app-info` response
adds the same optional `instanceId` and advertises `server-connection-identity`.
Existing routes and request bodies are unchanged. Released clients ignore the
additional fields. New clients keep single-address behavior on older servers.
The iOS client consumes this route even though the web client does not call it.
The singleton `server_identity` row is generated on first use and shared by all
replicas through PostgreSQL. Keep it in database backups. Application restart,
container replacement, DNS changes, and reverse-proxy aliases preserve it.
Multiple addresses must reach the same database and installation.
The UUID detects accidental connection to another installation through an
alternative address. It is public and is not a replacement for TLS certificate
validation. Clients never send account credentials when probing an alternative
and never bypass TLS trust. The address a user signed in with keeps the trust
released clients gave it, including following its redirects. An alternative is
used only after it answers with the enrolled UUID, and its redirects must stay on
its host. Users who explicitly choose HTTP retain that transport's existing trust
limits. If the identity row cannot be read, `/app-info` still answers and omits
`instanceId`.
A restored database belonging to the same installation keeps its UUID. When the
UUID changes (an independent clone, a fresh database, or a backup taken before
this table existed), clients keep using the address they signed in with, adopt
the new UUID from the authenticated `/app-info` response, and ask the user to
verify each alternative again. For an independent clone, stop every application
replica, remove only the singleton `server_identity` row through an
administrative database session, and restart. The next request creates a fresh
UUID. Never regenerate the row while live replicas hold a cached identity.
Regression coverage is available through `pnpm e2e:run -- server-connection-identity`.