feat(arr): ArrInstance model, persistence, and outbound client port (#4) - #57
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 1 of 3 for #4. DR-010 makes the
*arrapps 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, andIArrClientas 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.ArrConnectionResultwith a specificArrConnectionFailure: 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, migrationAddArrInstances.Two things worth noting
Mind the direction.
ArrInstance.ApiKeyis their key, held so we can authenticate to them.Krautwatch:ApiKeyis the inbound key the*arrapps use to call us. Opposite directions, different concerns — easy to conflate later.BaseUrlis 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
Kindis 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
BaseUrlindex, nativetimestamptzcolumns (no new ISO-8601-text timestamps; see Store DateTimeOffset as native timestamptz instead of ISO-8601 text (leftover SQLite workaround) #50).PostgresFixture: round-trip, text enum round-trip, duplicate-URL rejection, enabled filtering, cached-test-outcome update, delete, delete-absent no-op, ordering.Tests: App 67 / Arch 11 / Infra 83 green, 0 warnings.
Next
ArrHttpClient(GET /api/v3/system/statuswithX-Api-Key) +TestArrConnection, filling in the failure modes above./settingspage, which also adopts theGetSettings/SaveSettingshandlers 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