Repo cleanup - Add top-level .gitignore (was missing; node_modules, .next, *.s9pk, image.tar, seed/data/*.db, log files, etc.) and a root README. - Delete legacy start9/0.3.5/ package (StartOS 0.3.5 wrapper, no longer the deploy target). - Delete start9-example-packaging/ (template from another project). - Delete planning docs (START9_PACKAGING_LOG.md, VERSIONING.md, STARTOS_0.4_UPGRADE_PROMPT.md, ICON_FILES_INDEX.md, etc.) — info now lives in the deploy guide and code comments. - Drop the standalone Dockerfile, docker-compose.yml, ICON_*, and dev log/build artifacts from the app dir. - Drop the v0.1.0:18/19/20 version files (they belonged to the legacy workout-log package and don't apply to the new id). Rename + new package - Rename app dir workout-planner/ -> proof-of-work/. - Rename StartOS package id workout-log -> proof-of-work; the new id makes this a brand new StartOS service (clean cutover from the old one rather than in-place upgrade). - Reset version graph; v1.0.0:1 is the seeded cutover release. The Dockerfile bakes a one-time /data snapshot and docker_entrypoint.sh copies it into the new volume on truly-fresh first boot only (both /data/app.db missing AND /data/.seeded absent). - Move start9/0.4-migration/ -> start9/0.4/; the old start9/0.4/ stub is gone. Curated exercise library (multi-user-aware) - proof-of-work/prisma/exercises.seed.json is the canonical library shipped to every install (164 exercises today, dumped from the live snapshot). - proof-of-work/scripts/sync-library.cjs (npm run sync-library) refreshes the JSON from start9/0.4/seed/data/app.db after refresh_seed.sh. - proof-of-work/prisma/seed.ts now reads from the JSON instead of a hardcoded 52-exercise array; runs at Docker build time to seed the fallback DB and on first boot for fresh installs. - proof-of-work/prisma/ensureExerciseLibrary.cjs runs on every container boot (from docker_entrypoint.sh) and INSERT OR IGNOREs every library entry for every user, keyed on (userId, name). Library updates flow to existing installs on package upgrade; user-custom exercises (isCustom=true) and any colliding names are never overwritten; removed exercises stay on existing installs (additive-only). Deploy guide (start9/0.4/DEPLOY_040.md) - Rewritten end-to-end for the workout-log -> proof-of-work cutover: refresh_seed, sync-library, build, sideload, verify, rotate creds, stop the old service, then post-cutover cleanup release v1.0.0:2.
9.0 KiB
Deploy Proof of Work on StartOS 0.4 (sideload)
This guide walks the maintainer through cutting over from the legacy
workout-log package to the new proof-of-work package. They share the
same upstream code and on-disk schema, but proof-of-work is a brand new
StartOS service (different package id), so StartOS treats it as a fresh
install. Data preservation is handled by baking a one-time snapshot of
your live workout-log /data volume into the proof-of-work v1.0.0:1
image and copying it into the new volume on first boot.
Your existing workout-log install stays running and untouched the whole
time. The cutover is one-way only after you stop and uninstall it; until
then you can fall back to it.
NEVER click Uninstall on either package mid-cutover. Uninstall destroys the volume. Use Stop instead. Only Uninstall after you've verified proof-of-work is happy.
0) Prereqs on your build machine
You need:
- Node.js >= 20 and npm
- Docker with
buildx start-clifrom Start9 (SDK): https://docs.start9.com/packaging/0.4.0.x/environment-setup.htmljq,make,git,sqlite3,rsync,ssh- Reachable SSH access to your existing
workout-loghost
One-time start-cli key setup:
start-cli init-key
~/.startos/config.yaml should contain at least:
host: http://<your-04-host>.local
make install uses this to push the .s9pk to the target.
1) Refresh the seed snapshot from your live workout-log host
The repo includes start9/0.4/seed/data/app.db as a placeholder so the
build works without network access, but for an actual cutover you should
pull a fresh snapshot from your live host first.
./start9/0.4/refresh_seed.sh embassy@embassy.local
For a 0.3.5 host this auto-detects
/embassy-data/package-data/volumes/workout-log/data/main/. For a 0.4
host pass the volume path explicitly as arg 2 — get it from
start-cli package shell workout-log and df inside the container, or
from the StartOS docs.
Refresh prints expected row counts and runs PRAGMA integrity_check. If
anything looks off, fix the source before proceeding.
2) (Optional) Refresh the curated exercise library JSON
The shared library that ships with the package is generated from the same
snapshot. After refresh_seed.sh, regenerate the JSON if you've added
new exercises in the running app:
cd proof-of-work
npm run sync-library
git diff prisma/exercises.seed.json # eyeball the new rows
Commit the JSON change before building so it's baked into the image.
3) Build the package
cd start9/0.4
npm ci
make clean
make x86
On success you'll get an artifact at:
start9/0.4/proof-of-work_x86_64.s9pk
4) Sideload on the 0.4 host
Option A — make install (fastest)
make install
Option B — StartOS web UI
- Open the StartOS 0.4 web UI on the target host.
- System -> Sideload Service.
- Upload
start9/0.4/proof-of-work_x86_64.s9pk. - Click Install, then Start.
workout-log and proof-of-work will appear as two separate services in
the UI. Both can be running simultaneously — they have different volumes,
different ports the SDK assigns, and no shared state.
5) First-boot verification
5a. Confirm the seed branch ran
In Services -> Proof of Work -> Logs, or via CLI:
start-cli package logs proof-of-work | grep '\[entrypoint\]'
On the very first boot you should see:
[entrypoint] no /data/app.db and no .seeded marker; copying baked cutover seed from /app/seed/data/app.db
[entrypoint] ensuring curated exercise library is present for every user
[ensure-library] processed 1 user(s) x 164 exercise(s) (164 INSERT OR IGNORE statements)
[entrypoint] launching Next.js on :3000 with DATABASE_URL=file:/data/app.db
Subsequent boots:
[entrypoint] /data/app.db already present; live data is the source of truth
[entrypoint] found .seeded: seeded from baked cutover snapshot at 2026-...
[ensure-library] processed N user(s) x 164 exercise(s)
5b. Log into the app
Open the web UI from the StartOS service page and log in with the same
credentials you used on the legacy workout-log install (the bcrypt hash
came over with the snapshot, so existing passwords keep working).
Default seed credentials (only on a brand-new install with no baked
seed): admin@local / workout123 — change immediately via the action
in section 6.
5c. Spot-check the data
- Workouts count matches your old install (run-counts in the
refresh_seed.shoutput were the source of truth). - Exercise library shows your 164 exercises.
- Open a recent workout and confirm set logs match.
5d. Run a StartOS backup
Services -> Proof of Work -> Backup -> Create Backup. Confirm it
completes. This validates Backups.ofVolumes('main') wiring on the new
package.
6) Rotate admin credentials
Stop the service first (action is gated on allowedStatuses: only-stopped):
- Services -> Proof of Work -> Stop
- Actions -> Change admin credentials
- Fill in new email, new password, confirm. Submit.
- Start the service and log in with the new credentials.
7) Stop the legacy workout-log service
Once you've used proof-of-work for at least a session and confirmed
nothing's missing:
- Services -> Workout Log -> Stop
DO NOT Uninstall yet. Keeping the old service installed (just stopped)
preserves its /data volume in case you need to re-pull the snapshot or
roll back. Once you're confident (a week is plenty), Uninstall to free
the volume.
8) Post-cutover cleanup release (v1.0.0:2)
After a few days running on proof-of-work with no surprises, ship a
cleanup release that:
- Strips
COPY start9/0.4/seed/data /app/seed/datafrom the Dockerfile. - Strips the seed-copy branch out of
docker_entrypoint.sh(keep the fallback DB branch and the ensure-library step). - Leaves
seed/andrefresh_seed.shon disk as historical artifacts. - Adds
startos/versions/v1.0.0.2.ts(emptyup/downmigrations) and promotes it tocurrentin the version graph; v1.0.0:1 moves toother.
Reason: once /data is the sole source of truth, a baked seed in the
image becomes a foot-gun — removing it eliminates any chance of a future
upgrade accidentally stomping live data.
9) Library updates after cutover (steady state)
# add new exercises in the running app, then:
./start9/0.4/refresh_seed.sh embassy@embassy.local
cd proof-of-work && npm run sync-library
git add prisma/exercises.seed.json
# bump the build rev in start9/0.4/startos/versions, add a new version file,
# commit, then:
cd start9/0.4 && make clean && make x86 && make install
On every upgrade, every user on the instance picks up the new exercises
on first boot via the INSERT OR IGNORE step. Custom exercises a user
added themselves are never overwritten.
10) Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Login fails with your workout-log password |
Snapshot was pulled from a stale source | Re-run refresh_seed.sh against your live host and rebuild. |
| Workouts missing after cutover | Same | Same. |
/data/app.db is empty after first boot |
.seeded marker present without the DB (corrupted prior boot) |
start-cli package shell proof-of-work, rm /data/.seeded, restart. |
| Service never reaches Ready | Port 3000 not listening | Check logs: Prisma or Next.js threw at boot. Verify the DB file isn't zero bytes. |
[ensure-library] WARNING |
sqlite3 failed to apply the curated library | Inspect /data/app.db and the JSON. Boot continues; library updates skipped this cycle. |
| Container exits immediately | Build context wrong — proof-of-work/ not visible to Docker |
Run make from start9/0.4/, not the repo root. |
make x86 fails with No rule to make target .git/HEAD |
s9pk.mk's awk grabbed two id: matches and concatenated BASE_NAME |
Confirm manifest/index.ts has only one id: line. The shipped s9pk.mk uses {print $$2; exit} which already guards this. |
Docker fails with ERROR: failed to build: resolve : lstat start9: no such file or directory |
dockerBuild.dockerfile was set to a path including start9/0.4/ |
The shipped manifest sets dockerfile: './Dockerfile'. The dockerfile field is resolved relative to the package dir, not relative to workdir. |
11) Data-preservation summary
- Volume name (
main), mount path (/data), DB path (/data/app.db), internal port (3000): identical to the legacyworkout-logpackage. - Package id changed:
workout-log->proof-of-work. StartOS treats this as a brand new service. Migration is via the baked seed in v1.0.0:1, not via in-place upgrade. - Seed only writes to
/dataon truly-fresh first boot (bothapp.dbmissing AND.seededmarker absent). - Library updates (new exercises) flow to every user on every upgrade
via additive
INSERT OR IGNORE. Never deletes; never overwrites a user's own custom exercises. - StartOS Backup captures
/datain full. - Uninstall destroys
/data. ThealertUpdatecopy reminds users.