Commit Graph

4 Commits

Author SHA1 Message Date
mauriceboe edeb097d88 feat(plugins): lifecycle polish — uninstall, error log, egress guard, widget slot (M8)
- Uninstall with data disposition: POST /api/admin/plugins/:id/uninstall kills the
  plugin, removes its code + DB metadata, and (deleteData) drops its data dir,
  error log and per-user settings.
- Error log: GET/DELETE /api/admin/plugins/:id/errors — the plugin's own crash /
  request-failure log, surfaced in the admin panel.
- Egress guard: the isolated child wraps global fetch and refuses any outbound
  host not in the plugin's declared egress[]; with none declared, all outbound is
  blocked. Process-level defense in depth (the container runtime enforces it at
  the network layer in v2).
- Admin → Plugins is now actionable: activate / deactivate / uninstall, a
  registry browser (install), and per-plugin error log. i18n across all locales.
- Dashboard widget slot: active widget plugins render as sandboxed cards.

The trek:request core-data bridge + photo/calendar hook consumers remain follow-ups.
2026-07-03 18:08:11 +02:00
mauriceboe be07f29acd feat(plugins): secure installer — manifest, discovery, safe extract/fetch/scan (M4)
Plugins placed on the /plugins volume are now discovered, validated and registered
as inactive, ready to activate.

- manifest.ts: strict trek-plugin.json validation (id/version/type, known
  permissions only, egress required with http:outbound, native modules rejected).
- discovery.ts: scans the volume on startup + on demand (POST /api/admin/plugins/
  rescan), upserts rows INACTIVE, refreshes settings-field descriptors, and never
  downgrades or wipes an already-installed plugin's status / grants / config.
  Invalid or native-carrying plugins are skipped and logged.
- Activation now grants the DECLARED permissions (the consent gate) and persists
  them before spawning.
- install/ utilities for the registry installer (M5), each independently tested:
  - safe-fetch: host allowlist (GitHub only) + private-IP refusal + manual
    redirect following + size cap + sha256 (constant-time compare).
  - safe-extract: zip/tar-slip-safe extraction with its own minimal tar.gz + zip
    readers; rejects traversal, absolute paths, symlinks, oversized/too-many
    entries, and unsupported formats.
  - native-scan: refuses .node / binding.gyp / prebuilds, never follows symlinks.
2026-07-03 17:26:46 +02:00
mauriceboe 037eb8a209 feat(plugins): sandboxed page/widget frames + trekBridge (M3)
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.
2026-07-03 17:12:56 +02:00
mauriceboe d823ac443b feat(plugins): activation, HTTP route proxy + instance settings (M2)
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.
2026-07-03 16:50:23 +02:00