Files
proof-of-work/CHANGELOG.md
T
Keysat 5de974edaf ESLint, server-action tests, export-my-data, enriched healthcheck, CHANGELOG
ESLint
- Pinned eslint@^8 + eslint-config-next@^14 to match Next 14's `next lint`.
  ESLint 9's flat-config breaks `next lint` for legacy projects.
- .eslintrc.json extends next/core-web-vitals; ignores tests/, scripts/,
  prisma/data/, .next/, node_modules.
- 7 pre-existing warnings surfaced (exhaustive-deps + alt-text + img tag
  in user-written components). Left as warnings — pre-existing, not
  breaking. CI runs lint; warnings don't fail the job.

Server action tests (tests/actions-admin.test.ts, tests/actions-auth.test.ts)
- Vitest setup file (tests/helpers/setup-actions.ts) sets DATABASE_URL
  to a per-process temp SQLite DB and runs `prisma db push` BEFORE
  lib/prisma instantiates its global PrismaClient. Tests then call the
  real server actions against an isolated DB.
- vi.mock + vi.hoisted to mock @/lib/auth.getCurrentUser, next/headers
  cookies+headers, next/navigation redirect, next/cache revalidatePath.
- Coverage:
  - admin: setUserAdmin (Forbidden, promote, last-admin demote refused,
    demote-with-other-admin allowed), deleteUser (last-admin guard,
    self-delete refused, cascading delete to exercises + workouts),
    adminResetPassword (hash-and-revoke, short-password rejected).
  - auth flows: signupAction (closed by default, opens-and-creates,
    mismatched confirm rejected, short pwd rejected, malformed email
    rejected, no email-enumeration leak), changePasswordAction
    (rotate-and-revoke-others, wrong current pwd rejected, no-op pwd
    rejected), deleteMyAccountAction (phrase required, password required,
    last-admin refused, success cascades + clears cookie + redirects).
- Total suite: 34 tests, ~2s.

Export my data (/api/me/export + Settings -> Export my data)
- Downloads a JSON dump of every workout/set/exercise/program tied to
  the user. Excludes password hash and sessions. Filename includes
  email + date. content-disposition: attachment, no-store cache.
- Exported shape matches the underlying tables 1:1 so a future "import
  my data" flow can round-trip without ambiguity.

Enriched /api/health
- Now reports: database.connected, database.journalMode (and walEnabled
  shortcut), users count, instanceSettings.signupsOpen, library.available
  + sizeBytes. Surfaces a `warnings` array if journal_mode != 'wal' but
  doesn't fail the check (app still works without WAL — just unsafe for
  online backups). Returns 503 only on hard DB failure.

CHANGELOG.md
- Single Unreleased section documenting everything that will ship as
  v1.0.0:1 once the maintainer drops a fresh /data snapshot. Added /
  Changed / Removed / Compat-notes sections.
2026-05-09 10:41:13 -05:00

5.3 KiB

Changelog

All notable changes to Proof of Work and its StartOS package wrapper. The format roughly follows Keep a Changelog; versions track the StartOS package release rev (upstream:rev per ExVer).

[Unreleased]

This is everything in master since the last published .s9pk. Will ship as v1.0.0:1 once the maintainer drops a fresh /data snapshot into start9/0.4/seed/data/app.db and runs make x86 && make install.

Added

  • Multi-user support. Every install starts with one admin (admin@local) and sign-ups closed. The admin opens sign-ups via Settings -> Instance Settings or via the new StartOS package action "Set new signups". New users start with no admin privileges and the full curated exercise library auto-seeded.
  • Curated exercise library at proof-of-work/prisma/exercises.seed.json. Used by prisma/seed.ts for fresh installs and by ensureExerciseLibrary.cjs (run from docker_entrypoint.sh on every boot) so library updates flow to existing installs additively, never overwriting users' own custom exercises.
    • npm run sync-library regenerates the JSON from the live snapshot.
  • In-app password change at Settings -> Change password. Verifies current password, requires 8+ char new password, auto-revokes every other session for the user.
  • Admin user management at /main/admin/users. List / promote / demote / reset-password / delete with last-admin guard and self-delete guard. Admin-initiated password reset force-revokes all the target's sessions.
  • Self-serve account deletion at Settings -> Danger Zone. Requires current password AND typing the literal phrase "delete my account". Refused for the last admin. Cascades through Prisma onDelete.
  • Last-login tracking (User.lastLoginAt). Stamped on every session creation, displayed as a relative-age cell in the admin Users table.
  • Export my data at Settings -> Export my data. Downloads a JSON with every workout, set, exercise, program tied to the user. Password hash and sessions excluded.
  • Rate limits on /auth/login (10/IP/15min) and /auth/signup (5/IP/15min). In-process sliding window, no deps.
  • Security headers: Content-Security-Policy (with frame-ancestors 'none', form-action 'self', object-src 'none'), Strict-Transport-Security, Referrer-Policy, Permissions-Policy.
  • SQLite WAL mode + synchronous=NORMAL enabled in entrypoint. Keeps readers from blocking on a concurrent backup-time writer.
  • StartOS Package Action change-admin-credentials now keys on WHERE isAdmin = 1 ORDER BY createdAt ASC LIMIT 1 (previously oldest user regardless of role).
  • StartOS Package Action toggle-signups: same setter as the in-app admin toggle, accessible from the StartOS UI without an admin login. Asserts read-back matches written value.
  • Test suite (Vitest, 34 tests, ~2s): rate limit, hashing, curated-library multi-user idempotency, admin actions including last-admin guard, signup gate + email-enumeration leak check, password-change, account deletion.
  • GitHub Actions CI: app job runs prisma validate + prisma generate
    • tsc + lint + tests; startos job runs the package's npm run check (tsc --noEmit). Both on push and PR to master/main.
  • ESLint config (.eslintrc.json extending next/core-web-vitals). Wired into CI.
  • Enriched /api/health reports DB connection, journal mode (WAL status), library JSON availability, instance signup state, and user count. 503 if DB is unreachable; warnings field for non-fatal issues (e.g. journal_mode != wal).

Changed

  • Rebranded workout-log -> proof-of-work end-to-end. Folder, npm package name, StartOS package id. StartOS treats this as a brand new service; cutover from the legacy package is via baked seed in v1.0.0:1, not in-place upgrade.
  • Session tokens now 256-bit crypto.randomBytes hex. Were derived from Math.random() + Date.now() — predictable enough that a determined attacker could enumerate other users' tokens. Existing sessions invalidate on upgrade by design — token format/length changed, old tokens won't validate.
  • Version graph reset to 1.0.0:1 (was on the legacy workout-log v0.1.0:18 / :19 / :20 line).

Removed

  • Legacy start9/0.3.5/ package (StartOS 0.3.5 wrapper, 65MB image artifacts, no longer the deploy target).
  • start9-example-packaging/ template from another project.
  • Workout-planner standalone Dockerfile + docker-compose.yml (replaced by the StartOS package's own Dockerfile).
  • Various planning docs replaced by start9/0.4/DEPLOY_040.md and the root README.md.

Compat notes for cutover

The boot-time entrypoint runs idempotent ALTERs that:

  • Add User.isAdmin and auto-promote the oldest user to admin if no admin exists (preserves admin functionality across the cutover).
  • Add User.lastLoginAt.
  • Create the InstanceSettings table + singleton row (signupsOpen=0).
  • Switch SQLite to WAL mode (persists in DB header thereafter).

So a snapshot pulled off the legacy workout-log host comes up as a working multi-user proof-of-work install with no manual SQL.


v0.1.0:17 and earlier (legacy workout-log package)

Out of scope for this changelog. The legacy package's history lives in the git log; nothing in this repo references it after v1.0.0:1 ships.