Skip to content

Database

Implementation contracts and behavior for database.

SQLite (pure Go driver) with WAL mode. The live schema is code: internal/db/db.go: the initSQL create statements plus an in-code list of tolerant ALTER TABLE migrations with one-time backfills. There are no SQL migration files.

The instance-assignment upgrade preserves legacy Radarr/Sonarr default routing as explicit grants for existing users, including administrators, and retains saved library grants and personal pins. Later assignment removals and newly created admins are never enrolled on restart.

Authenticated requests read current user, device, and sign-in policy state every time. Device last_seen_at activity writes occur at most once per minute during normal request traffic, including concurrent artwork loads. This limits disk writes on NAS storage while retaining immediate revocation and permission checks. The activity timestamp is best-effort display metadata, not an authorization cache.

The pool holds exactly one connection (SQLite is single-writer), so every query in the process takes its turn through one door, and no query sets a timeout. Code that holds the connection while waiting for something that also needs it therefore wedges the whole server: silently: nothing crashes, nothing errors, and a blocked goroutine writes no further log line, so the container keeps reporting itself healthy while the app has stopped working. Two rules follow. Drain and close a cursor before calling anything that touches the database (see reportBookImportStalls, whose comment records the deadlock this caused). And a stall watchdog (internal/db/stallwatch.go) probes the pool from outside every 10s; two consecutive 5s failures to acquire the connection log one db: STALLED line with the pool counters and a goroutine dump naming the holder, a reminder every 5 minutes while it lasts, and the duration on recovery. Only the probe carries a deadline: real queries are untouched, so this cannot fail a slow query. It reports to the log and nowhere else on purpose: system issues and admin pushes are written through the very connection that is stuck, so during this failure the log is the only channel that still works.

