flytegg / flytegg/plugin-portal
Proposal: external plugin sources for GitHub Releases and GeyserMC downloads
- Dominant language
- Kotlin
- Stars
- 92
- Forks
- 15
- Avg merge
- 49m
- Merged PRs (30d)
- 4
Description
## Summary
Redesign adapter plugins as first-class external plugin sources backed by a user-editable `external-plugins.yml` and separate runtime state. Implement GitHub Releases and GeyserMC downloads as the first providers.
Credit to [Zoriot](https://github.com/Zoriot) for the report and adapter feedback.
## Problem
The current adapter system is hard to explain and not reliable enough to recommend as a polished feature. It mixes provider-specific behavior into a startup-only flow and stores adapter state separately, but without a clear user-facing model.
Server owners need a way to manage plugins that are not available through normal Plugin Portal marketplace indexing, without reuploading third-party artifacts to marketplaces or relying on scanner support. Common examples are GitHub Releases-hosted plugins and Floodgate from GeyserMC downloads.
## Design principles
- Model these as external plugins, not adapters. `adapter` should be an internal implementation detail.
- Keep marketplace-managed plugins and external plugins separate. Do not store external plugin state in the existing `plugins.json`.
- Make user intent editable in YAML, because that matches the Bukkit/Paper server ecosystem.
- Store runtime state separately from config.
- Normalize every provider into the same internal artifact model so update/install logic is shared.
- Start with GitHub Releases and GeyserMC downloads only. Jenkins and custom providers can be separate issues.
## Proposed files
```text
plugins/PluginPortal/plugins.json
plugins/PluginPortal/external-plugins.yml
plugins/PluginPortal/external-plugins-state.json
```
Responsibilities:
- `plugins.json`: existing marketplace-installed plugin state only.
- `external-plugins.yml`: user-editable desired external plugin config.
- `external-plugins-state.json`: installed external plugin state, hashes, versions, check timestamps, and last errors.
## Proposed config
```yaml
plugins:
floodgate:
source: geysermc:floodgate
artifact: spigot
file: floodgate-spigot.jar
updates: manual
viaversion:
source: github:ViaVersion/ViaVersion
asset: "^ViaVersion.*\\.jar$"
file: ViaVersion.jar
prereleases: false
updates: manual
```
Update policy:
- `manual`: only update through explicit external update commands.
- `auto`: eligible for automatic update behavior if enabled.
- `disabled`: tracked but never updated.
`/pp external updateAll` should be treated as an explicit operator action and can include `manual` and `auto`. Background/startup updates should only touch `auto`.
## Proposed state
```json
{
"plugins": {
"floodgate": {
"provider": "geysermc",
"source": "floodgate",
"artifact": "spigot",
"version": "2.2.5",
"build": "138",
"sha256": "44bdb908e2fb4ff1b974d5313d048a625a21555a9844cfb86256a98e8e1c6bd1",
"file": "plugins/floodgate-spigot.jar",
"installedAt": "2026-07-02T06:30:00Z",
"lastCheckedAt": "2026-07-02T06:30:00Z"
}
}
}
```
## Internal artifact model
Every provider should resolve to the same normalized shape:
```kotlin
data class ExternalArtifact(
val provider: String,
val sourceId: String,
val artifactId: String,
val version: String,
val build: String?,
val filename: String,
val downloadUrl: String,
val sha256: String?,
val publishedAt: Instant?,
val changelog: String?
)
```
Plugin Portal can then use one generic flow:
1. Read `external-plugins.yml`.
2. Ask the provider for the latest matching artifact.
3. Compare the artifact to `external-plugins-state.json`.
4. Download if requested.
5. Verify hash when available.
6. Write state.
7. Place the jar in the configured target file or Bukkit update folder.
## GitHub Releases provider
Example:
```yaml
plugins:
viaversion:
source: github:ViaVersion/ViaVersion
asset: "^ViaVersion.*\\.jar$"
file: ViaVersion.jar
prereleases: false
updates: manual
```
Behavior:
- Call the GitHub Releases API.
- Pick the latest non-prerelease by default.
- Include prereleases only when `prereleases: true`.
- Match release assets using the configured regex.
- Fail clearly if zero assets match.
- Fail clearly if multiple assets match, unless a future explicit strategy is added.
- Track release `tag_name` as the version, with release/asset IDs as stable metadata if needed.
- Support optional token configuration later for private repos or rate limits.
## GeyserMC downloads provider
Example:
```yaml
plugins:
floodgate:
source: geysermc:floodgate
artifact: spigot
file: floodgate-spigot.jar
updates: manual
```
Behavior:
- Call `https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest`.
- Select `downloads[artifact]`.
- Current Floodgate artifacts include `spigot`, `bungee`, and `velocity`.
- Build the download URL as `https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/{artifact}`.
- Track both API `version` and `build`.
- Verify the downloaded jar against the API-provided `sha256`.
## Commands
Suggested command surface:
```text
/pp external list
/pp external check
/pp external install
/pp external update
/pp external updateAll
/pp external reload
```
## Non-goals
- Preserve the current adapter schema.
- Store external plugin state in `plugins.json`.
- Implement Jenkins support in this issue.
- Implement a custom Floodgate-only one-off path instead of a provider model.
- Rehost or mirror third-party artifacts to Plugin Portal or public marketplaces.
## Migration notes
The existing `adapters.yml` can be ignored for the first implementation or migrated best-effort later. The new model should be allowed to replace the old adapter system instead of preserving its current behavior.
Contributor guide
Research direction
Start by reading plugins/PluginPortal/plugins.json and the proposed external-plugins.yml and external-plugins-state.json, then inspect the existing adapter startup flow. Trace how the GitHub Releases and GeyserMC providers should produce the shared ExternalArtifact model and how the external commands fit together. Done means both providers, separated configuration and state, update policies, hash verification, and the listed commands work without changing marketplace state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- json, kotlin, yaml
- Domain
- backend, cli, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100