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:
| Conventions | Wiring | |
|---|---|---|
| What they do | Connect apps to shared infrastructure (routing, SSO, monitoring, backup, homepage) | Connect apps directly to each other |
| How they work | Write config files that aggregators collect | Make API calls, edit config files, run CLI commands |
| Style | Declarative (write a file, let the aggregator pick it up) | Procedural (call an API, check the result, create the entry) |
| Examples | Traefik gets a route file for Jellyfin | Sonarr registers itself in Prowlarr via Prowlarr’s API |
| When they run | During the aggregator sync phase of the integration pass | After 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
- ntfysetup_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.authintegration_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: v3Each 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_reconcilers | integration_reconcilers | |
|---|---|---|
| Scope | Configures this app — admin user, API key, onboarding wizard, auth mode | Connects this app to others — register in Prowlarr, add a download client, wire up notifications |
| When it runs | Inline during the deploy phase, after the app’s main service reports ready but before sidecars start | In the integration pass, after every target has finished deploying |
| What it depends on | Only this app’s service being up (plus any explicit requires) | Other apps being deployed and reachable |
| What it produces | Secrets that get written back to SOPS via callback files (API keys, admin tokens) | API records, config entries, monitor definitions in other apps |
| If it fails | Deploy fails fast — sidecars don’t start, health checks don’t pass | Integration 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:
- Checks readiness — waits for each app to start up and respond (up to 30 seconds)
- Runs reconcilers — executes the wiring logic for each app-to-app connection
- Verifies idempotency — if the connection already exists and is correct, does nothing
- 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:
- Call Prowlarr’s API to list registered apps
- Check if Sonarr is already registered
- If not, POST to register Sonarr with its URL and API key
- 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.
- Read Backrest’s
config.jsonfrom inside the container - Add an ntfy notification hook to each backup repository
- Write the modified config back
- 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 — Forgejo → Authelia :
- Run
forgejo admin auth listinside the container to check existing auth sources - If no Authelia
OIDC source exists, run
forgejo admin auth add-oauthwith the right parameters - If it exists but needs updating, run
forgejo admin auth update-oauth - Call Forgejo’s REST API to link the admin user account
Systemd Timers#
Some integrations need periodic tasks rather than a one-time configuration.
- Create a systemd service file that triggers a Jellyfin library refresh
- Create a systemd timer that runs the service periodically
- Enable and start the timer
Authentication Flows#
Some apps require multi-step authentication before they can be configured.
Example — Home Assistant → Mosquitto (MQTT broker):
- Authenticate to Home Assistant’s login flow
- Exchange the auth code for an access token
- Check if MQTT is already configured
- If not, initiate a config flow for the MQTT integration
- 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:
| App | What PSW Does Automatically |
|---|---|
| Jellyfin | Creates admin account, completes setup wizard, generates persistent API key, installs SSO plugin, creates media libraries |
| Home Assistant | Creates owner account, completes onboarding, generates long-lived Prometheus token |
| Audiobookshelf | Creates root user, extracts API token |
| Uptime Kuma | Creates admin account via Socket.IO setup event |
| LLDAP | Provisions default admin user via GraphQL API |
| Seerr | Connects to Jellyfin , syncs libraries, completes setup wizard |
| Sonarr , Radarr , Prowlarr , Lidarr | Injects PostgreSQL config, API key, and external auth mode |
| Mosquitto | Generates MQTT password file |
| ntfy | Creates admin user |
| qBittorrent | Sets 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