v1.2.9
If you run openGym for other people, you have had no way to answer "who signed in, and when?" —
the server kept no record of anything. It does now: an activity log in the admin dashboard,
covering sign-ins, sign-outs, the attempts that failed, and every admin action. The other half of
this release is that the live demo is back, self-hosted this time, after two months offline.
And the project has a working home again: openGym now lives on GitLab, where CI builds the
container images and the signed Android APK for every release — the thing that has been missing
since the GitHub account went.
Activity log
- 🧾 The admin dashboard has an activity log. Successful and failed sign-ins, profile
creations and refused signups, sign-outs (including "sign out everywhere"), and every admin
action: disabling or re-enabling an account, creating or revoking an invite code, and clearing
the log itself. Filter it by sign-ins, admin actions or failures, and page back through it. - 📄 It is a plain file.
./data/audit.log, one JSON object per line —tail -fandjq
read it directly, which also means it is its own export format. Deliberately not part of
db.json: that file is rewritten in full on every save, and the sign-in handshake is
unauthenticated, so a log living in there would have turned one junk request into a full
rewrite. Retention is a cap rather than an archive — the lastAUDIT_MAXevents (5,000) or
AUDIT_DAYSdays (90), whichever runs out first. - 🔒 It records less than you might expect, on purpose. No IP addresses unless you turn them
on (AUDIT_IP=netkeeps only the network,fullkeeps the address); never the browser's
user-agent; and never the passkey id behind a failed sign-in, because that id is a stable handle
for one device and storing it would let an admin follow an unknown device from one attempt to
the next. A rejected invite code is not stored either — a near-miss guess sitting in a log file
helps nobody. - 🧹 Clearing it is itself logged, and the event ids keep counting, so an erased stretch always
leaves a visible gap. - ⚙️ On by default when you update, and one variable turns it off. It records strictly less
than your instance already holds — every profile is indb.json, every workout is in
state-<uid>.json, and any admin can already read both — and a log that ships switched off
tells you nothing on the day you need it.AUDIT_LOG=0disables it completely; no file is
written. Nothing leaves your server either way: this is a local file, not telemetry. - Guests still never appear anywhere — guest mode does not talk to the server at all.
The live demo is back
▶️ https://opengym.duarte-santos.ch/demo/ — the in-browser demo, running on the project's
own site instead of GitHub Pages, which went down in August with the suspended account. Same
build as before: no backend, no account, seeded example history, and a reset button in its
settings. The embedded demo on the landing page works again too.
openGym moved to GitLab
- 🏠 https://gitlab.com/DuarteSantos8/opengym is the home of the project. Same history,
same tags, same AGPL. gitea.com was the stopgap after the GitHub suspension and stays as a
mirror; it never had a CI runner, which is why releases there had no images. - 🐳 Prebuilt images are back.
docker compose pullnow fetches
registry.gitlab.com/duartesantos8/opengym/apiand/web, built for amd64 and arm64 on
every release. Pulling is anonymous — the project is public, no login, no token. - 🤖 The APK is built by CI now, not by hand. Every
vX.Y.Ztag produces azipaligned,
signed APK, attached to the GitLab release and mirrored onto the download page. The signing
key sits in protected CI variables, so it exists only onmainand on version tags — a
merge request from a fork builds an unsigned APK and never touches the key. - ✅ Every merge request is tested again. The frontend suite (346 tests), the locale checks,
the fatigue probe and the MCP suite all run on GitLab CI. The GitHub Actions workflows stay
in.github/for the day that account comes back. - 🌐 The in-browser demo also builds to GitLab Pages
(https://opengym-bc111a.gitlab.io/, which https://duartesantos8.gitlab.io/opengym/
redirects to); https://opengym.duarte-santos.ch/demo/ remains the copy the landing page
embeds. - 📄 Security reports have a private channel again: a confidential issue on GitLab. See
SECURITY.md. - 🔁 Dependency updates continue. GitLab has no Dependabot, so Renovate runs from a monthly
scheduled pipeline with the same deliberately quiet policy the Dependabot config had:
grouped per ecosystem, majors on their own, odd-numbered Node images skipped, and the
generatedandroid//ios/projects left to follow their@capacitor/*packages. Security
advisories ignore the schedule and land on their own.
Housekeeping
- The self-hosting docs,
SECURITY.mdand.env.examplecover the activity log, and the
api/server.jsline references inSECURITY.mdare accurate again. - Every repository link in the README, the docs, the app and the website points at GitLab, and
the website's live star/release numbers come from GitLab's API.
Published on GitHub on 2026-09-11, mirroring the GitLab release of the same tag, where the artefacts above were built (!NN numbers are GitLab merge requests). The same images are on GHCR, built from this release by GitHub Actions: ghcr.io/duartesantos8/opengym-api:1.2.9 and ghcr.io/duartesantos8/opengym-web:1.2.9.