Files
proof-of-work/start9/0.4
Keysat 54fa77f2eb Sessions UI, CSV parser tests, route tests, composite indexes, verify-db action
Per-user sessions UI (Settings -> Active sessions)
- listMySessions returns the current user's still-valid sessions with
  last-8-char token suffix (UX hint) and an isCurrent flag (the
  authoritative "this device" marker).
- revokeSession refuses if the target is the actor's current token —
  use Sign out for that flow. Per-row Revoke button on every other.
- revokeAllOtherSessions = the previously-internal `deleteOtherSessions`
  helper exposed as a single button "Sign out other devices".
- All gated to the actor's own userId (never lets a user touch another
  user's sessions).

CSV parser refactor + tests
- Extracted parseCSV, NAME_MAP, parseFloatMaybe, parseIntMaybe,
  getVariationNote, resolveExerciseName, parseDate from
  app/api/import/parse/route.ts to lib/csvParser.ts. Behavior
  byte-identical; route is now a thin wrapper that imports from the
  lib.
- 18 tests covering: empty input, simple rows, lowercased headers,
  quoted-field commas, escaped double quotes, CRLF normalization,
  empty-line handling; numeric maybe-parsers; getVariationNote known
  patterns + null pass-through; ALL 27 NAME_MAP entries map to their
  canonical target; named CSV-shorthand examples; M/D/YYYY + ISO date
  parsing with noon-UTC anchoring (so US negative-offset zones still
  see the same calendar day).

Workout + exercise CRUD route tests
- New tests/routes-crud.test.ts: GET/POST /api/exercises, GET/POST
  /api/workouts. 401 on unauthenticated, per-user data isolation,
  query filtering, soft-delete exclusion, isCustom stamping, duplicate
  detection, type-driven inputFields defaults (cardio gets
  duration+calories), Zod validation rejection, set creation with
  weight/reps/rpe persisted, negative-reps rejected.
- Helper builds NextRequest objects so the routes' nextUrl.searchParams
  access works.

Composite indexes for hot query paths (schema.prisma + entrypoint)
- Session: (userId, expiresAt) for "list my still-valid sessions" and
  per-user cleanup.
- Workout: (userId, deletedAt, date) for the workout list query
  (filter by user + alive + date order).
- SetLog: (workoutId, setNumber) for the always-ordered set fetch
  under each workout.
- Existing single-column indexes kept; composites are additive.
- Entrypoint runs CREATE INDEX IF NOT EXISTS so live snapshots pick
  up the new indexes on first boot after upgrade.

verify-database StartOS action (start9/0.4/startos/actions/verifyDatabase.ts)
- Read-only. Runs PRAGMA integrity_check + quick_check + row-count
  queries against /data/app.db, reports as a structured result.
- allowedStatuses: only-running. Mounts the volume read-only.
- Use after a StartOS Backup, after a host crash, or after a fresh
  sideload to confirm the data is sound before relying on it.

Test suite now 67 tests across 7 files in ~2.4s.
2026-05-09 10:53:30 -05:00
..

Proof of Work Logo

Proof of Work on StartOS 0.4 (migration package)

This directory packages Proof of Work (proof-of-work) for StartOS 0.4 beta. It is the cutover package that carries your 0.3.5 data across to a new x86_64 StartOS 0.4 host.

Upstream app lives at ../../proof-of-work/ in this repo. Legacy 0.3.5 package lives at ../0.3.5/ (kept intact; do not modify). Codex's WIP 0.4 scaffold lives at ../0.4/ (kept intact; superseded by this folder).

Goals

  • Keep the package id proof-of-work so StartOS recognizes it as the same service.
  • Keep the persistent data volume main mounted at /data.
  • Keep the SQLite database at /data/app.db.
  • Preserve every existing workout, set, exercise, and preference.
  • Ship x86_64 only for 0.4 beta (sideload target).

How data preservation works

  1. seed/data/app.db holds a one-time snapshot of /data from the live 0.3.5 host (currently 1 user, 348 workouts, 164 exercises, 5720 set logs).
  2. The Dockerfile bakes that snapshot into the image at /app/seed/data/.
  3. On first boot only/data/app.db missing AND /data/.seeded absent — docker_entrypoint.sh copies the seed into /data/ and writes a .seeded marker.
  4. On every subsequent boot, /data/ is the sole source of truth; the seed in the image is ignored.

See seed/README.md for the snapshot provenance and row counts.

Image runtime

Property Value
Base image node:20-alpine (multi-stage build)
App runtime Next.js standalone + Prisma + SQLite
Entrypoint /usr/local/bin/docker_entrypoint.sh (dumb-init wrapped)
Internal port 3000
Architectures x86_64 (beta)

Build and sideload

cd start9/0.4
npm ci
make clean
make x86      # outputs proof-of-work_x86_64.s9pk

Sideload via StartOS web UI or make install (requires ~/.startos/config.yaml). Step-by-step instructions are in DEPLOY_040.md.

What is unchanged from 0.3.5

  • Package id: proof-of-work
  • Volume id: main
  • Mount path: /data
  • DB path: /data/app.db
  • Health endpoint: /api/health
  • Compat ALTER TABLE block (idempotent; no-op on a current DB)

What is new in 0.4

  • TypeScript SDK manifest under startos/
  • ExVer version (0.1.0:18) replaces the 0.3.5 4-part 0.1.0.17
  • Seed-on-first-boot with a .seeded marker and stderr logging
  • alertUpdate warning users not to Uninstall to troubleshoot
  • Self-contained Dockerfile — no references to ../0.3.5/ or ../0.4/

Follow-up releases (planned, do not ship yet)

  • v0.1.0:19 — remove the COPY seed/data \u2026 line and the seed block from the entrypoint once the cutover is confirmed. Leaves seed/ on disk unreferenced.
  • v0.1.0:19 or v0.1.0:20 — add a StartOS Package Action change-admin-credentials that updates the User row in /data/app.db (bcryptjs, salt rounds 10) so you can rename/rotate the admin from the StartOS UI.