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).
Admin list: flush-left header like Addons, type/reviewed badges, runtime
health as a dot on the icon tile (text badge only for problem states),
description + source-repo link on installed cards, manifest icon.
Browse: cards show the plugin screenshot (docs/screenshot.png at the pinned
commit) and open a detail dialog fed by GET /api/admin/plugins/registry/:id —
live-manifest permissions in plain language, egress hosts, setup preview,
repo/homepage links. Manifest fetched server-side through safeDownload at the
reviewed commit, cached per plugin for 30 min and only when a detail opens.
Also fixes the update flow (restart the running child around the install,
keep the admin's enabled intent instead of force-activating disabled
plugins), guards the icon lookup against Object.prototype names, reserves
ids that would shadow static admin routes, stops negative-caching failed
manifest fetches, and makes the version compare prerelease-safe.
Widget plugins can now declare capabilities.widget.slot 'hero' to render as a
transparent, click-through overlay sitting on the boarding-pass bar's top edge
(migration 157 persists capabilities; manifest validation server+SDK, feed
exposes the slot, dashboard mounts hero frames above the pass). Sidebar stays
the default slot.
Replaces the trip-countdown example with Koffi, the TREK mascot: an animated
suitcase with a 14-state behavior engine driven by real trip data — walking,
waving, napping, trolley rolls, passport-stamp stickers, a split-flap luggage-
tag countdown under 7 days, and sunglasses while the trip runs. Validated by
the SDK suite like any author plugin; published as mauriceboe/trek-plugin-koffi
in the registry.
Migrations 156/157 follow the idempotent ALTER pattern (the reconciliation
test re-runs everything from v135, so duplicate-column must stay non-fatal).
- Plugin frame CSP: the frame runs at an opaque origin (sandbox without
allow-same-origin), so script-src 'self' matches nothing and the widget's own
script never runs (stuck on 'Loading…'). Allow 'unsafe-inline' — the sandbox,
not this directive, is the isolation boundary, and the plugin author controls
the frame code either way.
- Fix a no-constant-binary-expression eslint error in registry.test.ts that was
failing the server lint:check (eslint .) in CI.
Connects TREK to the static GitHub registry (mauriceboe/TREK-Plugins). The
registry repo + CI gates were already live; this is the server side.
- registry.service: fetches the single aggregated dist/index.json (never
per-plugin GitHub API calls — the HACS rate-limit lesson), caches it 30 min,
soft-fails to a stale/empty registry, and installs a pinned version through
the M4 pipeline: safe download -> sha256 verify -> slip-safe extract ->
manifest re-validate -> native re-scan -> atomic move -> discover (inactive),
recording repo/commit/sha provenance. Handles the codeload {repo}-{sha}/
wrapper directory.
- Admin endpoints: GET /api/admin/plugins/registry (browse metadata) and
POST /api/admin/plugins/install { id, version }. Install never executes code;
activation stays a separate, deliberate step.