# System Settings API ## Overview The System Settings API is the canonical way to read and write Bookhoard's tunable system settings (scanning, security, rate limits, sync, performance, and defaults). Every setting carries full metadata — type, range, category, description, and whether a restart is required — so the admin UI (and API clients) can render and validate settings generically. **Base URL**: `/api/system` **Authentication**: Admin JWT token required **Content-Type**: `application/json` --- ## Endpoints ### List All Settings Retrieve every known tunable setting with its current value and metadata. **Endpoint**: `GET /api/system/settings` **Authentication**: Admin role required **Response**: **200 OK** ```json [ { "key": "scan_poll_interval_seconds", "value": "60", "type": "int", "min": "1", "max": "3600", "requires_restart": false, "category": "scanner", "group": "Scanning", "description": "How often to scan all libraries (seconds)", "is_default": true } ] ``` **Entry fields**: | Field | Type | Description | | ------------------ | ------- | -------------------------------------------------------- | | `key` | string | Setting identifier (stable API name) | | `value` | string | Current value (validated/clamped by the registry) | | `type` | string | `int`, `bool`, or `string` | | `min` / `max` | string | Range bounds for `int` settings (omitted otherwise) | | `requires_restart` | boolean | Change takes effect only after a server restart | | `category` | string | Coarse area: `scanner`, `security`, `api`, `sync`, `performance`, `general` | | `group` | string | Sub-section shown in the admin UI | | `description` | string | Human-readable description | | `is_default` | boolean | True when the current value equals the compiled default | **Example**: ```bash curl -X GET https://bookhoard.example.com/api/system/settings \ -H "Authorization: Bearer " ``` --- ### Update a Setting Validate, persist, and reload a single setting. **Endpoint**: `PUT /api/system/settings` **Authentication**: Admin role required **Request Body**: ```json { "key": "scan_poll_interval_seconds", "value": "30" } ``` | Field | Type | Required | Description | | ------- | ------ | -------- | ------------------------------- | | `key` | string | Yes | Setting key (from the list) | | `value` | string | Yes | New value, as a string | **Response**: **200 OK** ```json { "key": "scan_poll_interval_seconds", "value": "30", "type": "int", "min": "1", "max": "3600", "requires_restart": false, "category": "scanner", "group": "Scanning", "description": "How often to scan all libraries (seconds)", "is_default": false, "reload_required": false, "message": "" } ``` - `reload_required: true` means the change takes effect only after a restart (e.g. rate limits, worker pool, lockout settings). - Validation is type-aware: `int` values are checked against `min`/`max`, `bool` values must parse, `default_timezone` must be a valid IANA timezone via `time.LoadLocation`, and strings must be non-empty. **Errors**: `400` (unknown key, invalid value, out of range), `401`, `403`, `503` (settings registry not initialized). **Example**: ```bash curl -X PUT https://bookhoard.example.com/api/system/settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"key": "scan_poll_interval_seconds", "value": "30"}' ``` --- ## Setting Catalog Current tunable settings by category: **Scanner** (`scanner`) | Key | Default | Range | Restart | Description | | ------------------------------ | ------- | -------- | ------- | ----------------------------------------- | | `scan_poll_interval_seconds` | `60` | 1-3600 | No | How often to scan all libraries (seconds) | | `auto_scan_enabled` | `true` | - | No | Whether auto-scanning is enabled | **General** (`general`) | Key | Default | Restart | Description | | ----------------- | ------- | ------- | ------------------------- | | `default_timezone`| `UTC` | No | System default timezone | **Security** (`security`) | Key | Default | Range | Restart | Description | | ---------------------------- | --------- | ------------ | ------- | ---------------------------------------------- | | `session_duration_seconds` | `604800` | 300-31536000 | No | How long a login session stays valid | | `password_min_length` | `8` | 1-128 | No | Minimum password length | | `password_require_upper` | `true` | - | No | Require at least one uppercase letter | | `password_require_lower` | `true` | - | No | Require at least one lowercase letter | | `password_require_number` | `true` | - | No | Require at least one number | | `password_require_special` | `true` | - | No | Require at least one special character | | `auth_rate_limit_per_min` | `10` | 1-10000 | **Yes** | Global auth API rate limit (req/min) | | `login_max_attempts` | `5` | 1-100 | **Yes** | Failed login attempts before lockout | | `login_lockout_minutes` | `15` | 1-10080 | **Yes** | Lockout duration after failed logins | **API** (`api`) | Key | Default | Range | Restart | Description | | ------------------------------- | ------- | --------- | ------- | ------------------------------------ | | `opds_default_page_size` | `50` | 1-500 | No | Default OPDS page size | | `opds_max_page_size` | `200` | 1-1000 | No | Maximum OPDS page size | | `device_rate_sync_per_min` | `60` | 1-10000 | No | Device sync requests per minute | | `device_rate_progress_per_min` | `120` | 1-10000 | No | Device progress requests per minute | | `device_rate_metadata_per_min` | `30` | 1-10000 | No | Device metadata requests per minute | **Sync** (`sync`) | Key | Default | Range | Restart | Description | | ------------------------------- | ------- | -------- | ------- | -------------------------------------------------- | | `annotation_tombstone_ttl_days` | `30` | 1-3650 | No | How long deleted annotations are kept before purge | | `sync_queue_interval_seconds` | `5` | 1-3600 | **Yes** | How often the sync queue flushes | | `sync_queue_batch_size` | `50` | 1-10000 | **Yes** | Max items processed per sync queue flush | **Performance** (`performance`) | Key | Default | Range | Restart | Description | | ------------------------ | ------- | --------- | ------- | ------------------------------------------- | | `conversion_cache_ttl_hours` | `24` | 1-720 | No | How long converted (KEPUB) files are cached | | `worker_pool_size` | `3` | 1-100 | **Yes** | Number of background worker goroutines | | `worker_queue_cap` | `100` | 1-10000 | **Yes** | Background worker job queue capacity | --- ## Legacy Scan Settings Routes The older JSON routes still work for backward compatibility and now refresh the settings registry cache on write, but they are **superseded** by `GET/PUT /api/system/settings`: - `GET /api/libraries/scan-settings` — returns only `scan_poll_interval_seconds` and `auto_scan_enabled` - `PUT /api/libraries/scan-settings` — accepts `{ "scan_poll_interval_seconds": int, "auto_scan_enabled": bool }` Both fields are backed by the same registry entries documented above. --- ## Related Endpoints - `GET/PUT /api/system/config` — raw key/value system configuration (see [System Config API](config.md)) - `POST /api/scanner/scan` — trigger a manual scan (see [Scanner API](../scanner/)) - `GET /api/admin/hash-conflicts` — duplicates found during hashing (see [Hash Conflicts API](../admin/hash-conflicts.md))