Wiring#

What Is It?#

Wiring is how PSW automatically connects apps to each other. When you install Sonarr and Prowlarr , they need to know about each other — Sonarr needs Prowlarr’s address and API key to search for content. Wiring takes care of that for you, automatically, without you touching a single settings page.

Think of it like an electrician connecting outlets in your house. You decide where the appliances go (which apps on which targets ) — the electrician wires them together so they can talk to each other.

How Is It Different from Conventions?#

PSW has two integration systems that work together:

ConventionsWiring
What they doConnect apps to shared infrastructure (routing, SSO, monitoring, backup, homepage)Connect apps directly to each other
How they workWrite config files that aggregators collectMake API calls, edit config files, run CLI commands
StyleDeclarative (write a file, let the aggregator pick it up)Procedural (call an API, check the result, create the entry)
ExamplesTraefik gets a route file for JellyfinSonarr registers itself in Prowlarr via Prowlarr’s API
When they runDuring the aggregator sync phase of the integration passAfter the aggregator sync, in the same integration pass

Conventions are about connecting apps to the platform (how do I reach this app? who can log in? is it monitored?). Wiring is about connecting apps to each other (Sonarr talks to Prowlarr, Backrest sends alerts to ntfy, Home Assistant connects to the MQTT broker).

How Apps Declare Their Connections#

Each app declares its automation in meta.yml using three complementary sections:

integrations — a summary of which apps this one can connect to (used for neighborhood filtering ):

integrations:
  - prowlarr
  - sabnzbd
  - qbittorrent
  - ntfy

setup_reconcilers — first-boot configuration of this app’s own internals (admin account, API key, database connection). These run inline during deploy, between “service ready” and “sidecars start”:

# sonarr/meta.yml (simplified)
setup_reconcilers:
  - type: arr.setup.postgres
    requires: [postgres]
  - type: arr.setup.api_key
  - type: arr.setup.auth

integration_reconcilers — the actual wiring to other apps. These run in the integration pass after every target has deployed:

# sonarr/meta.yml (simplified)
integration_reconcilers:
  - type: arr.prowlarr_app
    requires: [prowlarr]
    params:
      api_version: v3
      sync_categories: [5000, 5010, 5020, 5030, 5040, 5045, 5050]
  - type: arr.download_client.sabnzbd
    requires: [sabnzbd]
    params:
      media_type: tv
      api_version: v3
  - type: arr.ntfy
    requires: [ntfy]
    params:
      api_version: v3

Each requires entry is checked before the reconciler runs. If Prowlarr is installed but qBittorrent isn’t, PSW only runs the Prowlarr reconciler — it silently skips the qBittorrent one. No errors, no wasted work.

This means adding wiring support to a new app is as simple as adding entries to its meta.yml — no code changes to any central file needed. See app metadata for the full schema.

Setup vs Integration Reconcilers#

The two sections exist because they solve different problems and run at different times:

setup_reconcilersintegration_reconcilers
ScopeConfigures this app — admin user, API key, onboarding wizard, auth modeConnects this app to others — register in Prowlarr, add a download client, wire up notifications
When it runsInline during the deploy phase, after the app’s main service reports ready but before sidecars startIn the integration pass, after every target has finished deploying
What it depends onOnly this app’s service being up (plus any explicit requires)Other apps being deployed and reachable
What it producesSecrets that get written back to SOPS via callback files (API keys, admin tokens)API records, config entries, monitor definitions in other apps
If it failsDeploy fails fast — sidecars don’t start, health checks don’t passIntegration pass reports the failure; other apps are untouched

Why split them? Setup has to finish before sidecars start (exporters need the API key, backup sidecars need the admin account), and it only needs the app itself. Integration needs the whole neighborhood to be up. Treating them the same would either delay deploy waiting for neighbours or run integration too early. The split makes both parts fast, correct, and easy to reason about.

What Happens During Wiring#

When convergence deploys apps, it runs the integration pass at the end. The wiring step:

  1. Checks readiness — waits for each app to start up and respond (up to 30 seconds)
  2. Runs reconcilers — executes the wiring logic for each app-to-app connection
  3. Verifies idempotency — if the connection already exists and is correct, does nothing
  4. Records results — reports what was created, updated, or skipped

Each connection is handled by a reconciler — a piece of code that knows how to wire two specific apps together. Reconcilers are idempotent: running them twice produces the same result as running them once.

Types of Wiring#

Reconcilers use different strategies depending on what the apps support:

API Calls (Most Common)#

Most apps have a REST API where you can configure integrations. The reconciler calls the API to check if the connection exists, and creates or updates it if needed.

