The GitHub Pages site used the generic jekyll-theme-cayman theme --
purple gradient hero, no site nav, no per-page SEO. Replaced with a
custom layout that mirrors the actual app UI: dark fixed navbar with
the theta42 logo, Bootstrap 5 + Font Awesome (same stack the app
uses), content in a card, dark footer matching bottom.ejs
(copyright, MIT license, GitHub, Changelog links).
- New cross-page nav (Home/Deployment/Configuration/OAuth/LDAP/
Changelog) -- there was previously no way to get from one docs
page to another except a single "Back to Home" link per page.
- SEO: jekyll-seo-tag + jekyll-sitemap (both GitHub-Pages-supported
plugins, no custom build needed) -- real per-page meta description,
Open Graph/Twitter card tags, canonical URLs, JSON-LD, sitemap.xml,
and a robots.txt referencing it. Added a real description to every
page's front matter (none existed before).
- Mobile: Bootstrap's responsive grid + collapsible navbar; the
screenshot pairs in index.md (inline width="49%" for a two-up
desktop layout) now stack to full-width below 576px instead of
squeezing illegibly small.
Verified with a real Jekyll build (jekyll/jekyll Docker image, no
Ruby available locally) + Playwright: desktop and mobile (375px)
screenshots of the home and deployment pages, mobile nav toggle
open/close, active-link highlighting per page, zero console/page
errors, and confirmed real SEO output (meta description, OG/Twitter
tags, canonical, JSON-LD, sitemap.xml, robots.txt) via curl against
the served site.
ops/backup.sh snapshots LDAP (slapcat), Redis (BGSAVE, dynamic RDB path
lookup), and ./config for standalone deployments, with retention. A
background service polls GitHub releases every 24h and surfaces an
admin-only banner in the UI when a newer version is published.
The Integrations page's Service Accounts (bind-only, organizationalRole)
don't cover the other real use case: an account something actually runs
as on a Linux host -- a media manager, a torrent client, Emby -- with a
real uidNumber/gidNumber that owns files, and a group other accounts
join for write access (e.g. a `stuff_manager` group granting write
rights to a media library). That needs a real posixAccount, which the
bind-only model can't be.
- New well-known group `app_sso_service_account`, seeded the same way as
app_sso_admin/app_sso_invite/app_sso_oauth_admin (docker-entrypoint.sh,
ops/ldap-setup.sh). Not a permission gate -- a marker.
- "Add new user" form gets a "This is a service account" checkbox: swaps
the person-shaped fields (first/last name, birthday, ToS agreement)
for a single account-name field, since none of those make sense for a
non-person account. On create, the route adds the user to
app_sso_service_account.
- User.listDetail() annotates each user with isServiceAccount (checked
against the marker group's member list once per call, not the memberof
overlay's reverse attribute -- not reliably returned by every LDAP
server this app might point at, confirmed against a real external
directory during testing). Users page shows a "service" badge.
- Notification broadcasts (filter_type=all/all_active) exclude service
accounts by default -- nobody reads mail as `stuff_manager`.
- Fixed a real, previously-unrelated bug this surfaced: addPosixAccount
unconditionally set `mail: data.mail` in the LDAP entry even when
undefined, and ldapts/slapd reject an attribute given an explicit
undefined value ("no values for attribute type") rather than treating
it as absent. This meant creating ANY user without an email already
failed outright -- not something a service account (which commonly has
no real mailbox) could route around. Made mail conditional, matching
how mobile/sshPublicKey/dob already work.
- docs/ldap.md now explains both kinds of service account side by side
and when to use which.
Verified end-to-end against a real external LDAP server (not a local
sandbox): created a service account with no email, confirmed it's
correctly flagged and excluded from broadcast recipient resolution,
confirmed a normal user is unaffected, confirmed the code degrades
gracefully if the marker group doesn't exist yet (pre-upgrade
deployments).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a concrete "Connecting a 3rd-party app or container" section:
a bind-parameter reference table, a worked Gitea example, a generic
Docker LDAP_* env var pattern, and a pointer to theta42/ldap-client for
full host-level (SSH/sudo/PAM) integration as opposed to a single app.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lossless upgrades + config story for the all-in-one image.
Redis persistence (Part A):
- Replace in-memory `--save "" --appendonly no` with AOF + RDB persisted to /data
(--appendonly yes, periodic saves, --dbfilename dump.rdb). OAuth clients,
tokens, and other model-redis state now survive container recreation.
- Add the `sso-data` named volume -> /data in docker-compose.yml.
Config from ./config/sso-secrets.js (Part B):
- docker-entrypoint.sh: when /config/sso-secrets.js is mounted, symlink it to
/app/conf/secrets.js and read the server-side LDAP vars (base DN, admin pass,
org, domain, cert CN, JWT) from the file via one `node` call (base64-decoded,
no eval/quoting hazards). No app_* env is exported in this mode, so the file
is authoritative (@simpleworkjs/conf precedence: base < env < secrets.js <
app_* env). Falls back to the existing LDAP_* env-var mode when the file is
absent (standalone/bare-metal still works).
- docker-compose.yml: trim `environment:` to NODE_ENV/NODE_PORT only and add
`./config:/config:ro`. Removing the app_* env is required — any leftover
app_* would silently override secrets.js.
- secrets.js.example: add orchestrator-only `stack`, `bootstrap`, and
`serviceAccountPass` keys (ignored by the app; read by the entrypoint, the
theta-env bootstrap, and setup.sh).
Backup/restore docs:
- Full "Backups and restore" runbook in DEPLOYMENT.md (what lives where,
manual backup, full / Redis-only / LDAP-only restore, AOF-vs-RDB note,
upgrades). Restore uses slapadd -f (static slapd.conf), and RDB restore
requires deleting the AOF first (AOF wins on startup).
- Pointers in docs/deployment.md and docs/ldap.md; update the Docker Setup
section for the new ./config/ approach (env vars now advanced/optional).
Co-authored-by: Claude <noreply@anthropic.com>