From 5ed83a2f594359c661607621d9416dafde003821 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Mon, 10 Aug 2026 22:19:21 -0400 Subject: [PATCH] feat(multi-site): auto-derive site slug; wire proxy/jump service integrations Two real gaps found while fixing the Directory's Multi-Site modal: 1. SITE_SLUG was never set anywhere -- site_config.js's own fallback ("site-default") was all a fresh master could ever show, since nothing in setup.sh/docker-compose.yml passed it a value and bootstrap.js never generated one. Derived from CFG_SITE_NAME (same source jump-host's default exit node name already uses) with the same slugify rule bootstrap.js's own site Resource slug uses, formatted to match site_config.js's own "site-default" convention. Only a first-run default -- a real join/promote's persisted site.json value always wins. 2. PROXY_INTERNAL_URL and JUMP_INTERNAL_URL -- the env vars utils/proxy_client.js (no-inbound relay automation) and the new utils/jump_client.js (real gateway-mesh count on the modal) read to find each service -- were never actually set anywhere in docker-compose.yml. Both features existed in sso-manager-node's code but were completely unreachable in every real deployment, always hitting their "not configured" fallback. Wired both to the docker network hostnames. Also documents how to mint + store the two integration API tokens those features need (self-service tokens each app already has, not a new credential type -- same reasoning as the relay automation). --- docker-compose.yml | 11 +++++++++++ setup.env.example | 14 ++++++++++++++ setup.sh | 10 ++++++++++ 3 files changed, 35 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 7e699bd..a3f1500 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -71,8 +71,19 @@ services: # setup.sh (policy sso-broker) — NOT the root token. - NODE_ENV=production - NODE_PORT=3001 + # Only a first-run default (site_config.js's envDefaults()) -- a real + # join/promote persists its own value to /config/site.json afterward, + # which always wins. Derived by setup.sh from CFG_SITE_NAME. + - SITE_SLUG=${SITE_SLUG:-} - LDAP_SERVER_ID=${LDAP_SERVER_ID:-} - LDAP_REPLICATION_HOSTS=${LDAP_REPLICATION_HOSTS:-} + # utils/proxy_client.js (no-inbound relay automation) and + # utils/jump_client.js (real mesh-gateway count on the Multi-Site + # modal) both no-op/skip without these -- neither was ever actually + # wired into the compose environment before, so both features were + # unreachable in every real deployment despite existing in code. + - PROXY_INTERNAL_URL=http://proxy:3000 + - JUMP_INTERNAL_URL=http://jump-host:3002 - VAULT_ADDR=http://openbao:8200 - VAULT_TOKEN=${SSO_VAULT_TOKEN:-} # Optional upstream HTTP(S) proxy for outbound calls (SMTP, etc.) at diff --git a/setup.env.example b/setup.env.example index adfb31c..a9bf17b 100644 --- a/setup.env.example +++ b/setup.env.example @@ -78,6 +78,20 @@ CFG_DOMAIN=example.com #CFG_SPOKE_NO_INBOUND=true #CFG_SPOKE_PUBLIC_HOST=sso-branch2.master-domain.example.com +# Two service-to-service integrations the Directory uses (both reuse each +# app's existing self-service API token system -- see MULTI_SITE_SPEC.md's +# "service-to-service auth" note -- not a new credential type each): +# - No-inbound relay automation (above) needs a theta-proxy API token so +# sso-manager can create/update the relay Host route on its own. +# - The Multi-Site modal's real gateway-mesh count needs a jump-host API +# token (minted by a jump-admin user) to read GET /api/mesh/gateways. +# Neither is required for the rest of the stack to work -- both features +# just report "not configured" until you mint a token in each app's own web +# UI (Settings -> API Tokens) and store it in OpenBao, from inside the +# sso-manager container (VAULT_ADDR/VAULT_TOKEN are already set there): +# docker compose exec sso-manager node -e "require('@simpleworkjs/bao-conf').set('integrations/theta-proxy', {token: 'prx_...'})" +# docker compose exec sso-manager node -e "require('@simpleworkjs/bao-conf').set('integrations/theta-jump', {token: 'jmp_...'})" + # ── Optional outbound HTTP(S) proxy ────────────────────────────────────────── # For an isolated/offline/corporate-network test host that only reaches the # internet through an upstream HTTP proxy — NOT the theta42 "proxy" app. diff --git a/setup.sh b/setup.sh index de4690b..0a94efc 100755 --- a/setup.sh +++ b/setup.sh @@ -543,6 +543,16 @@ BAOEOF CFG_SSO_HOST="${CFG_SSO_HOST:-sso.$CFG_DOMAIN}" CFG_PROXY_HOST="${CFG_PROXY_HOST:-proxy.$CFG_DOMAIN}" CFG_SITE_NAME="${CFG_SITE_NAME:-local}" + # Multi-site identity (site_config.js's `siteSlug`, shown on the Directory's + # Multi-Site modal) -- without this it's never set anywhere and every fresh + # master shows the module's own literal fallback, "site-default", forever. + # Derived from CFG_SITE_NAME with the same slugify rule bootstrap.js uses + # for the site Resource's own slug (site_$(slugify), underscore prefix -- + # this is hyphenated to match site_config.js's own "site-default" format). + # site.json overrides this after first bring-up (join/promote write real + # values there), so this only ever matters for a fresh install. + SITE_SLUG="site-$(echo "$CFG_SITE_NAME" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g')" + export SITE_SLUG CFG_ORG="${CFG_ORG:-Theta Directory}" CFG_ADMIN_UID="${CFG_ADMIN_UID:-admin}" CFG_ADMIN_EMAIL="${CFG_ADMIN_EMAIL:-admin@$CFG_PROXY_HOST}"