Skip to content

feat(arr): ArrInstance model, persistence, and outbound client port (#4) - #57

Merged
ChrisonSimtian merged 1 commit into
mainfrom
feat/arr-instance-model
Jul 27, 2026
Merged

feat(arr): ArrInstance model, persistence, and outbound client port (#4)#57
ChrisonSimtian merged 1 commit into
mainfrom
feat/arr-instance-model

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Contributor

PR 1 of 3 for #4. DR-010 makes the *arr apps the drivers of Krautwatch, but nothing in the system knew an instance existed — no entity, no persistence, no client port. This is the foundation that #6 (monitored-series work-list), #5 (env-var bootstrap) and the settings page all stand on.

Domain

  • ArrInstance — name, kind, base URL, API key, enabled flag, plus a cached last-test outcome so the UI can show state without re-probing every instance on page load.
  • ArrKind (Sonarr | Radarr). Prowlarr is deliberately absent: it's configured pointing at Krautwatch as an indexer, so there's nothing to call outbound and no instance record to keep.
  • IArrInstanceRepository, and IArrClient as the outbound HTTP boundary. Optionally pre-warm the crawl list from Sonarr/Radarr monitored series #6 extends the same port with the monitored-series fetch — which is why it's a port now rather than a UI helper later.
  • ArrConnectionResult with a specific ArrConnectionFailure: unreachable, TLS failure, 401, 404-wrong-base-path, and 200-but-not-an-*arr. "It doesn't work" is the most common self-hosting complaint and only a specific cause is actionable. PR 2 fills these in.

Infrastructure

ArrInstanceRepository, EF mapping, migration AddArrInstances.

Two things worth noting

Mind the direction. ArrInstance.ApiKey is their key, held so we can authenticate to them. Krautwatch:ApiKey is the inbound key the *arr apps use to call us. Opposite directions, different concerns — easy to conflate later.

BaseUrl is uniquely indexed, because #5 matches env-var-bootstrapped instances by base URL. That needs to be impossible to violate at the schema level, not prevented by convention inside a handler.

A behaviour I pinned rather than assumed

Kind is persisted as text so the column stays readable in the database. That has a consequence: SQL then orders it alphabetically ("Radarr" < "Sonarr") rather than by enum value, so listings group Radarr first.

My first version of the ordering test assumed enum declaration order and failed. Rather than quietly flip the expectation, the test now asserts the real behaviour with a comment explaining why, and the repository documents it — otherwise it reads like an accident waiting to be "fixed" into a regression.

Verified

Tests: App 67 / Arch 11 / Infra 83 green, 0 warnings.

Next

  • PR 2ArrHttpClient (GET /api/v3/system/status with X-Api-Key) + TestArrConnection, filling in the failure modes above.
  • PR 3 — the /settings page, which also adopts the GetSettings/SaveSettings handlers that are currently registered in DI but called by nothing.

Plan: docs/plans/2026-07-27 - arr-instance-config-ui.md

🤖 Generated with Claude Code

…rt (#4)

PR 1 of the Sonarr/Radarr configuration work. DR-010 makes the *arr apps the drivers
of Krautwatch, but nothing in the system knew an instance existed — no entity, no
persistence, no client port. This is the foundation #6 (monitored-series work-list),
#5 (env-var bootstrap) and the settings page all stand on.

Domain:
- ArrInstance — name, kind, base URL, API key, enabled flag, plus a cached last-test
  outcome so the UI can show state without re-probing every instance on page load.
- ArrKind (Sonarr | Radarr). Prowlarr is deliberately absent: it is configured
  pointing AT us as an indexer, so there is nothing to call outbound.
- IArrInstanceRepository, and IArrClient as the outbound HTTP boundary. #6 extends the
  same port with the monitored-series fetch, which is why it is a port now rather than
  a UI helper later.
- ArrConnectionResult with a specific ArrConnectionFailure: unreachable, TLS, 401,
  404-wrong-base-path, and 200-but-not-an-arr. "It doesn't work" is the most common
  self-hosting complaint and only a specific cause is actionable. PR 2 fills these in.

Infrastructure: ArrInstanceRepository, EF mapping, migration AddArrInstances.

Note the direction: ArrInstance.ApiKey is *their* key, held so we can authenticate to
them. Krautwatch:ApiKey is the inbound key the *arr apps use to call us. Different
concerns, opposite directions.

BaseUrl is uniquely indexed because #5 matches env-var-bootstrapped instances by base
URL — that has to be impossible to violate at the schema level, not prevented by
convention in a handler.

Kind is persisted as text so the column stays readable in the database. That has a
consequence worth pinning: SQL then orders it alphabetically ("Radarr" < "Sonarr")
rather than by enum value, so listings group Radarr first. A test asserts the actual
ordering rather than the one the enum declaration implies — my first version of that
test assumed enum order and failed.

Verified: migration applies to an empty Postgres 18.3 and produces the expected schema
including the unique BaseUrl index and native timestamptz columns.

Tests: App 67 / Arch 11 / Infra 83 green, 0 warnings.

Next: PR 2 (ArrHttpClient + TestArrConnection), PR 3 (the /settings page, which also
adopts the GetSettings/SaveSettings handlers that are currently registered but called
by nothing).

Plan: docs/plans/2026-07-27 - arr-instance-config-ui.md

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChrisonSimtian
ChrisonSimtian merged commit 3eb97f4 into main Jul 27, 2026
1 check passed
@ChrisonSimtian
ChrisonSimtian deleted the feat/arr-instance-model branch July 27, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant