# ───────────────────────────────────────────────────────────────────────────── # setup.env — first-run setup for the theta-suite stack. # # This file is used ONLY on the FIRST run of ./setup.sh, to generate # ./config/sso-secrets.js + ./config/proxy-secrets.js with your domain filled # in everywhere consistently. Once ./config/*.js exist they are operator-owned # (edit them directly; setup.env is ignored on later runs). # # cp setup.env.example setup.env # $EDITOR setup.env # set CFG_DOMAIN below to your domain # ./setup.sh # generates ./config/ and builds the stack # # Copying this file to setup.env (gitignored) keeps your domain out of git. # ───────────────────────────────────────────────────────────────────────────── # Your domain. THIS IS THE ONE PLACE THE DOMAIN IS ENTERED. Everything else # derives from it: the SSO/proxy hostnames default to sso. / # proxy., and the LDAP base DN is built from it (example.com becomes # dc=example,dc=com; a 3-label domain like myhost.duckdns.org becomes # dc=myhost,dc=duckdns,dc=org — any number of labels works). Required — # setup.sh refuses to run without it. CFG_DOMAIN=example.com # Site name for the SSO directory — the root node this stack registers itself # under on the Directory page, and the default "Location (Site)" that Linux # hosts joined via ldap-client attach to (parent slug: site_). # Optional — defaults to "local". #CFG_SITE_NAME=local # Public hostnames. Optional — default to sso. / proxy. derived # from CFG_DOMAIN above. Uncomment and set only if your hostnames differ # (e.g. a different subdomain, or the domain isn't the bare apex): #CFG_SSO_HOST=sso.example.com #CFG_PROXY_HOST=proxy.example.com # ── SSH jump host (always installed) ───────────────────────────────────────── # The theta42/jump-host component is installed and started by default — a # public SSH jump host that authenticates users against the directory and # bridges them to downstream hosts (ssh uid_-_target@jump, or an interactive # picker). setup.sh clones/builds the jump-host submodule, the bootstrap mints # its directory API token + writes ./config/jump-secrets.js, and it's registered # in the proxy + directory. See jump-host's README for the LDAP write-ACL note # (the bundled deployment binds as cn=admin). #CFG_JUMP_HOST=jump.example.com # defaults to jump. #JUMP_SSH_PORT=2222 # host port mapped to the jump host's SSH (never 22 by default) # Advanced: override the derived LDAP base DN directly (e.g. to namespace # under an OU-style prefix). Leave unset to use the DN built from CFG_DOMAIN: #CFG_BASE_DN=dc=example,dc=com # ── Multi-Site: join an existing (master) directory ────────────────────────── # To run THIS deployment as a read-only SPOKE of an existing Theta Directory # instead of seeding a fresh one, set the master's URL and a site join key # (mint one on the master: Directory -> the Master Site modal -> Site Join Keys # -> Mint key). Honored ONLY on a first-run bring-up (before ./config/ exists), # so it can never merge an already-populated directory; re-runs ignore it. # The spoke adopts the master's users/groups/resources and persists its spoke # role in ./config/site.json (isMaster=false, masterUrl, siteSlug). It also # registers itself with the master (using this site's own CFG_SSO_HOST) so # future catalog changes on the master get pushed here live instead of this # being a one-time snapshot -- the master must be able to reach THIS site's # CFG_SSO_HOST for that part to work; if it can't (this site has no inbound # path), the join still succeeds, it just never receives live updates. #CFG_MASTER_DIRECTORY_URL=https://sso.master.example.com #CFG_MASTER_DIRECTORY_JOIN_KEY=stj_9f2e... # No public IP at all (CGNAT, etc.)? The master can still reach this spoke by # relaying over the gateway-to-gateway WireGuard mesh instead of the open # internet (MULTI_SITE_SPEC.md §5.2) -- but the mesh peering itself is a # manual, out-of-band step on BOTH jump-hosts (mint a mesh join token on the # master's jump-host, paste it into this site's jump-host "Join a mesh" UI # action) that can't run unattended inside this script. Once that's done, set # these two and re-run setup.sh: it discovers this jump-host's assigned mesh # IP (GET /api/mesh/self) and registers it with the master, which then # auto-creates the relay route on its own theta-proxy. Safe to leave set # before meshing -- setup.sh just reports "not meshed yet" and skips until a # later re-run finds the mesh IP. #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. # Wired into every service's docker build (npm/apt) AND its running container # (SMTP, ACME/Let's Encrypt, DNS provider calls, the jump-host directory API # client). Leave unset to disable (the default); CFG_HTTPS_PROXY falls back to # CFG_HTTP_PROXY if unset, and CFG_NO_PROXY defaults to covering the stack's # own internal service names so container-to-container traffic never goes # through the proxy. #CFG_HTTP_PROXY=http://proxy.example.com:3128 #CFG_HTTPS_PROXY=http://proxy.example.com:3128 #CFG_NO_PROXY=localhost,127.0.0.1,sso-manager,proxy,jump-host # Optional — sensible defaults if left blank: #CFG_ORG=SSO Manager # app display name + outbound email org #CFG_ADMIN_UID=admin # initial SSO admin username #CFG_ADMIN_EMAIL=admin@proxy.example.com # defaults to admin@ #CFG_LDAP_CERT_CN= # LDAP TLS cert CN; empty -> defaults to the domain # # Hostname advertised on the SSO /integrations page for direct LDAPS binds. # Leave blank to derive it from the public SSO host (same as oauth.issuer). # Recommended: set an internal-only name like 'ldap.internal.example.com' or # 'sso-manager' so clients don't need a public 636 port forward. See docs. #CFG_LDAPS_HOST= # ── DO NOT put secrets here ────────────────────────────────────────────────── # The LDAP admin password, JWT secret, admin password, LDAP service-account # password, and the proxy's local admin password are all GENERATED (random) # into ./config/sso-secrets.js + ./config/proxy-secrets.js on first run. # Change them later by editing those files directly (the proxy's local admin # password is the exception — see ./config/proxy-secrets.js's auth.localAdminPass # comment for how to actually change it after the account exists). Do NOT set # CFG_LDAP_ADMIN_PASS / CFG_JWT_SECRET / CFG_ADMIN_PASS / CFG_SVC_PASS / # CFG_PROXY_ADMIN_PASS here. # ── theta-agent Host Integration ───────────────────────────────────────────── # Configure theta-agent integration with the local host. All options default to # enabled (1). Set to 0 to disable. # # Enable theta-agent installation and configuration on this host. #CFG_THETA_AGENT_ENABLE=1 # # Configure LDAP authentication for this host via ldap-client (SSSD/PAM). #CFG_THETA_AGENT_LDAP_AUTH=1 # # Allow theta-agent full control of this host (arbitrary_bash, service_control, # reboot, configure_ldap capabilities). #CFG_THETA_AGENT_FULL_CONTROL=1 # ── Geo-Location Scaling (N-Way Multi-Master LDAP) ─────────────────────────── # If deploying this stack across multiple physical sites to provide local HA # for directory services, you can enable N-Way Multi-Master OpenLDAP replication. # This requires assigning a unique ID to each site and listing the LDAPS URLs # of all OTHER sites in the cluster. # # Each site MUST have a unique LDAP_SERVER_ID (e.g. 1, 2, 3). # LDAP_REPLICATION_HOSTS is a space-separated list of the other sites' LDAP URLs. # Example for Site 1: #LDAP_SERVER_ID=1 #LDAP_REPLICATION_HOSTS="ldaps://sso.site2.com:636 ldaps://sso.site3.com:636" # ── Proxy HTTP/HTTPS Defaults ──────────────────────────────────────────────── # If you are running the stack behind an external reverse proxy (like Cloudflare # or another ingress) that handles TLS termination, you may want the internal # proxy to serve everything over plain HTTP without forcing redirects to HTTPS. # Set this to 1 to create all default proxy host entries with forcessl=false. #CFG_CREATE_ALL_HTTP=1