Skip to content

Sources, targets, and profiles

A source plugin reads provider observations and emits JSON-safe canonical items. It may expose library scopes, account authentication, actions, webhooks, collections, ratings, progress, playback state, watchlist membership, and history. It never decides how a target should be changed.

A source instance is one configured connection. Plex currently models each server as a separate instance, even when servers share one Plex account.

A target plugin maps canonical items to target references, reads current remote state, writes and reverts opaque operations, and owns provider-specific reconciliation. It declares supported media, authentication, features, actions, rating scale, and HTTP pacing/retry policy.

A target instance stores connection settings and encrypted credentials for one account. It does not store profile decisions such as whether ratings are enabled.

A profile is the declarative relationship between exactly one source instance and one target instance. It owns:

  • selected source libraries or other scope;
  • every enabled feature and its options;
  • webhook enablement and dry-run preference;
  • full-baseline and incremental fingerprints;
  • feature state, manual mappings, unmatched counters, and ignores;
  • managed custom-list bindings;
  • a lifecycle instance ID so history remains distinct if an ID is deleted and reused.

Rules therefore belong to profiles, not providers. Two profiles can use the same Plex and AniList accounts with different libraries and protections.

The resolver intersects source observations, target capabilities, standard SDK features, and provider-specific features. Available features are materialized when the profile is created. Unsupported features are shown with a reason or omitted from the normal editor.

Feature versions prevent silent behavior changes. When a feature contract is upgraded, an existing profile receives Needs review and that feature does not run until the profile is saved again.

Production plugins are folders under app/sync_v2/sources or app/sync_v2/targets, discovered through create_plugin(). They may import the public provider SDK and their own package, but not the database, FastAPI, the core sync service, or another concrete provider. This internal plugin architecture is also the boundary required for possible external packages in the future.