The app can keep any number of named libraries and switch between them
from the top bar or Info → Libraries. Each can sync to the account's other
devices or stay on this one (a scratch library to try things in).
The sync server now keeps several libraries per user under
/api/libraries; a migration makes each existing library the user's default
"Main", which /api/library keeps serving for older app builds. Every sync
lines the device's libraries up with the account's: new ones elsewhere are
downloaded, new ones here created on the server, ones deleted elsewhere
flagged rather than thrown away. The existing library keeps its files and
merge base, so upgrading asks nothing.
The image now runs as USER 10001:10001 with a matching group, so
Kubernetes can verify runAsNonRoot and fsGroup: 10001 grants it a mounted
volume. When the database can't be opened because its directory isn't
writable, the server says so and how to fix it, instead of SQLite's bare
"unable to open database file". The README covers the pod securityContext.
Drop the Compose/Caddy example: production runs on Kubernetes, and locally
the container only needs a forwarded port and a volume. The README now gives
the `docker run` steps and what a Kubernetes deployment needs (one replica,
since it's a single SQLite file; a volume at /data; TLS at the ingress).
The server only shut down on Ctrl-C, so `docker stop` and pod termination,
which send SIGTERM, waited out the grace period and killed it. It now shuts
down gracefully on either.
Also ignore `.env`, which can hold the OIDC client secret.
Folder sync left too much to the user's sync tool: platform file access
(the Android SAF plugin), choosing the document, merging conflict copies —
and its merge base could fall out of step with the document (absorbing
conflict copies, "forget", picking a new Android folder), reading every
difference as a deletion.
The new mtgc-server crate stores one versioned library per user and accepts
a write only when it names the version it replaces. It never merges: devices
still do that with merge3, through a new sync cycle over a `Remote` trait in
mtgc-core. The base advances only on knowledge — to what was read, or to a
write the server confirmed — and is stored with the server and account it
belongs to, so a different account starts from an empty ancestor instead of
deleting. The working copy is only rewritten under the app-state lock, and
sync commands now run off the main thread.
Sign-in happens in the browser on the server's pages, via the owner's OIDC
provider and/or local username/password accounts (Argon2id, managed with
`mtgc-server user ...`), and returns a per-device revocable token to the app
by PKCE-protected code: a loopback port on desktop, the mtgc:// scheme on
Android.
The Info tab gains sign-in, the first-sync combine/replace choice, the
account's devices, and JSON export/import; the top bar shows sync state.
Includes a Dockerfile, Compose/Caddy example, and scripts to build and push
${DOCKER_REGISTRY}/mtgc/sync-server.