Mail sending fell back to a hardcoded noreply@theta42.com From address when smtp.from wasn't set, which authenticated relays reject with "Sender is not same as SMTP authenticate username" since no relay authorized this account to send as that address. Falls back to smtp.user first now. Also: catalog card titles now read name-then-icon instead of icon-then-name, and a handful of docs corrections found in an accuracy pass (configuration.md missing the OpenBao/live-config layer, plugins.md undercounting plugin types, vault.md describing OpenBao dev-mode/root-token access that doesn't reflect the real production setup, orphaned discovery.md/vault.md pages linked in, README's required-groups list missing app_sso_directory_admin). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0113gCdnfSCuZr6xvPDxTo3D
4.9 KiB
layout, title, description
| layout | title | description |
|---|---|---|
| default | Configuration | SSO Manager's config layers — conf/base.js defaults, secrets.js overrides, and app_* environment variables. |
Configuration
The app loads configuration via
@simpleworkjs/conf, which
deep-merges, in order (later wins):
conf/base.js— committed, generic defaults (dc=example,dc=com,localhost,SSO Manager).conf/<NODE_ENV>.js— optional, environment-specific.conf/secrets.js— gitignored; secrets + per-deployment values.app_*environment variables — the highest-precedence layer among these four.
Any env var whose name starts with app_ overrides the merged config. The rest
of the name splits on double-underscore (__) into a nested path. Values are
JSON.parse-coerced when possible (numbers, booleans, null, JSON) and kept as
raw strings otherwise.
A fifth, higher-precedence layer: OpenBao + the Configuration UI
In a theta-suite deployment, @simpleworkjs/bao-conf's init() deep-merges
secret/sso-manager/conf (from OpenBao) over the four layers above at boot —
this is the layer setup.sh/theta-suite actually manages, and it wins over
everything else here. On top of that, the admin Configuration page in the
UI writes straight to secret/sso-manager/conf (via routes/api_conf.js)
and applies the change to the live conf object immediately
(applyToLiveConf) — no restart, and it bypasses conf/secrets.js entirely.
If a value isn't behaving the way conf/secrets.js says it should, check the
Configuration UI / OpenBao before assuming a file edit didn't take — it's
almost certainly OpenBao (or a live UI edit) winning the merge.
Examples
| Env var | Sets | Type |
|---|---|---|
app_ldap__url=ldap://host:389 |
conf.ldap.url |
string |
app_ldap__bindPassword=secret |
conf.ldap.bindPassword |
string |
app_ldap__userBase=ou=people,dc=… |
conf.ldap.userBase |
string |
app_ldap__uidGidMin=1500 |
conf.ldap.uidGidMin |
number (new-user id floor) |
app_ldap__uidGidReservedFloor=9000 |
conf.ldap.uidGidReservedFloor |
number (ids at/above this are ignored when allocating) |
app_ldap__ldapsHost=ldap.internal.example.com |
conf.ldap.ldapsHost |
string (hostname shown on /integrations for LDAPS binds; empty = derive from oauth.issuer) |
app_ldap__ldapsPort=636 |
conf.ldap.ldapsPort |
number (port shown on /integrations) |
app_oauth__jwtSecret=... |
conf.oauth.jwtSecret |
string |
app_oauth__issuer=https://sso.example.com |
conf.oauth.issuer |
string |
app_oauth__token_lifetime__access_token=3600 |
conf.oauth.token_lifetime.access_token |
number |
app_smtp__secure=false |
conf.smtp.secure |
boolean |
app_smtp__host=smtp.example.com |
conf.smtp.host |
string |
app_name=My SSO |
conf.name |
string |
app_redis__host=redis.local |
conf.redis.host |
string (external Redis) |
The app_* env layer requires conf >= 1.1.0
The app_* environment-variable override layer was added in
@simpleworkjs/conf 1.1.0. On 1.0.0 the app ignores all app_* vars and only
reads base.js / <NODE_ENV>.js / secrets.js. The Docker image will not honor
app_* env on 1.0.0. Refresh the lock from the nodejs/ directory:
cd nodejs && npm install @simpleworkjs/conf@^1.1.0
Inspecting the merged config
From the nodejs/ directory:
node -e "console.log(require('@simpleworkjs/conf').ldap)"
node -e "console.log(require('@simpleworkjs/conf').oauth)"
node -e "console.log(require('@simpleworkjs/conf'))" # everything
Or, inside the running container:
docker compose exec sso-manager node -e "console.log(require('@simpleworkjs/conf').ldap)"
app_* env vars override secrets.js, which overrides base.js — if a value
isn't what you expect, check those layers in that order.
Migrating an existing instance to the generic defaults
The committed nodejs/conf/base.js ships generic defaults
(dc=example,dc=com, localhost, SSO Manager). Previously it carried
Theta42-specific values (LDAP bind DN/bases, SMTP host/user/sender, OAuth
issuer). If you run an existing instance off this repo:
- Move per-deployment, non-secret values (bind DN, user/group bases, SMTP
host/user/sender, OAuth issuer, org name) from
base.jsinto your gitignoredconf/secrets.js, or set them asapp_*env vars. - Secret values (LDAP bind password, SMTP password, JWT secret) already belong
in
secrets.js.
Troubleshooting app_* env vars
app_* vars seem to do nothing
You're on @simpleworkjs/conf 1.0.0. Bump to 1.1.0+ (above).
LDAP operations 401 / "Invalid Credentials"
Check the merged LDAP config the app actually sees:
cd nodejs && node -e "console.log(require('@simpleworkjs/conf').ldap)"
Confirm url / bindDN / bindPassword / userBase match your directory.