8. Headless mode (server)
Note
This section describes an advanced, optional mode of blunderDB, intended for server deployments, multi-user setups and automation. The normal and recommended use of blunderDB remains the desktop application described in the previous chapters. If you use blunderDB on your own, on your computer, you do not need this mode: you can skip this chapter without losing any of the analysis features.
8.1. Overview
In addition to the desktop application and the command-line commands (see Command Line Interface (CLI)), the same blunderdb binary can run in headless mode: without a graphical interface, driven entirely from the command line or over the network. This mode covers three use cases:
the
servedaemon — exposes the blunderDB engine as an HTTP + JSON service, to run a shared database on a server and access it from several clients;the generic
calldispatcher — calls any storage operation directly, locally, for scripting and testing;the
migratecommand — transfers a single-user SQLite database to a multi-user PostgreSQL backend.
These three use cases rely on a shared storage layer that can talk to two backends: SQLite (the usual .db file format of the desktop application) and PostgreSQL (for multi-user server deployments).
8.2. The serve daemon
blunderdb serve runs the engine as an HTTP service that responds in JSON. It lets you host a position database on one machine and access it from several clients.
# Servir une base SQLite locale sur le port 8080
blunderdb serve --db ma_base.db --addr :8080
# Servir un backend PostgreSQL
blunderdb serve --backend postgres \
--dsn "postgres://user:pass@host:5432/blunderdb?sslmode=disable" \
--addr :8080
Warning
The daemon performs no authentication. It trusts the X-Tenant-ID request header and must run behind a reverse proxy (nginx, Caddy…) responsible for authentication. Never expose it directly to the public Internet.
Options:
Option |
Default |
Meaning |
|---|---|---|
|
– |
SQLite file (shorthand for |
|
|
storage backend: |
|
|
backend connection string |
|
|
listen address |
|
|
log level: |
|
|
exposes |
|
– |
enables CORS for this origin (disabled by default) |
|
|
requests per second per tenant limit (0 = disabled) |
|
|
token-bucket size for request bursts |
|
|
PostgreSQL: enables per-tenant Row-Level Security (defence in depth, opt-in) |
Most options can also be provided through environment variables (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_LOG_LEVEL, BLUNDERDB_RLS).
8.2.1. Endpoints
The service exposes operational endpoints, always present:
GET /healthz— liveness (the process is running);GET /readyz— readiness (storage responds);GET /metrics— Prometheus metrics (if--metricsis enabled).
The business surface follows the POST /v1/<family>.<method> scheme (for example /v1/positions.save, /v1/matches.get). The families cover positions, analyses, matches, comments, collections, tournaments, Anki cards, filters, sessions, search, metadata, statistics and import. Listing endpoints return an NDJSON stream (one JSON object per line). The server shuts down cleanly on SIGINT / SIGTERM.
Two methods in the positions family decode a position without storing it: positions.fromXGID rebuilds a position from an XGID string, and positions.fromXGP from a single-position .xgp file.
8.3. PostgreSQL backend and multi-user
For a shared deployment, blunderDB can store data in PostgreSQL rather than in a SQLite file. The backend is selected with --backend postgres and the --dsn connection string. The schema is created and migrated automatically at startup.
Data is partitioned per tenant: each request carries a scope identifier (the X-Tenant-ID header, default by default), which lets several users share the same instance without seeing each other’s data. The --rls option additionally enables PostgreSQL’s Row-Level Security: per-tenant isolation policies are installed and app.tenant_id is set per connection. This is an optional defence in depth, disabled by default.
8.4. Migrating a SQLite database to PostgreSQL
blunderdb migrate copies a single-user SQLite database to a PostgreSQL backend, under a chosen tenant scope — this is the path to “upload” a desktop library to a server deployment.
blunderdb migrate \
--from sqlite:///chemin/vers/base.db \
--to "postgres://user:pass@host:5432/db?sslmode=disable" \
--tenant-id mon-tenant
# Prévisualiser sans rien écrire
blunderdb migrate --from sqlite:///chemin/vers/base.db \
--tenant-id mon-tenant --dry-run
The migration copies the positions, their analyses and comments, the matches (games + moves), the tournaments (with their match links) and the collections (with their composition), reassigning primary and foreign keys, all within a single transaction on the destination side: the operation is atomic (a failure leaves the destination intact, just run it again). Progress and the final summary are emitted as NDJSON on standard output.
Option |
Default |
Meaning |
|---|---|---|
|
– |
source SQLite database ( |
|
– |
destination PostgreSQL DSN ( |
|
– |
destination tenant scope (required except in |
|
– |
counts what would be copied without writing anything |
|
|
|
Note
Application state is not (yet) migrated: Anki decks/cards, the filter library, search and command history, and session metadata. The priority is migrating the position library and the match history.
8.5. The generic call dispatcher
In addition to the historical subcommands (Command Line Interface (CLI)), blunderdb call exposes all storage operations directly, locally. It goes through the same handlers as the serve daemon: the behaviour is therefore identical to POST /v1/<family>.<method>. This is useful for scripting and integration testing.
# Lister toutes les méthodes disponibles
blunderdb call --list
# Lectures
blunderdb call metadata.counts --db ma_base.db
blunderdb call positions.list --db ma_base.db --json '{"limit":10}'
blunderdb call matches.get --db ma_base.db --json '{"id":1}'
# Écritures
blunderdb call positions.save --db ma_base.db --json '{"position":{...}}'
blunderdb call matches.delete --db ma_base.db --json '{"id":42}'
Options:
Option |
Default |
Meaning |
|---|---|---|
|
– |
SQLite file (shorthand for |
|
|
|
|
|
backend connection string |
|
|
tenant scope (sent as |
|
|
request body in JSON format |
|
– |
reads the request body from a file |
|
– |
lists all |
The JSON response (or the NDJSON stream for *.list endpoints) is written to standard output. On error, the process exits with a non-zero code and the {"error":{…}} envelope is printed to standard output so it stays parseable (for example with jq).