Area Tables
Accounts & sessions users, refresh_tokens, connect_tokens, devices (local hardware-id deduplication; auth_method, oidc_issuer and plex_account_id preserve session provenance), oidc_identities (unique issuer/subject and user/issuer pairs), plex_identities (unique numeric Plex account ID and one per user; email/username for display), webauthn_credentials, user_content_policies (a row makes the user a kids account: rating caps per media type in a region’s scheme, hide-unrated, hidden genre ids; cascades with the user; never for an admin)
Requests request_log (approval + season/quality/book-format/instance capture, the fulfilled arr record id in book_record_id; Chaptarr book or Lidarr album; so status survives foreign-id re-keys, catalog_provider/catalog_id retaining public source identity, match_confirmed recording a verified user choice, park_reason marking server-owned delivery/import waits, and add_failure_reason marking an approval-queue row whose automatic add already failed), book_request_waiters (shared pending book subscribers + their concrete format coverage; music rows are per-user and need none), user_request_settings, request_dispatch (durable per-format state, attempts, retry schedule, lease, canonical/native record identity), request_dispatch_locks (per-instance worker leases), request_tag_jobs (unique request/requester/format eligibility captured at admission, retry schedule, lease, safe failure and applied receipt; upgrades preserve existing movie/TV jobs), tv_match_overrides (local correction/pause/default revision tombstones), request_tv_targets (source/target snapshots, mapping revisions, resumable pilot phase and unique audited repair links)
Request allowances request_quota_defaults, user_request_quotas, immutable-owner request_quota_charges and request_quota_items, request_quota_resets audit, and singleton request_quota_lock; request_dispatch.delivery_started_at persists the first potentially consequential write
Instances service_instances (Radarr/Sonarr/Chaptarr/Lidarr auto_add_users onboarding choice + tag_requests opt-in + encrypted keys/passwords + current/pending server-only webhook credentials + the encrypted per-Chaptarr-instance hardcover_token and connection-change hardcover_revision + per-instance media path mappings/legacy mode + the media servers’ media_server_config document), user_default_instances (routing preferences only), user_media_library_policies (ABS per-user library choices and pending managed updates), user_instance_grants (authoritative per-user library assignments; for media servers the grant is access eligibility), user_media_server_accounts (one row per user × media-server instance: remote id and name: on Plex the canonical email of the share: whether Cantinarr created it, manage_access, access_sync_pending, disabled_at), user_media_server_unlinks (per-user/per-instance automatic relink suppression; no remote identity retained), video_app_preferences (personal per-service iPhone/iPad overrides of video-server app defaults; cascades on user deletion), listening_app_preferences (personal iOS/Android overrides of per-instance Audiobookshelf app defaults; cascades on user deletion), arr_queue_witness (durable per-instance queue-departure completion witness; its observed_at doubles as the import-history catch-up cursor; one row per instance, ignored past 6h)
Apple TV apple_tv_devices (stable identity, address, encrypted pairing credentials, revision), apple_tv_grants (per-TV adult access; cascades on user/TV deletion)
Hardcover OAuth hardcover_connections (encrypted access/refresh-token pair, credential revision and reconnect flag), hardcover_instance_connections (one selected OAuth connection per Chaptarr instance; explicit sharing and final-link credential cleanup)
Push push_tokens (one per device), notification_prefs (account master and categories), settings.push_notification_policy (server master and allowed categories), content_alert_claims (durable new-content dedupe, 10-minute window; also counted as the 12-per-window alert storm breaker, per storm_scope; broadcast and upgrade alerts spend separate budgets, silent upgrade claims spend none)
Discord discord_notifications (event outbox and delivery receipts), discord_user_preferences (personal IDs and opt-in categories), discord_availability (per-request notification receipts and the last observation key, never library truth); encrypted destination settings in settings
AI access user_ai_settings (explicit personal selection and default-off model_fallback_enabled), user_ai_credentials (per-provider encrypted personal API keys), user_codex_accounts (personal encrypted OpenAI OAuth authorization), shared_codex_account (singleton encrypted included authorization), user_grok_accounts / shared_grok_account (the xAI Grok OAuth equivalents); users.ai_shared_enabled stores the included-access grant, while settings stores the daily health-check switch/timestamp
AI configuration history external_setting_changes (append-only AI/MCP quality-profile/custom-format outcomes, server-held before/applied snapshots, and linked quality-profile restores), profile_change_proposals (quality-profile changes parked by external MCP agents awaiting in-app admin approval; server-held plans and drift hashes, one pending per profile)
Remediation issues (exact arr-scoped reports plus admin-only system alerts, with closure provenance, the problem_kind label on auto-detected rows: the Import Doctor’s verdict, or the pre-air season finding; book issues store the Chaptarr author/book record ids in place of TMDB/TVDB identity: and last_paged_at, the durable per-issue record of the last queue-delivered admin page, which is also the 24-hour re-page budget’s input), issue_observations (durable retry/settle clocks, baseline + compact import receipt), issue_observation_downloads (incident download IDs + arr attempt/file boundaries), issue_observation_attempts (transition audit), remediation_queue_snapshots (latest successful minimal typed snapshot), remediation_observation_failures (bounded outage timer), remediation_observation_watermarks (monotonic per-instance success/failure ordering), issue_alert_queue (owed admin pushes, held for the promotion hold-down and coalesced on delivery), agent_action_alert_queue (owed approval pushes, held and coalesced the same way), issue_messages, agent_runs, agent_steps, agent_actions (one active proposal per issue; immutable proposal + approved params; auto_rule_id attributes a standing-rule decision; target_download_id freezes the arr download a dispatched fix acted on, so a repeat of an ineffective remedy is distinguishable from a first attempt on a new release), agent_approval_rules (admin-armed standing auto-approvals keyed problem × fix × facet, with counters and self-pause state), prevention_notices (one durable row per instance × problem label Cantinarr has told an admin keeps happening: the measured counts, the raise count that caps the cooldown, and the issue that carried it; the issue is the dismissable surface, this row is the memory that decides whether to speak again and how soon). The recurrence roll-up behind it is served by the partial index idx_issues_problem_recurrence on issues
MCP OAuth oauth_clients, oauth_authorization_codes (with local/OIDC/Plex provenance), oauth_refresh_tokens
Misc settings (encrypted KV: JWT secret, push key, request policy, the outbound proxy (outbound_proxy_url, address plus credentials), the Seerr-compatible API key (seerr_api_key, with its issuing admin and issue time), the app-wide Plex client identifier and the Plex migration marker, and OIDC configuration (oidc_config, containing the encrypted client secret), oidc_tested configuration proof and oidc_sso_only policy, Plex sign-in settings (plex_auth, plex_auth_enabled), and the server_settings blob holding the management-portal URL plus the discovery row source/language preferences), tmdb_tvdb_cache (30-day TTL)

View the maintained source for this page.