Example — Sonarr → Prowlarr:

  1. Call Prowlarr’s API to list registered apps
  2. Check if Sonarr is already registered
  3. If not, POST to register Sonarr with its URL and API key
  4. If it exists but the config drifted, PUT to update it

Config File Editing#

Some apps store their config in JSON or YAML files inside their container. The reconciler reads the file, modifies it, and writes it back.

Example — Backrestntfy :

  1. Read Backrest’s config.json from inside the container
  2. Add an ntfy notification hook to each backup repository
  3. Write the modified config back
  4. Restart Backrest to pick up the changes

CLI Commands#

Some apps expose management through command-line tools inside their container. The reconciler runs commands via SSH.

Example — ForgejoAuthelia :

  1. Run forgejo admin auth list inside the container to check existing auth sources
  2. If no Authelia OIDC source exists, run forgejo admin auth add-oauth with the right parameters
  3. If it exists but needs updating, run forgejo admin auth update-oauth
  4. Call Forgejo’s REST API to link the admin user account

Systemd Timers#

Some integrations need periodic tasks rather than a one-time configuration.

Example — TdarrJellyfin :

  1. Create a systemd service file that triggers a Jellyfin library refresh
  2. Create a systemd timer that runs the service periodically
  3. Enable and start the timer

Authentication Flows#

Some apps require multi-step authentication before they can be configured.

Example — Home AssistantMosquitto (MQTT broker):

  1. Authenticate to Home Assistant’s login flow
  2. Exchange the auth code for an access token
  3. Check if MQTT is already configured
  4. If not, initiate a config flow for the MQTT integration
  5. Submit the Mosquitto broker address and port

Smart Execution#

Wiring doesn’t blindly run everything on every convergence cycle. It rides on PSW’s execution plan , which uses neighborhood pruning to be efficient:

  • If only Prowlarr changed, the plan keeps Prowlarr’s own reconcilers plus reconcilers for any app that integrates with Prowlarr (like Sonarr, Radarr, Lidarr) — everything else is dropped
  • If nothing integration-related changed, the reconciler nodes are pruned entirely
  • Reconcilers check the current state before making changes — if everything is already correct, they report “unchanged” and move on

Within a single plan, reconcilers for different apps on different targets run in parallel. Same target, same app, or explicit requires relationships serialize automatically — see execution plan for how the runner decides.

When Apps Are Removed#

Wiring also handles cleanup. When an app is removed (psw app remove sonarr), the teardown reconcilers run to undo the connections:

  • Delete the Sonarr entry from Prowlarr’s registered apps
  • Remove Sonarr’s monitor from Uptime Kuma
  • Clean up any notification entries

This keeps your other apps clean — no stale references to apps that no longer exist.

First-Boot Setup#

Some apps require a one-time setup wizard before their API becomes available. PSW automates this so you never need to open a browser and click through setup pages.

How It Works#

As part of the deploy phase — before the integration pass runs — PSW’s setup reconcilers detect whether an app is freshly installed and complete its initialization automatically:

AppWhat PSW Does Automatically
JellyfinCreates admin account, completes setup wizard, generates persistent API key, installs SSO plugin, creates media libraries
Home AssistantCreates owner account, completes onboarding, generates long-lived Prometheus token
AudiobookshelfCreates root user, extracts API token
Uptime KumaCreates admin account via Socket.IO setup event
LLDAPProvisions default admin user via GraphQL API
SeerrConnects to Jellyfin , syncs libraries, completes setup wizard
Sonarr , Radarr , Prowlarr , LidarrInjects PostgreSQL config, API key, and external auth mode
MosquittoGenerates MQTT password file
ntfyCreates admin user
qBittorrentSets WebUI password, configures download path

Setup reconcilers generate secrets (API keys, tokens) that are automatically persisted to SOPS via callback files. This means the API keys are available for other reconcilers and homepage widgets on all subsequent deployments.

Recovery#

If a generated secret is lost (SOPS corruption, manual reset), setup tasks automatically detect the gap and re-extract the secret by logging in with the stored admin password. No manual intervention needed.

Key Ideas#

  • Automatic — you never configure app-to-app connections by hand; PSW handles it
  • Self-configuring — apps that need a first-boot setup wizard are initialized automatically, no browser required
  • Idempotent — wiring runs on every convergence cycle and only makes changes when needed
  • Conditional — connections are only made when both apps are deployed; missing optional integrations are silently skipped
  • Reversible — when an app is removed, its wiring is cleaned up automatically
  • Efficient — neighborhood filtering ensures only affected connections are checked
  • Diverse — different apps need different approaches (API calls, config files, CLI, systemd), and the system handles them all