From 4f61eeb1a712684d5cedf8ba8216b8470935989f Mon Sep 17 00:00:00 2001 From: William Mantly Date: Thu, 23 Jul 2026 02:44:19 -0400 Subject: [PATCH] feat: also seed OpenLDAP and OpenResty services in the directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack runs two more real services than the last seed captured: - OpenLDAP: independently consumed via direct LDAPS binds (the SSO's /integrations page advertises it). Seeded with the ldaps:// endpoint, honoring ldap.ldapsHost when the operator set one. - OpenResty: the proxy container's data plane (80/443) that every hostname in the stack actually flows through — distinct from the 'proxy' entry, which is the node management UI. Seeded with a wildcard https://*. address (same wildcard convention the proxy's Host records use). Both use metadata.subType so the directory UI badges them as service (openldap) / service (openresty). Same idempotency: existing slugs are operator-owned and untouched. Re-verified against a live app: pass 1 creates all six resources + oauth edge, pass 2 changes nothing. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- bootstrap/bootstrap.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a66be11..c1b7859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ for what changed inside the apps it composes. ## [Unreleased] ### Added -- The bootstrap now seeds the SSO directory with the stack's own resources: a site (from the configured domain), a "Stack host", and the SSO Manager + Proxy services (with their public URLs in metadata), linking the proxy's auto-registered OAuth client under its service. The Directory page is populated out of the box instead of starting empty. Idempotent — resources whose slug already exists are operator-owned and never touched, and a seed failure only warns (never fails a bring-up, e.g. against an older sso-manager image without `/api/directory`). +- The bootstrap now seeds the SSO directory with the stack's own resources: a site (from the configured domain), a "Stack host", and the SSO Manager + Proxy services (with their public URLs in metadata), linking the proxy's auto-registered OAuth client under its service. Also seeds the two non-obvious services the stack runs: the OpenLDAP directory (advertising the `ldaps://` endpoint legacy apps bind to, honoring `ldap.ldapsHost`) and the OpenResty edge (the 80/443 data plane every hostname flows through, with a wildcard `https://*.` address). The Directory page is populated out of the box instead of starting empty. Idempotent — resources whose slug already exists are operator-owned and never touched, and a seed failure only warns (never fails a bring-up, e.g. against an older sso-manager image without `/api/directory`). ## [1.3.3] - 2026-07-23 diff --git a/bootstrap/bootstrap.js b/bootstrap/bootstrap.js index 290ae03..36ad5cb 100644 --- a/bootstrap/bootstrap.js +++ b/bootstrap/bootstrap.js @@ -324,7 +324,23 @@ async function seedDirectory(token, clientId) { const site = await ensure('site', ORG, slugify(DOMAIN || ORG), null, {}); const host = await ensure('host', 'Stack host', 'stack-host', site.id, {}); await ensure('service', 'SSO Manager', 'sso-manager', host.id, { address: `https://${SSO_HOST}` }); + // Proxy = the node management UI; OpenResty = the data plane every hostname + // in the stack actually flows through (80/443). Two faces, two entries. const psvc = await ensure('service', 'Proxy', 'proxy', host.id, { address: `https://${PROXY_HOST}` }); + // OpenLDAP is independently consumed (direct LDAPS binds for legacy apps — + // see the SSO's /integrations page), so it gets its own entry. Advertise + // the operator-configured LDAPS hostname when set, else the SSO host. + const LDAPS_HOST = (sso.ldap && sso.ldap.ldapsHost) || SSO_HOST; + await ensure('service', 'OpenLDAP Directory', 'openldap', host.id, { + address: `ldaps://${LDAPS_HOST}:636`, + subType: 'openldap', + }); + // Wildcard address: OpenResty fronts every host under the domain (same + // */** wildcard convention the proxy's Host records use). + await ensure('service', 'OpenResty Edge', 'openresty', host.id, { + address: DOMAIN ? `https://*.${DOMAIN}` : `https://${PROXY_HOST}`, + subType: 'openresty', + }); // Link the proxy's OAuth client (Resource-backed since sso-manager 1.3.0) // under its service, if it appears in the directory and isn't linked yet.