Compare commits

..

16 Commits

Author SHA1 Message Date
wmantly 30146e9588 Merge pull request #68 from theta42/bump-both-1.1.14
Bump proxy and sso-manager-node submodule pins to v1.1.14
2026-07-17 23:49:46 -04:00
wmantly 481602ae60 Bump proxy and sso-manager-node submodule pins to v1.1.14
- proxy -> v1.1.14
- sso-manager-node -> v1.1.14

Both bump @simpleworkjs/conf to 1.2.0 and jq-repeat to 2.2.0, and use
the new CONF_SECRETS env var instead of symlinking the mounted secrets
file into /app/conf/secrets.js. Updated theta-env's own docs/setup.sh/
docker-compose.yml comments to match -- no change to the config file
format or bind mounts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 23:49:21 -04:00
wmantly 26b853e1d6 Merge pull request #67 from theta42/bump-both-1.1.13
Bump proxy and sso-manager-node submodule pins to v1.1.13
2026-07-17 22:33:31 -04:00
wmantly 04a9c557e8 Bump proxy and sso-manager-node submodule pins to v1.1.13; update CHANGELOG
- proxy -> v1.1.13
- sso-manager-node -> v1.1.13

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 22:33:05 -04:00
wmantly d1136a98a6 Merge pull request #66 from theta42/bump-both-1.1.11
Bump proxy and sso-manager-node submodule pins to v1.1.11
2026-07-17 20:03:42 -04:00
wmantly 33f603dcc0 Bump proxy and sso-manager-node submodule pins to v1.1.11; update CHANGELOG
- proxy -> v1.1.11
- sso-manager-node -> v1.1.11

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 20:03:01 -04:00
wmantly f3a5194104 Merge pull request #65 from theta42/bump-both-1.1.10
Bump proxy and sso-manager-node submodule pins to v1.1.10
2026-07-17 19:31:07 -04:00
wmantly d21dfefc37 Bump proxy and sso-manager-node submodule pins to v1.1.10; update CHANGELOG
- proxy -> v1.1.10
- sso-manager-node -> v1.1.10

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 19:30:29 -04:00
wmantly 0861b6a075 Merge pull request #64 from theta42/bump-proxy-1.1.9
Bump proxy submodule pin to v1.1.9
2026-07-17 19:06:42 -04:00
wmantly fee1baaaf9 Bump proxy submodule pin to v1.1.9; update CHANGELOG
- proxy -> v1.1.9

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 19:06:07 -04:00
wmantly 5f5df7d2bf Merge pull request #63 from theta42/bump-proxy-1.1.8
Bump proxy submodule pin to v1.1.8
2026-07-17 18:48:56 -04:00
wmantly 03838e46d9 Bump proxy submodule pin to v1.1.8; update CHANGELOG
- proxy -> v1.1.8

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 18:48:23 -04:00
wmantly a8373fa413 Merge pull request #62 from theta42/bump-sso-1.1.9
Bump sso-manager-node submodule pin to v1.1.9
2026-07-17 11:33:00 -04:00
wmantly 40e4c9f9ef Bump sso-manager-node submodule pin to v1.1.9; update CHANGELOG
- sso-manager-node -> v1.1.9

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 11:32:05 -04:00
wmantly 3bece9beeb Merge pull request #61 from theta42/bump-sso-1.1.8
Bump sso-manager-node submodule pin to v1.1.8
2026-07-17 11:05:10 -04:00
wmantly f5312fa528 Bump sso-manager-node submodule pin to v1.1.8; update CHANGELOG
- sso-manager-node -> v1.1.8

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
2026-07-17 11:04:11 -04:00
8 changed files with 152 additions and 34 deletions
+116 -1
View File
@@ -10,6 +10,113 @@ for what changed inside the apps it composes.
## [Unreleased]
## [1.1.16] - 2026-07-18
### Bumped
- proxy -> [v1.1.14](https://github.com/theta42/proxy/releases/tag/v1.1.14)
- sso-manager-node -> [v1.1.14](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.14)
Both: bumped `@simpleworkjs/conf` to 1.2.0 and `jq-repeat` to 2.2.0.
### Changed
- `./config/sso-secrets.js` and `./config/proxy-secrets.js` are now loaded via each app's `CONF_SECRETS` env var (set by the entrypoint) instead of being symlinked into `/app/conf/secrets.js` — neither container needs write access to its own `conf/` directory anymore. No change to the config file format or bind mounts; existing `./config/` directories keep working as-is.
## [1.1.15] - 2026-07-17
### Bumped
- proxy -> [v1.1.13](https://github.com/theta42/proxy/releases/tag/v1.1.13)
- sso-manager-node -> [v1.1.13](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.13)
proxy:
### Fixed
- The host edit form's "Parent Wildcard" option stayed greyed out even when a valid wildcard actually existed for that host, so an already-created host could never be switched onto one from the edit modal (only brand-new hosts, via the field's `keyup` handler, ever saw it become available). The underlying `/host/lookup/:item` check also had the same self-match issue as the recently-fixed backend bug: it resolved an already-existing host to its own record instead of a sibling wildcard. Added a dedicated `/host/wildcard-parent/:item` endpoint that checks both directions, and the edit form now actually runs the check when it opens.
- Fixed an nginx startup warning: `the "listen ... http2" directive is deprecated, use the "http2" directive instead`. Migrated to the standalone `http2 on;` directive (nginx 1.25.1+).
### Added
- Four new plain-language docs aimed at less technical readers, replacing the system-design-level Architecture/Installation docs as the target of most card help links: **Hosts & HTTPS**, **DNS Providers**, **Users, Groups & Permissions**, and **API Tokens**. Each links onward to the deeper technical reference for readers who want it; the technical docs link back the other way too. The personal-access-token card (previously missed entirely) now has a help link.
### Fixed
- The in-app docs viewer rendered every `docs/*.md` page with a garbled heading and a stray horizontal rule at the top — Jekyll front matter (meant only for the GitHub Pages build) was never stripped before being handed to the markdown renderer. Also fixed: cross-doc links never resolved in-app, since this viewer serves docs at `/docs/<slug>` with no `.html` suffix — they're now rewritten to the correct in-app URL (by registered slug, falling back to the doc's real filename), the same way image paths already were.
sso-manager-node:
### Added
- Three new plain-language docs aimed at less technical readers, replacing the schema-level LDAP/OAuth/API docs as the target of most card help links: **Accounts, Groups & Managers**, **Connecting Apps (SSO)**, and **API Tokens**. Each links onward to the deeper technical reference for readers who want it; the technical docs link back the other way too. The personal-access-token card (previously missed) now links to its own doc.
### Fixed
- The in-app docs viewer rendered every `docs/*.md` page with a garbled heading and a stray horizontal rule at the top — Jekyll front matter (meant only for the GitHub Pages build) was never stripped before being handed to the markdown renderer. Also fixed: cross-doc links (`ldap.html`, `index.html`, etc.) never resolved in-app, since this viewer serves docs at `/docs/<slug>` with no `.html` suffix — they're now rewritten to the correct in-app URL, the same way image paths already were.
- The new concept docs' cross-links (`concepts-accounts.html` etc.) are the correct, working URL on the Jekyll/GitHub Pages build (where the page's URL is its filename stem) but didn't resolve in the in-app docs viewer, which serves docs at a separate short slug (`/docs/accounts`). The in-app renderer now also resolves a doc's real filename as a fallback, so one link written in a doc works on both targets.
## [1.1.14] - 2026-07-17
### Bumped
- proxy -> [v1.1.11](https://github.com/theta42/proxy/releases/tag/v1.1.11)
- sso-manager-node -> [v1.1.11](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.11)
Both: moved the help (❓) link out of the global header and onto each relevant card individually, so it deep-links straight to the doc that actually covers that card instead of one generic per-page guess.
## [1.1.13] - 2026-07-17
### Bumped
- proxy -> [v1.1.10](https://github.com/theta42/proxy/releases/tag/v1.1.10)
- sso-manager-node -> [v1.1.10](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.10)
Both: added a help icon (❓) in the top-right header that deep-links to the doc most relevant to the current page, and made the in-app docs viewer (`/docs`) searchable (a simple line-substring search over the local doc set, no new dependency, still works with no internet access).
## [1.1.12] - 2026-07-17
### Bumped
- proxy -> [v1.1.9](https://github.com/theta42/proxy/releases/tag/v1.1.9)
proxy:
### Added
- The host list now shows who created each host, and when.
- Plain (non-wildcard) hosts can now be renamed after creation — the hostname field is no longer permanently locked. Wildcard hosts, wildcard children, and auto-created subdomain cache entries stay locked, since other records reference them by name.
- More inline help text on the host create/edit form (Target SSL, wildcard matching behavior).
### Fixed
- The host create/edit modal's tabs could overflow awkwardly on narrow (mobile) screens — they now scroll horizontally instead.
- Fixed a bug in the vendored `model-redis` library's record-rename path: renaming a record's primary key while another `always`-type field (e.g. `updated_on`) is defined earlier in the schema left a stray, incomplete hash behind under the old key, making that name permanently unavailable for reuse. Worked around in `Host.prototype.update()`.
## [1.1.11] - 2026-07-17
### Bumped
- proxy -> [v1.1.8](https://github.com/theta42/proxy/releases/tag/v1.1.8)
proxy:
### Fixed
- **Couldn't attach an existing host to a parent wildcard.** The host edit form's "Parent Wildcard" option submitted correctly, but `Host.prototype.update()` had no `challengeType` handling at all (only `Host.create()` did) — selecting it and saving silently did nothing. Added the same wildcard-parent lookup to `update()`.
- **Couldn't register a wildcard's own base domain as a host.** A wildcard cert's `altNames` already cover both the base domain and `*.base domain`, but the lookup tree stores the wildcard one level below its base domain, and a lookup for the bare base domain landed on that empty parent node and found nothing — even though the already-issued cert covers it. `buildLookUpObj()` now also stamps the parent node so this resolves correctly, without re-issuing or duplicating the cert.
## [1.1.10] - 2026-07-17
### Bumped
- sso-manager-node -> [v1.1.9](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.9)
sso-manager-node:
### Added
- Every account's personal Unix group (its primary GID holder) can now have supplementary members managed from the account's profile page ("Members of `<uid>`'s group", admin-only) — e.g. to share write access to files owned by that group. Uses the standard `memberUid` attribute (RFC 2307 `posixGroup`).
## [1.1.9] - 2026-07-17
### Bumped
- sso-manager-node -> [v1.1.8](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.8)
sso-manager-node:
### Added
- Group membership is now editable directly from a user's profile page ("My groups" -- add via a group-name picker, remove with a button per row), instead of only from each group's own card on the Groups page. Admin-only, using the existing per-group member add/remove endpoints.
### Fixed
- The Edit Profile form's Mobile Phone field had a stray `validate=":9"` making it effectively required (submission was blocked with "Please fix the form errors" if left blank) -- it was always meant to be optional, matching the "Add user" form. Removed.
- A service account's profile always showed `Name: Service Account` -- every service account has the same literal filler given/last name (a schema-satisfying placeholder, not meant to be shown), making them indistinguishable by name. The Name line is now hidden for service accounts.
- The Users page's Service Accounts tab, and a freshly-created service account's own profile, could appear empty/not-a-service-account for up to 5 minutes right after creation. Creating a user caches it via `User.get()` *before* the route handler marks it as a service account (group membership), so the cached copy had `isServiceAccount` stuck wrong until the cache TTL expired. Now cleared and re-fetched immediately after marking.
- A user belonging to exactly one LDAP group had their `memberOf` attribute returned as a bare string instead of a one-element array (ldapts's normal behavior for single-valued attributes) -- client-side permission checks (`for(let group of user.memberOf)`) would then iterate the DN character-by-character instead of once, causing pages gated on that group (e.g. Groups) to incorrectly show "You do not have permission to be here." Normalized `memberOf` to always be an array, same fix already applied to `manager`.
## [1.1.8] - 2026-07-17
### Bumped
@@ -99,7 +206,15 @@ First tagged release. Establishes the `vX.Y.Z` tag convention going forward.
- proxy -> [v1.1.0](https://github.com/theta42/proxy/releases/tag/v1.1.0)
- sso-manager-node -> [v1.1.0](https://github.com/theta42/sso-manager-node/releases/tag/v1.1.0)
[Unreleased]: https://github.com/theta42/theta-env/compare/v1.1.8...HEAD
[Unreleased]: https://github.com/theta42/theta-env/compare/v1.1.16...HEAD
[1.1.16]: https://github.com/theta42/theta-env/compare/v1.1.15...v1.1.16
[1.1.15]: https://github.com/theta42/theta-env/compare/v1.1.14...v1.1.15
[1.1.14]: https://github.com/theta42/theta-env/compare/v1.1.13...v1.1.14
[1.1.13]: https://github.com/theta42/theta-env/compare/v1.1.12...v1.1.13
[1.1.12]: https://github.com/theta42/theta-env/compare/v1.1.11...v1.1.12
[1.1.11]: https://github.com/theta42/theta-env/compare/v1.1.10...v1.1.11
[1.1.10]: https://github.com/theta42/theta-env/compare/v1.1.9...v1.1.10
[1.1.9]: https://github.com/theta42/theta-env/compare/v1.1.8...v1.1.9
[1.1.8]: https://github.com/theta42/theta-env/compare/v1.1.7...v1.1.8
[1.1.7]: https://github.com/theta42/theta-env/compare/v1.1.6...v1.1.7
[1.1.6]: https://github.com/theta42/theta-env/compare/v1.1.5...v1.1.6
+2 -1
View File
@@ -172,7 +172,8 @@ operator-owned and `setup.env` is ignored.
### Configuration — `./config/` (no `.env` files)
All config and secrets live in a bind-mounted `./config/` directory (gitignored),
read by each app's `@simpleworkjs/conf` from a symlinked `secrets.js`:
read by each app's `@simpleworkjs/conf` via the `CONF_SECRETS` env var, which
the entrypoint points at the mounted file:
- **`./config/sso-secrets.js`** — SSO config: `ldap` (base, admin password,
user/group bases), `oauth` (issuer, `jwtSecret`), `smtp`, `name`, plus
+8 -7
View File
@@ -13,11 +13,12 @@
# Config + secrets live in bind-mounted ./config/ (gitignored):
# ./config/sso-secrets.js — SSO app + orchestrator config
# ./config/proxy-secrets.js — proxy OIDC/LDAP/auth config
# Each app's entrypoint symlinks its file into /app/conf/secrets.js so
# @simpleworkjs/conf reads it. No app_* env is passed (app_* env would override
# secrets.js). The sso-manager mounts ./config read-write so the bootstrap can
# write the generated OAuth client creds back into proxy-secrets.js; the proxy
# mounts it read-only.
# Each app's entrypoint points CONF_SECRETS at its file so @simpleworkjs/conf
# (>= 1.2.0) reads it directly -- no app_* env is passed (app_* env would
# override secrets.js), and no write access to /app/conf is needed. The
# sso-manager mounts ./config read-write so the bootstrap can write the
# generated OAuth client creds back into proxy-secrets.js; the proxy mounts
# it read-only.
#
# Compose only interpolates the port defaults below — there is no .env file.
# First-run wiring (LDAP service account, first admin, OAuth client) is
@@ -56,7 +57,7 @@ services:
volumes:
# Operator-edited SSO secrets (sso-secrets.js). Read-WRITE so the bootstrap
# can write the generated OAuth client creds into proxy-secrets.js. The
# entrypoint symlinks /config/sso-secrets.js -> /app/conf/secrets.js.
# entrypoint points CONF_SECRETS at /config/sso-secrets.js.
- ./config:/config
# Persist the LDAP database across container recreation.
- ldap-data:/var/lib/ldap
@@ -108,7 +109,7 @@ services:
volumes:
# Operator-edited proxy secrets (proxy-secrets.js). READ-ONLY — the proxy
# only reads it; the sso-manager bootstrap writes the OAuth creds. The
# entrypoint symlinks /config/proxy-secrets.js -> /app/conf/secrets.js.
# entrypoint points CONF_SECRETS at /config/proxy-secrets.js.
- ./config:/config:ro
# Persist Redis (AOF + RDB) so Host records, permissions, DNS creds, local
# users, AND the auto-ssl Let's Encrypt certs survive container recreation.
+13 -12
View File
@@ -102,9 +102,9 @@ inputs from the bind-mounted `./config/sso-secrets.js` + `./config/proxy-secrets
read-only). If `proxy-secrets.js` already holds a `clientId`+`clientSecret`
matching an existing client, they are kept; if the client exists but the file
has no usable secret, the secret is rotated and written back.
6. **Build + start the proxy**, wait for `/health`. The proxy entrypoint symlinks
`./config/proxy-secrets.js` to `/app/conf/secrets.js`, so `@simpleworkjs/conf`
(≥1.1.0) reads the OAuth creds + LDAP bind creds from the file.
6. **Build + start the proxy**, wait for `/health`. The proxy entrypoint points
`CONF_SECRETS` at `./config/proxy-secrets.js`, so `@simpleworkjs/conf`
(≥1.2.0) reads the OAuth creds + LDAP bind creds from the file.
7. **Register `<SSO_HOST>` and `<PROXY_HOST>` as Host records in the proxy**
`setup.sh` runs a short script inside the proxy container that calls its
Host model directly (`Host.create({host, ip, targetPort, ...})`), rather
@@ -123,19 +123,20 @@ inputs from the bind-mounted `./config/sso-secrets.js` + `./config/proxy-secrets
### How config reaches the apps (no `.env`)
All config and secrets live in `./config/` (gitignored, bind-mounted). Each
entrypoint symlinks its file to `/app/conf/secrets.js` early, before the app
starts:
entrypoint points the `CONF_SECRETS` env var (`@simpleworkjs/conf` >= 1.2.0)
at its file early, before the app starts:
```
./config/sso-secrets.js -> sso-manager:/app/conf/secrets.js (./config RW)
./config/proxy-secrets.js -> proxy:/app/conf/secrets.js (./config RO)
CONF_SECRETS=/config/sso-secrets.js (sso-manager, ./config RW)
CONF_SECRETS=/config/proxy-secrets.js (proxy, ./config RO)
```
`@simpleworkjs/conf` loads `conf/base.js → <env>.js → conf/secrets.js → app_*
env`, where **env beats `secrets.js`**. So compose passes **no `app_*` env vars**
(only `NODE_ENV`, `NODE_PORT`) — that makes `secrets.js` authoritative. The SSO
entrypoint reads the few values it needs at startup (LDAP base DN, admin
password, JWT secret, cert CN) from `secrets.js` via an in-container `node` call.
`@simpleworkjs/conf` loads `conf/base.js → <env>.js → secrets file → app_*
env`, where **env beats the secrets file**. So compose passes **no `app_*` env
vars** (only `NODE_ENV`, `NODE_PORT`) — that makes the secrets file
authoritative. The SSO entrypoint reads the few values it needs at startup
(LDAP base DN, admin password, JWT secret, cert CN) from `sso-secrets.js` via
an in-container `node` call.
### Why not `require` the SSO's internal models?
+8 -8
View File
@@ -25,18 +25,18 @@ mkdir -p config && cp secrets.js.example config/sso-secrets.js # edit it
docker compose up -d --build
```
The entrypoint symlinks `config/sso-secrets.js` to `nodejs/conf/secrets.js` so
The entrypoint points the `CONF_SECRETS` env var at `config/sso-secrets.js` so
`@simpleworkjs/conf` reads it. Set `ldap.bindPassword`, `oauth.jwtSecret`, and
the `stack`/`bootstrap` keys (the app ignores the ones it doesn't use). Pass
**no `app_*` env** — env beats `secrets.js`, so `app_*` would silently override
your file.
**no `app_*` env** — env beats the secrets file, so `app_*` would silently
override your file.
- Web UI: `http://localhost:3001`
- Health: `http://localhost:3001/health`
- OIDC discovery: `http://localhost:3001/.well-known/openid-configuration`
- LDAPS: `ldaps://<host>:636`
Requires `@simpleworkjs/conf` >= 1.1.0. Full reference:
Requires `@simpleworkjs/conf` >= 1.2.0. Full reference:
[SSO Manager deployment docs](https://theta42.github.io/sso-manager-node/deployment.html).
### Bare metal
@@ -62,11 +62,11 @@ mkdir -p config && cp secrets.js.example config/proxy-secrets.js # edit it
docker compose up -d --build
```
The entrypoint symlinks `config/proxy-secrets.js` to `nodejs/conf/secrets.js` so
`@simpleworkjs/conf` reads it. Fill in `oidc` (your SSO's endpoints +
The entrypoint points the `CONF_SECRETS` env var at `config/proxy-secrets.js`
so `@simpleworkjs/conf` reads it. Fill in `oidc` (your SSO's endpoints +
`clientId`/`clientSecret`/`redirectUri`), `ldap` (bind creds + search base), and
`auth` (admin groups/users). Pass **no `app_*` env** — env beats `secrets.js`,
so `app_*` would silently override your file.
`auth` (admin groups/users). Pass **no `app_*` env** — env beats the secrets
file, so `app_*` would silently override your file.
- Proxy (public, auto-SSL): `https://<host>/`
- Mgmt UI / API: `http://127.0.0.1:3000/`
+1 -1
Submodule proxy updated: 11f44176c0...f0eadbc2d7
+3 -3
View File
@@ -220,7 +220,7 @@ write_sso_secrets() {
cat > "$CONFIG_DIR/sso-secrets.js" <<SSOEOF
'use strict';
// Generated by setup.sh. Edit freely; re-run ./setup.sh to apply.
// The SSO app reads this via @simpleworkjs/conf (symlinked to conf/secrets.js).
// The SSO app reads this via @simpleworkjs/conf (CONF_SECRETS env var).
// The app ignores the extra stack/bootstrap/serviceAccountPass keys (read by
// the orchestrator). Back this file up off-host — it holds all SSO secrets.
@@ -271,8 +271,8 @@ write_proxy_secrets() {
local dn="$CFG_BASE_DN"
cat > "$CONFIG_DIR/proxy-secrets.js" <<PROXYEOF
'use strict';
// Generated by setup.sh. The proxy reads this via @simpleworkjs/conf (symlinked
// to conf/secrets.js). clientId/clientSecret are filled in by the bootstrap
// Generated by setup.sh. The proxy reads this via @simpleworkjs/conf (CONF_SECRETS
// env var). clientId/clientSecret are filled in by the bootstrap
// (run by ./setup.sh) — leave them as-is. ldap.bindPassword MUST equal
// serviceAccountPass in sso-secrets.js (the proxy binds as that account).