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.
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.
| 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) |
app_* env layer requires conf >= 1.1.0The 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
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.
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:
base.js into your gitignored
conf/secrets.js, or set them as app_* env vars.secrets.js.app_* env varsapp_* vars seem to do nothingYou're on @simpleworkjs/conf 1.0.0. Bump to 1.1.0+ (above).
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.