Skip to content

chore(plugins): plugin API 1.0.1, process security limitations, remove subsonic plugin - #44

Merged
tajanthind merged 2 commits into
mainfrom
fix/plugin-arch-v101-limitations-subsonic-removal
Sep 10, 2026
Merged

tajanthind merged 2 commits into
mainfrom
fix/plugin-arch-v101-limitations-subsonic-removal

Conversation

@tajanthind

Copy link
Copy Markdown
Owner

Three plugin-ecosystem hygiene items in one PR:

1. Plugin API version 1.0.0 → 1.0.1

PLUGIN_API_VERSION bumped (fnack/plugin_api SDK_VERSION mirrors it automatically). The security batch changed plugin-contract behavior (permissions enforced on every facade, context.http = None without network, secret-at-rest); manifests declaring ^1.0 stay compatible — a minor, non-breaking bump that records the change.

2. Remaining security limitations — detect + document

  • Network-import bypass is now detected, not silent. New load-time static scan (plugins/manager.py::_network_capable_imports, AST): a plugin importing requests / urllib / http.client / httpx / aiohttp / urllib3 / socket / websockets without declaring network gets a prominent warning at load. Warning-only by design (static scans can false-positive; in-process plugins are trusted, not sandboxed). New tests/architecture/test_plugin_network_honesty.py pins the scanner and holds every bundled official plugin to the rule (network imports ⇒ network declared — all six current importers already do).
  • Secret-key loss documented in AUTHORING.md with concrete guidance: back up plugin_secret.key with the DB; restore without it ⇒ re-enter secrets; deleting it revokes all stored plugin secrets.

3. Remove fnack.subsonic from the ecosystem (core side)

Paired with fnack-plugins PR #1 (catalog/index/README/dist removal).

  • bundled_plugins/fnack.subsonic deleted.
  • app.py (header docstring, default_disabled, blueprint comment), README.md (Subsonic-server claims), docs/architecture.md (official table row, capability example, media-scan vs server-API distinction now concept-level).
  • Tests updated: doc-gate subsonic test now asserts removal; test_essential_plugins optional list; test_plugin_boundary no longer requires a bundled server.extension provider (none ships; catalog-installable fnack.lidarr covers the capability).

Verification

  • Architecture suite 26/26 pass; smoke test passes.

…nstall, and update

Multi-repo duplicate-id handling was incoherent: browse de-duplicated by id
("newest/whatever-wins"), install picked the FIRST repository that had the
id, and update flags scanned every repo. Ordering (repo insertion time) was
an implicit side effect and there was no provenance in install requests.

New, documented contract (docs/architecture.md, "Marketplace identity"):

- Browse returns ONE entry per (repository, plugin) — duplicate ids across
  enabled repositories are never merged; every entry lists the other
  publishers (`also_in_repos`) and is labelled `installed_elsewhere` when
  the id is installed from a different repository.
- install(plugin_id, version, source_repo_id) — provenance is explicit and
  recorded on InstalledPlugin.source_repo_id. Without a source, an id
  published by >1 enabled repository is REFUSED as ambiguous (candidates
  listed); a version/id the chosen repo does not publish is refused.
- update() re-installs from the repository the plugin was INSTALLED from —
  never from whichever repo is checked first. latest_versions() is
  source-aware so duplicate ids elsewhere never drive update badges.
- Duplicates are detected, not ignored: add/refresh-repository responses
  carry conflict warnings; marketplace cards show "Also in: <repo>" inline.
- Config-as-code export now records source_repo_url per plugin; import
  re-installs from the same repository URL.

UI: install/update/switch buttons pass source_repo_id; switching an id
installed from another repository is an explicit labelled action; conflict
warnings render as toasts + inline card warnings.

New arch test tests/architecture/test_marketplace_repo_identity.py pins the
contract (per-repo browse, ambiguity refusal, provenance recording, source-
aware update, conflict detection).
…s, remove subsonic plugin

Three related "plugin ecosystem hygiene" items:

1. PLUGIN_API_VERSION 1.0.0 -> 1.0.1 (plugins/__init__.py; fnack/plugin_api
   SDK_VERSION mirrors). The security batch (permissions enforced on every
   facade, context.http=None without "network", secret-at-rest) changed the
   plugin contract behaviorally; manifests declaring ^1.0 remain compatible
   (1.0.1 is contained), this is a minor, non-breaking bump that records the
   change.

2. Remaining security limitations processed (detect + document):
   - NEW load-time static scan (plugins/manager.py _network_capable_imports):
     plugins importing network-capable modules (requests/urllib/http.client/
     httpx/aiohttp/urllib3/socket/websockets) WITHOUT declaring "network"
     now get a prominent warning at load — the direct-import bypass of
     context.http is surfaced instead of silent. Warning-only (static scans
     can't be perfect); bundled official plugins are held to the rule by
     tests/architecture/test_plugin_network_honesty.py (network imports =>
     "network" declared).
   - AUTHORING.md documents both remaining limitations with concrete
     guidance: the network-honesty scan + why it warns, and secret-key loss
     (plugin_secret.key must back up with the DB; deleting it revokes all
     stored plugin secrets; restore without it => re-enter secrets).

3. fnack.subsonic removed from the fnack ecosystem (core side):
   - bundled_plugins/fnack.subsonic deleted (paired with fnack-plugins PR
     removing it from the catalog/index/README).
   - app.py: header docstring, default_disabled set, blueprint comment no
     longer reference it.
   - docs/architecture.md: official-plugins table row, server.extension
     capability example and the media-scan vs server-API distinction
     paragraph rewritten at concept level.
   - README: subsonic-server claims removed.
   - Tests updated: test_essential_plugins optional list, doc-gate subsonic
     test now asserts removal + concept distinction, plugin_boundary no
     longer requires a bundled server.extension provider.
@tajanthind
tajanthind merged commit 9a252d0 into main Sep 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant