Closes the four gaps the security review surfaced: - OS permission model on the prod plugin child (Node --permission with fs-read scoped to the compiled server dir + the plugin's own code dir, no fs-write/child_process/worker/native). A plugin can no longer read trek.db or the .jwt_secret/.encryption_key files, nor shell out — the direct-fs and RCE escapes that bypassed the RPC layer. Opt-out via TREK_PLUGIN_PERMISSIONS=off. - Egress guard extended from fetch to the net.Socket connect choke point, so node:http/https/net/tls obey the declared-egress allowlist too (no declared egress = no outbound). Under the permission model there is no clean escape to an unwrapped runtime. Kernel/network-namespace containment remains the container step. - Trip reads are membership-checked against the acting user the HOST binds from the authenticated invocation, not an asUserId the plugin supplies; a job/onLoad (no user) can't read user-scoped trips. - Optional minisign (Ed25519) author signatures verified offline, TOFU-pinned (migration 158). Unsigned plugins install on sha256 alone; a signed plugin can't silently drop its signature or swap its author key. Server suite green (permission-model activation verified against the Koffi reference plugin on dev1).
4.6 KiB
Publishing a Plugin
Plugins are distributed from a static registry — the TREK-Plugins GitHub repo. There is no upload server and no account: you host your plugin in your own GitHub repo and list it with a pull request.
1. Host your plugin
Put your plugin in a public GitHub repo (convention: trek-plugin-<id>). It
must contain a trek-plugin.json at the root, a built server/index.js, a
filled-in README.md, and (page/widget) a built client/.
2. Tag a release
Tag vX.Y.Z where X.Y.Z equals version in your manifest. Either attach a
plugin.zip release asset, or rely on the GitHub source archive at that commit.
3. Validate locally
npx trek-plugin validate .
This runs the exact checks the registry CI runs — a local pass predicts a CI pass.
4. Fork the registry and open a PR
You don't have write access to the registry, so fork
TREK-Plugins to your own account,
add your entry there, and open a pull request from your fork back to the
registry's main branch.
Add one file, registry/plugins/<id>.json:
{
"id": "flight-tracker",
"name": "Flight Tracker",
"author": "Your Name",
"description": "Live flight status widget.",
"repo": "you/trek-plugin-flight-tracker",
"type": "widget",
"versions": [{
"version": "1.0.0",
"gitTag": "v1.0.0",
"commitSha": "<40-hex commit the tag points at>",
"downloadUrl": "https://github.com/you/trek-plugin-flight-tracker/releases/download/v1.0.0/plugin.zip",
"sha256": "<sha256 of that artifact>",
"minTrekVersion": "3.2.0",
"apiVersion": 1,
"nativeModules": false
}]
}
CI validates the entry and the artifact, and the maintainer reviews the source at the pinned commit before merging.
What CI enforces
Entry: valid schema · id matches the filename and is a valid slug ·
reserved namespaces (trek-, official-) blocked · your id is bound to your
GitHub owner on first registration (nobody can repoint it later) · homoglyph
check · the release tag exists and points at the pinned commit · manifest parity
· the artifact's SHA-256 matches the pin · no native binaries ·
egress[] present when http:outbound is declared.
README: must exist, have the required sections (What it does / Screenshots / Permissions / Setup), contain at least one screenshot that actually resolves, have real content (not the unfilled template), and explain every declared permission.
Provenance & integrity
commitShapins the exact source the maintainer reviewed (git tags are movable).sha256pins the exact artifact bytes TREK will run (release assets are mutable).
TREK verifies the downloaded bytes against sha256 and refuses to install on a
mismatch. A reviewedAt date on your entry means a maintainer looked at that
exact commit — it is not an ongoing guarantee.
Signing your releases (optional, recommended)
sha256 proves the bytes are the ones the registry vouches for. An author
signature additionally proves the bytes were signed by you — so a compromised
registry cannot ship attacker code under your name. TREK verifies the signature
offline (Ed25519, no external service), and pins your key on first install
(trust-on-first-use): a later release signed with a different key is refused until
an admin re-trusts it.
One-time — create a key:
minisign -G # writes minisign.key (keep secret) + minisign.pub
Put the public key in your registry entry as authorPublicKey (the base64 payload
line from minisign.pub). It is stable across versions.
Each release — sign the artifact:
minisign -Sm plugin.zip # writes plugin.zip.minisig
Add the base64 signature line from plugin.zip.minisig to that version as
signature, next to its sha256. Example entry:
{
"id": "flight-tracker",
"authorPublicKey": "RWQ…base64 minisign public key…",
"versions": [{
"version": "1.2.0",
"sha256": "3b2a…",
"signature": "RUR…base64 .minisig payload…"
}]
}
Signing is opt-in: an entry without authorPublicKey/signature installs on
sha256 alone, exactly as before. But once a plugin has shipped signed, an
unsigned update for it is refused — don't drop the signature between versions.
Updating
Add a new version entry (new version/gitTag/commitSha/sha256) to your
plugin file and PR it. Instances see the update on their next registry poll;
updating is always an explicit admin action, and if a new version requests more
permissions the admin must re-approve.