- Fix MIT LICENSE copyright placeholder
- Remove private flag and correct GitHub repository URL in package.json
- Bump version to 1.1.16
- Genericize committed config defaults (example.com/localhost)
- Harden global error handler against information leakage
- Generate random initial password for proxyadmin2 bootstrap account
- Correct docs to describe CONF_SECRETS instead of symlink behavior
Co-Authored-By: Claude <noreply@anthropic.com>
- ops/install.sh now installs to /opt/theta42/proxy (was /var/www/proxy)
and seeds /etc/proxy/secrets.js from secrets.js.example on first run
(never overwritten on later runs), instead of requiring a manual
nodejs/conf/secrets.js edit inside the repo checkout.
- ops/proxy.service points at the new install path and sets
CONF_SECRETS=/etc/proxy/secrets.js (requires @simpleworkjs/conf >=
1.2.0, already the pinned version) so the app picks up the secrets
file with no symlink into the repo checkout.
- install.sh now prints the version it's updating from/to (or "Already
up to date") on every run, instead of a silent update.
- Updated README/DEPLOYMENT/installation docs to match the new paths.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- New docs/concepts-{hosts,dns,access,api-tokens}.md -- plain-language
guides aimed at less technical readers, each linking onward to the
existing system-design-level doc for anyone who wants that detail.
Card help links (Proxy List, Add/Edit host, DNS Provider cards,
Users/Permissions/Groups cards) now point here instead of straight at
Installation/Architecture.
- The "New API Token" card had no help link at all -- added, pointing to
the new API Tokens doc.
- Fixed the in-app docs viewer rendering every docs/*.md page with a
garbled heading + stray <hr> at the top: Jekyll front matter (meant
only for the GitHub Pages build) was never stripped before being
handed to the markdown renderer.
- Fixed cross-doc links never resolving in-app, since this viewer serves
docs at /docs/<slug> with no .html suffix: rewritten to the correct
in-app URL, first by registered slug, falling back to the doc's real
filename (the correct, working link form on the Jekyll/GitHub Pages
build) -- same idea as the existing image-path fix, and lets one link
written in a doc work on both targets.
Bumps to v1.1.13.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
Same treatment as the sso-manager-node companion PR: replaced the
generic jekyll-theme-cayman theme with a custom layout mirroring 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. Keeps this repo's own favicon.svg
(confirmed genuinely distinct SVG artwork from the shared theta42
logo, not a duplicate) as the browser-tab icon.
- New cross-page nav (Home/Installation/Architecture/API/Docker/
Contributing/Changelog).
- SEO: jekyll-seo-tag + jekyll-sitemap, per-page meta description,
OG/Twitter card tags, canonical URLs, JSON-LD, sitemap.xml,
robots.txt.
- Mobile: Bootstrap's responsive grid + collapsible navbar; the
screenshot pairs in index.md stack to full-width below 576px.
Verified with a real Jekyll build (jekyll/jekyll Docker image) +
Playwright: desktop and mobile (375px) screenshots, mobile nav
toggle, active-link highlighting, zero console/page errors, and
confirmed real SEO output + the correct (non-shared) favicon via curl
against the served site.
- Rewrite docs/index.md as a short landing page (what it is, screenshots,
why this over the alternatives, features, a minimal "get it" snippet)
instead of a full documentation dump — full docs live in the repo
(README, docs/*.md) and are linked from here.
- Cross-link to SSO Manager and theta-env's own Pages sites.
- Screenshots are now clickable (open full size) on both the Pages site
and the README.
- Disable show_downloads in docs/_config.yml — the Cayman theme's
"Download .zip/.tar.gz" buttons are gone; "View on GitHub" (which links
back to the repo) is the only header link now.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Captured from a fresh theta-env install with demo data, via headless
Chrome + Playwright (scripted login, no manual UI interaction needed to
reproduce).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reported error when adding a DuckDNS provider:
TypeError: this.domains.map is not a function
at Proxy.updateDomains (models/dns_provider.js:185:37)
DnsProvider.__intraModel merges `{...DnsProvider._keyMap,
...Provider._keyMap}`, so a provider-defined field with the same name
as one of DnsProvider's own (created_by, updated_by, name,
dnsProvider, domains, id) silently overwrites it. DuckDNS defined a
`domains` field (the operator-supplied comma-separated subdomain
list), which replaced DnsProvider's `domains` relation (rel: 'many' to
Domain, populated by updateDomains()) — so `this.domains` stopped
being the array relation and became DuckDNS's raw string instead.
Rename the field to `subdomains` throughout (model, docs, tests). Add
a comment on __intraModel documenting the collision risk for future
providers, and a regression test asserting no registered provider's
_keyMap redefines one of DnsProvider's reserved field names.
DuckDNS's API is smaller than the other providers' (no list/read API,
no arbitrary sub-records, one A/AAAA + one TXT record per domain), so
domains are entered by the operator instead of auto-discovered, and
getRecords reads from public DNS since there's nothing else to query.
Documented as a free option in the README and DNS provider docs.
Prepares the docs for the public release announcement: removes obsolete/dead
material, fixes drift between the API reference and the actual routes, and
standardizes on the default GitHub Pages URL.
- Remove Vagrant entirely: delete Vagrantfile, docs/dev_setup.md, and stale
vagrant references in .gitignore/.dockerignore; rewrite openresty/README.md
to describe the actual (currently unused) directory and point to
ops/nginx_conf/ for the real OpenResty config.
- Delete docs/Update 4.11.md (personal scratch changelog) and drop both its
and dev_setup.md's references from docs/README.md's Legacy Documentation
section.
- Remove checkmark emoji from docs/contributing.md's PR Requirements list.
- Bring the auth model docs up to date with the code: document
GET /api/auth/oidc/start + /callback, the /api/permission and /api/group
RBAC routers, the /api/dns/dynamic/* sub-API, and /api/api-token (self
-service PATs) in both nodejs/api.md and docs/api.md; add the missing
"Clear Host Cache" section; drop the invite-token/SSH-key endpoints that no
longer exist in nodejs/routes/user.js; note admin-only routes. Mention
OIDC/LDAP/RBAC as core features in README.md.
- Keep nodejs/api.md and docs/api.md fully in sync (same body, differing only
in Jekyll front matter / relative links) instead of letting them drift.
- Fix Node.js version references (20.x -> 22.x) in README.md and
docs/installation.md to match ops/install.sh and the Dockerfile.
- Note that the manual nginx-conf/systemd install steps in README.md and
docs/installation.md won't auto-track repo changes the way install.sh's
symlink approach does, and recommend install.sh.
- Update the stale test/unit file lists in docs/contributing.md and
nodejs/test/README.md to match the actual directory contents.
- Add npm run test:integration to README.md's Running Tests section.
- Add nodejs/conf/, nodejs/controller/, and nodejs/migrations/ to the project
structure diagrams in README.md, docs/architecture.md, and
docs/contributing.md.
- Standardize "CloudFlare" -> "Cloudflare" everywhere to match the actual API
value in nodejs/models/dns_provider.js.
- Add the missing app_auth__adminGroups row to DEPLOYMENT.md's app_* table.
- Delete docs/CNAME (custom domain) so GitHub Pages serves from the default
https://theta42.github.io/proxy/, matching docs/README.md.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up to #121: the repo line still used the live codename + "main", but
the openresty.org Debian tree only publishes up to bookworm (no trixie) and
uses the "openresty" component, not "main". Verified against the repo:
/package/debian/dists/ -> bookworm bullseye buster jessie stretch (no trixie)
bookworm Release -> Components: openresty
/package/ubuntu/dists/ -> noble jammy focal ... ; Components: main
So:
- Debian: distro = host codename when published (jessie..bookworm), else
bookworm (binary-compatible with trixie, same OpenSSL 3 era); component
"openresty".
- Ubuntu/Mint: distro = host codename; component "main" (unchanged).
Produces the working line on a trixie host:
deb [...] http://openresty.org/package/debian bookworm openresty
docs/installation.md manual steps updated to match.
Co-authored-by: Claude <noreply@anthropic.com>
Two issues on Debian 13 (Trixie):
1. apt's sequoia GPG backend now rejects SHA-1 signatures, and the OpenResty
repo signing key is still SHA-1 — so `apt-get update` fails to verify the
repo. When /usr/share/apt/default-sequoia.config is present (Debian 13+),
install a back-end override that extends the SHA-1 acceptance window to
2028 (the OpenResty key is expected to rotate to a stronger algorithm;
revisit before then). No-op on older Debian/Ubuntu. Idempotent on re-run.
2. The repo path was hardcoded to /package/ubuntu with the host codename,
which worked on older Debian by coincidence. trixie lives under
/package/debian, so pick the tree by distro ID (debian -> /package/debian,
else -> /package/ubuntu).
docs/installation.md: mirror both changes in the manual install steps, with a
note that install.sh applies the sequoia override automatically.
Co-authored-by: Claude <noreply@anthropic.com>
Personal access tokens so scripts/CI can call the management API without an
OIDC browser session. Each logged-in user mints their own token; it
authenticates as the creator (groups snapshotted at mint, mirroring the proxy's
browser AuthToken), and the existing authz layer (Permission.effectiveFor /
roles.resolveEffective) applies unchanged. Local groups and owned-domain rights
are recomputed live; only SSO/LDAP group membership is the mint-time snapshot.
- models/api_token.js: new ApiToken model (prx_<id>_<secret> format; id is the
lookup key, secret bcrypt-hashed + isPrivate, shown once). add()/rotate()/
authenticate(); optional expires_at; best-effort last_used_on; groups
snapshot. No _ttl (persists). Deliberately NOT wrapped in ModelPs (so the
last_used_on write on the auth path doesn't spam the socket).
- routes/api_token.js: self-service CRUD (list/get/update/delete/rotate),
owner-scoped (created_by === reqUsername(req), 403 otherwise).
- middleware/auth.js + models/auth.js: accept `Authorization: Bearer prx_...`
(precedence over the auth-token session header). Builds a synthetic req.token
that satisfies the only three req.token reads (auth.js .user/.groupsArray,
authz.js reqUsername .created_by) so the authz layer works unchanged.
checkApiToken collapses every failure to one generic 401 (no leak).
- views/api_tokens.ejs + routes/render.js (GET /api-tokens): self-service page
(forceLogin, no admin gate) — create (token shown once), rotate, revoke.
- views/top.ejs: "API Tokens" nav entry visible to all logged-in users.
- public/js/app.js: app.apiToken client module.
- DEPLOYMENT.md + docs/docker.md: API tokens section.
Co-authored-by: Claude <noreply@anthropic.com>
Lossless upgrades + config story for the all-in-one proxy image.
Redis persistence (Part A):
- Replace in-memory `--save "" --appendonly no` with AOF + RDB persisted to
/data. Host records, permissions, DNS creds, local users, AND the
lua-resty-auto-ssl Let's Encrypt certs now all survive container recreation
(persisting Redis persists the cert store — no LE re-issue / rate-limit on
rebuild).
- Add the `proxy-data` named volume -> /data in docker-compose.yml; fix the
stale "in-memory, lost on recreation" comment.
Config from ./config/proxy-secrets.js (Part B):
- docker-entrypoint.sh: when /config/proxy-secrets.js is mounted, symlink it to
/app/conf/secrets.js so @simpleworkjs/conf reads the oidc/ldap/auth config
from the file. No app_* env should then be passed (app_* beats secrets.js).
Falls back to app_* env when the file is absent (standalone still works).
- docker-compose.yml: drop all app_oidc__* / app_ldap__* / app_auth__* env and
add `./config:/config:ro`. Keep RESOLVER/REAL_IP_FROM/NODE_ENV/NODE_PORT
(OpenResty-runtime / process env, not app_* config). No env_file.
- New secrets.js.example (the proxy had none): oidc (enabled, endpoints,
clientId/clientSecret, redirectUri, scopes, claims), ldap (url, bindDN,
bindPassword, searchBase, userFilter, tlsOptions), auth (adminGroups,
adminUsers, groupRoleMap), plus an orchestrator-only `stack` key.
Backup/restore docs:
- Full "Backups and restore" runbook in DEPLOYMENT.md (what lives where, manual
backup, Redis restore with the AOF-vs-RDB note — AOF wins on startup so the
AOF must be deleted before an RDB load; restoring Redis restores cert state
at snapshot time; migrations note). Update the Setup + Auto-SSL sections.
- docs/docker.md: update Quick start + How configuration works + Auto-SSL for
the new ./config/ approach (app_* env now advanced/optional).
Co-authored-by: Claude <noreply@anthropic.com>
- OpenResty checks Redis FIRST for every request
- Node.js is only queried as fallback when Redis has no entry
- Cached hosts continue working even if Node.js goes down
- Updated diagram to show lookup hierarchy
- Restructured caching strategy to emphasize L1/L2 tiers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>