The Control Plane That Keeps the Loop Moving
Sonarr's policy decision is useful only if the application can keep work moving after the decision is made. This chapter examines the control plane behind that continuity: how configured provider definitions become executable capabilities, how recurring jobs become durable commands, how the queue coordinates competing work, and how events connect state changes to other concerns. The reader should be able to tell which part of Sonarr owns a decision, which part owns persisted work, and where the external provider boundary begins.
A control plane is the part of a system that decides when work should happen and coordinates the actors that perform it. In Sonarr, it sits between the desired library state and the external services that supply metadata, releases, downloads, or files. It does not replace those services. It makes their participation repeatable, bounded, and visible enough for the reconciliation loop to continue.
A configuration record is not an implementation
Sonarr's provider architecture begins with a distinction that is easy to miss: a configured provider is not the same thing as the code that implements a provider. The provider contract defines the operations and characteristics that the rest of Sonarr may rely on. A provider definition stores configuration such as its name, implementation identity, settings, enablement, and tags. The definition is durable state; the implementation is an executable capability resolved at runtime.
That separation has a practical consequence. A reader can configure an indexer or download client without the central workflow needing to know the provider's internal API. The workflow asks for the common capability. A factory selects a matching implementation, attaches the persisted definition, validates its settings, and returns an instance ready for the operation. The same pattern can support many providers while leaving provider-specific details at the boundary.
The generic provider factory also manages lifecycle changes. It discovers implementations through the application's composition system, exposes default definitions, resolves configured instances through dependency injection, and publishes events when definitions are added, updated, or deleted. It removes stored definitions whose implementation is no longer available. These behaviors keep configuration from becoming a set of dead records that the runtime quietly cannot execute.
Indexer and download-client factories add domain-specific characteristics. An indexer reports whether it supports RSS or different search modes, while a download client reports its protocol and handling availability. Both can be filtered by tags and recent failure status, and both can record success or failure. These facts influence whether a provider participates in the next operation, but they do not make every provider behavior identical. The source explicitly supports extensibility at the contract level; it does not promise that all third-party services have the same reliability or settings semantics.
The host composes the core, API, HTTP, and live-messaging assemblies through the dependency-injection container. This arrangement lets the central workflow ask for contracts rather than constructing one particular provider directly. It also explains why the system can expose user-facing control and live progress without making those concerns the owners of release selection or file import.
Consider what happens when a configured provider changes. The durable definition can remain the record of the reader's settings while the factory resolves a fresh implementation instance and reapplies the definition. Validation can prevent an invalid configuration from becoming an active provider, and lifecycle events can tell other parts of the application that the available capability has changed.