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.
Plugins can now render UI. Page plugins appear as a nav entry and open a
full-page sandboxed iframe; the frame talks to TREK only over the postMessage
bridge.
- Server serves plugin client assets at /plugin-frame/:id/* with a strict path
guard and a locked-down, per-plugin CSP (default-src none; connect-src limited
to declared outbound hosts; sandbox WITHOUT allow-same-origin -> opaque origin,
so the frame can't read the session cookie or the parent DOM). Global CSP
frameSrc relaxed from 'none' to 'self' for exactly these frames.
- GET /api/plugins feed lists active plugins for the client.
- Client: pluginStore + PluginFrame (the trekBridge host) authenticates every
inbound message by SENDER WINDOW IDENTITY (event.source), not by a claimed id
or origin; pushes context (theme/locale/tripId/userId), validates navigation,
renders notifications as text, resizes widgets, and proxies trek:invoke to the
plugin's own routes host-side (session cookie stays with the host).
- Page route /plugins/:id + Navbar nav injection for page plugins.
Dashboard widget slot and the trek:request core-data bridge are deferred to a
later slice.
Wires the isolated runtime into TREK. Admins can now activate a plugin from the
panel and its HTTP routes work end to end, still behind the kill switch.
- PluginRuntimeService owns the supervisor: activate spawns the child with its
granted permissions + decrypted instance config, deactivate kills it, status
and errors are persisted to the plugins / plugin_error_log tables, and active
plugins are booted on startup (OnModuleInit).
- Bidirectional RPC: the child now handles host→child invokes (routes/jobs) and
reports its declared routes on load; the supervisor gained invoke()/routesOf().
- /api/plugins/:id/* proxy controller — a single static route that matches the
plugin's declared routes, enforces per-route auth (auth:false routes are public
for OAuth callbacks/webhooks), forwards only a whitelisted request view (never
the session cookie), and strips unsafe response headers.
- Admin endpoints: POST :id/activate, POST :id/deactivate, GET/PUT :id/config —
instance settings with secret fields encrypted (apiKeyCrypto) and masked.
- Kill switch moved to its own module so it never collides with test config mocks.
Photo/calendar hook consumers are deferred to a later slice.
First slice of the plugin system. Lays down the data model and a read-only
admin surface; nothing executes yet.
- Migration 155: plugins, plugin_meta_migrations, plugin_error_log and
plugin_settings_fields tables. Plugin data will live in a per-plugin sqlite
file under /plugins-data, never in these tables.
- New Nest module server/src/nest/plugins with GET /api/admin/plugins
(admin-gated, returns the installed list + the runtime-enabled flag).
- TREK_PLUGINS_ENABLED kill switch (config.pluginsEnabled), off by default.
- Admin → Plugins tab with a read-only panel: installed list, status badges,
and a clear banner when the runtime is disabled by server config.
- i18n keys for the tab and panel across all locales.
Install, activation, the isolated runtime and the registry browser follow in
later slices.