Generalize the half-built discovery plugins into a real plugin system: plugin
TYPES (the plugins/<category>/<type>.js modules with manifests) and loadable,
configurable, multi-copy plugin INSTANCES (PluginInstance ORM model) managed
from a dedicated /plugins page and /api/plugins API, with per-instance secrets
in OpenBao at secret/plugins/<id>/conf.
- plugin_registry.js: getTypes/getModule/splitConfig/mask + required-field helpers
- PluginInstance model (Sequelize): id/pluginType/category/name/slug(unique)/
enabled/cron/config(json, non-secret)/lastRun*; registered in models/index.js
- plugin_secrets.js: read/write/remove/mergeForRun over @simpleworkjs/bao-conf
- scheduler.js: schedules from the DB registry; per-instance stable BullMQ
JobScheduler ids (plugin:<id>) for load/unload; legacy migration from
conf.discovery.plugins on first boot (idempotent, empty-table-guarded)
- api_plugins.js (replaces routes/plugins.js): types/list/get/create/update/
secrets/test/load/unload/run/delete/runs; admin-gated; secrets always masked
- /plugins page (plugins.ejs) + nav; Agents & Scheduler tab removed from
/directory; /docs/agents aliased to /docs/plugins
- proxmox/unifi/nmap gained manifests (configSchema/validate/run alias)
- tests/plugins.test.js: registry unit + plugin_secrets (mocked bao-conf) +
PluginInstance model round-trip/unique-slug
- docs (plugins.md, vault.md, _config.yml, API.md) + 1.16.1 -> 1.17.0
Requires theta-suite >= v1.30.1 for the sso-broker secret/plugins/* grant;
fails-soft with a clear error if absent.
Co-Authored-By: Claude <noreply@anthropic.com>
Closes the end-user half of the directory and adds nested LDAP groups.
The directory could describe the lab but could not tell anyone what they had
or how to reach it, and several of the paths meant to do so were silently
returning nothing:
- GET /api/discovery/me resolved groups from req.user.groups, which does not
exist (req.user carries memberOf), so it returned only isPublic resources
for every human caller -- "My Services" was blank for everyone. The same
read made isDirectoryAdmin() false for real admins.
- The portal's "Discover More Services" called the admin-gated endpoint and
swallowed the 403, so it never rendered for non-admins at all.
- Services reported no address, because /me had reimplemented getMyAccess
without its parent-walking resolution.
Adds the catalog at /, self-service access requests, and admin access
visibility (per-resource counts, and the reverse "what can this user reach").
Nested groups come in two halves. groupOfNames.member already accepts a group
DN, so nesting needs no schema -- what it needs is resolution, which no
released OpenLDAP performs. The all-in-one image therefore builds slapd from a
pinned master commit for the nestgroup overlay, and the app computes the
closure itself when pointed at a server without it. Both paths are covered.
member-values is deliberately left out of nestgroup-flags: it expands `member`
when reading a group, which destroys the distinction between "listed here" and
"reachable through a nested group" and is not recoverable afterwards.
Full suite green in both resolution modes: 215 passed, 2 skipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The GitHub Pages site used the generic jekyll-theme-cayman theme --
purple gradient hero, no site nav, no per-page SEO. Replaced with a
custom layout that mirrors the actual app UI: dark fixed navbar with
the theta42 logo, Bootstrap 5 + Font Awesome (same stack the app
uses), content in a card, dark footer matching bottom.ejs
(copyright, MIT license, GitHub, Changelog links).
- New cross-page nav (Home/Deployment/Configuration/OAuth/LDAP/
Changelog) -- there was previously no way to get from one docs
page to another except a single "Back to Home" link per page.
- SEO: jekyll-seo-tag + jekyll-sitemap (both GitHub-Pages-supported
plugins, no custom build needed) -- real per-page meta description,
Open Graph/Twitter card tags, canonical URLs, JSON-LD, sitemap.xml,
and a robots.txt referencing it. Added a real description to every
page's front matter (none existed before).
- Mobile: Bootstrap's responsive grid + collapsible navbar; the
screenshot pairs in index.md (inline width="49%" for a two-up
desktop layout) now stack to full-width below 576px instead of
squeezing illegibly small.
Verified with a real Jekyll build (jekyll/jekyll Docker image, no
Ruby available locally) + Playwright: desktop and mobile (375px)
screenshots of the home and deployment pages, mobile nav toggle
open/close, active-link highlighting per page, zero console/page
errors, and confirmed real SEO output (meta description, OG/Twitter
tags, canonical, JSON-LD, sitemap.xml, robots.txt) via curl against
the served site.
- Rewrite docs/index.md as a short landing page (what it is, screenshots,
why this over the alternatives, features, a minimal "get it" snippet)
instead of a full documentation dump — full docs live in the repo
(README, docs/*.md) and are linked from here.
- Cross-link to Proxy and theta-env's own Pages sites.
- Screenshots are now clickable (open full size) on both the Pages site
and the README.
- Disable show_downloads in docs/_config.yml — the Cayman theme's
"Download .zip/.tar.gz" buttons are gone; "View on GitHub" (which links
back to the repo) is the only header link now.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>