wmantly b95cb08c41 feat(multi-site): no-inbound relay automation via theta-proxy's existing API tokens
Implements TODO items "service-to-service auth model" and "no-inbound
relay automation" together -- the first was never really "no credential
type exists", it was that nothing wired one of the credential types that
ALREADY exist (theta-proxy, jump-host, and this app each already have
their own self-service API token system, models/api_token.js) into an
actual inter-service call. This is that wiring, not a new invented
credential type.

- utils/proxy_client.js: ensureRelayRoute({host, ip, targetPort}) calls
  theta-proxy's real Host API (GET/POST/PUT /api/host) using a `prx_...`
  token an operator mints on theta-proxy and stores in OpenBao
  (secret/integrations/theta-proxy), same pattern as agent_keys.js.
  Idempotent and best-effort -- never fails the caller if the token/URL
  isn't configured, since this is an enhancement on top of a working
  join, not a join requirement.
- POST /api/site/spokes accepts optional noInbound/meshIp/publicHost
  fields; when a spoke reports itself no-inbound, the master
  best-effort creates/updates the matching relay route automatically.
  SiteSpoke gained the fields + a relayNote for visibility.

Verified against a REAL running theta-proxy container (not mocked):
booted it standalone, logged in as the local admin, minted a real
`prx_` token via its actual API, and drove ensureRelayRoute() against
it for real. Caught a real bug doing this: GET /api/host/:item wraps
the record in `{item, results}`, not flat -- the mocked unit tests
(which I wrote first) all had the flat shape baked in and passed
cleanly, so this only surfaced against the real API. Fixed in both the
implementation and the unit tests' mocked response shape.
2026-08-10 20:31:42 -04:00

Theta Directory

A production-grade, self-hosted OpenID Connect provider, Resource Directory & IAM Engine, and bundled OpenLDAP directory with a modern web console — designed for home-labs and enterprise infrastructure that demand total sovereignty over their identity, secrets, and resource catalog.

It provides a single source of truth for identity (OIDC + LDAP), host/service directory inventory, access control groups, and secrets management running entirely on your own hardware without third-party cloud lock-in.

Theta Directory is deployed as part of Theta Suite, alongside Theta Proxy and Theta Gateway — it isn't installed or run on its own. ./setup.sh wires the whole stack together automatically.

Documentation: https://theta42.github.io/theta-suite/sso/

Screenshots

Dashboard Users
Dashboard User list
Groups OAuth Apps
Groups OAuth clients
Sites & Replication
Sites
Agent Capabilities & Metrics Agent Install (Join Key)
Agent capabilities & metrics Agent install with join key

Features

  • OpenID Connect / OAuth 2.0 provider — issue your own access, refresh, and ID tokens; protect your apps with standard OIDC login. Discovery document at /.well-known/openid-configuration.
  • Bundled OpenLDAP directory — users, groups, POSIX accounts (posixAccount/inetOrgPerson), SSH public keys, and sudo roles, with memberOf + referential-integrity overlays. This is your single source of truth for identity, not a sidecar.
  • Web management UI — manage users, groups, and OAuth clients from a browser; invite and password-reset flows over email; user self-service for profile and API tokens.
  • Direct LDAP binds — Linux hosts (PAM/SSSD login, LDAP-backed sudo rules, SSH public keys via openssh-lpk) and LDAP-native apps (Gitea, Emby, and anything else that speaks LDAP) use LDAPS (636) or StartTLS against the same directory, so you don't maintain a second user database for them.
  • Personal access tokens — any user can mint a long-lived bearer token to drive the management API from scripts or CI, scoped to their own permissions.
  • Directory & Inventory Graph — full host/service/site graph with resource metadata, automatic LDAP group provisioning (_access / _admin), and Access Request workflows.
  • Subtype Management & Metrics Drivers Engine — 4-tier resolution engine binding subType metadata (systemd, docker, proxmox, wireguard, postgresql, redis, k8s) to operational telemetry, log streaming, and remote lifecycle control.
  • Explicit Secret Inheritance Mode — OpenBao KV-v2 integration with strict upward ancestor lineage (Resource -> Host -> Cluster -> Site), preserving precise secret scoping across services and containers.
  • Multi-Site Support (Geo-Location Scaling) — built-in support for N-Way Multi-Master OpenLDAP replication across physical sites for HA and low latency.

Secrets

Secrets are loaded from OpenBao at boot via @simpleworkjs/bao-conf, which deep-merges secret/sso-manager/conf over the file-loaded config (fail-soft: if OpenBao is unreachable, boot continues from CONF_SECRETS). The SSO authenticates to OpenBao with the scoped VAULT_TOKEN (env, policy sso-broker) — never the root token.

The SSO also acts as the vault broker for the whole stack: it mints per-user (user-<uid>) and per-admin (sso-admin) tokens through the sso-broker token role and exposes the personal-secrets UI at Vault → My Secrets (secret/users/<uid>/*, server-side token injection + path-scope guard) and an admin Apps tab to mint scoped tokens for external apps (secret/apps/<name>/*). The old utils/conf_manager.js was replaced by @simpleworkjs/bao-conf; the admin Configuration UI (/api/conf) now writes secret/sso-manager/conf through bao-conf.set.

The config/*-secrets.js files are operator-edit seed artifacts (gitignored), not the authoritative store. For the full architecture, policies, token model, and rotation procedure, see theta-suite's Secrets docs.

Architecture

┌─────────────┐
│  Browser /  │
│  OIDC apps  │
└──────┬──────┘
       │ HTTP/HTTPS
       ▼
┌────────────────────────┐      ┌─────────────┐
│  Theta Directory       │◄────►│   Redis     │
│  - OIDC provider       │      │ - sessions  │
│  - web UI (:3001)      │      │ - models    │
│  - management API      │      └─────────────┘
└────────┬───────────────┘
         │ ldapi/ldap (localhost)
         ▼
┌────────────────────────┐
│  OpenLDAP (slapd)      │
│  - users / groups      │
│  - LDAPS :636          │─── Linux hosts + LDAP apps bind directly
│  - StartTLS :389       │
└────────────────────────┘

Documentation

The nitty LDAP details (overlay setup, the custom theta42Person schema, the required groups, LDAPS/TLS, direct-bind service accounts) live in:

  • DEPLOYMENT.md — Docker + bare metal, the config layers, the app_* env reference, LDAPS/TLS, backups, troubleshooting.
  • API.md — the management API.
  • docs/ — the same content broken into OAuth/OIDC and LDAP, also published at the unified theta-suite docs site.
  • CHANGELOG.md — what changed in each release.
  • All of the above is also readable from the running app itself at /docs — no internet access required.

License

MIT — see LICENSE.

S
Description
LDAP GUI and API manager for use with SSO.
Readme MIT 58 MiB
Languages
JavaScript 63.6%
EJS 31%
Shell 5.2%
CSS 0.2%