Repository navigation
chore(plugins): plugin API 1.0.1, process security limitations, remove subsonic plugin - #44
Merged
tajanthind merged 2 commits intoSep 10, 2026
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three plugin-ecosystem hygiene items in one PR:
1. Plugin API version 1.0.0 → 1.0.1
PLUGIN_API_VERSIONbumped (fnack/plugin_apiSDK_VERSIONmirrors it automatically). The security batch changed plugin-contract behavior (permissions enforced on every facade,context.http = Nonewithoutnetwork, secret-at-rest); manifests declaring^1.0stay compatible — a minor, non-breaking bump that records the change.2. Remaining security limitations — detect + document
plugins/manager.py::_network_capable_imports, AST): a plugin importingrequests/urllib/http.client/httpx/aiohttp/urllib3/socket/websocketswithout declaringnetworkgets a prominent warning at load. Warning-only by design (static scans can false-positive; in-process plugins are trusted, not sandboxed). Newtests/architecture/test_plugin_network_honesty.pypins the scanner and holds every bundled official plugin to the rule (network imports ⇒networkdeclared — all six current importers already do).AUTHORING.mdwith concrete guidance: back upplugin_secret.keywith the DB; restore without it ⇒ re-enter secrets; deleting it revokes all stored plugin secrets.3. Remove
fnack.subsonicfrom the ecosystem (core side)Paired with fnack-plugins PR #1 (catalog/index/README/dist removal).
bundled_plugins/fnack.subsonicdeleted.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).test_essential_pluginsoptional list;test_plugin_boundaryno longer requires a bundledserver.extensionprovider (none ships; catalog-installablefnack.lidarrcovers the capability).Verification