# Start9 Packaging Log: Workout Log (from example to working 0.3.5 wrapper) This file records exactly what was adapted from `start9-example-packaging` to package `workout-planner` for a Start9 server on StartOS `0.3.5` (Raspberry Pi / ARM64). It is written as reusable documentation so you can repeat this process for future apps. ## 0) Goal and constraints - Target now: StartOS `0.3.5` on Raspberry Pi. - Future target: StartOS `0.4.0` when stable. - Priority: package should work now, while keeping data layout and wrapper structure easy to migrate later. ## 1) What was reviewed first Reviewed the existing example wrapper in `start9-example-packaging/0.3.5`: - `manifest.yaml` - `Makefile` - `Dockerfile` - `docker_entrypoint.sh` - `healthcheck.sh` - `instructions.md` - `README.md` - `DEPLOY_035.md` Then reviewed the app in `workout-planner`: - App Docker build/runtime behavior (`workout-planner/Dockerfile`) - DB shape + config (`prisma/schema.prisma`, `DATABASE_URL` usage) - Health endpoint (`/api/health`) - Seed/default user (`prisma/seed.ts`) ## 2) New Start9 wrapper scaffold created Created a **new** folder (separate from your example): - `start9/0.3.5/` - `start9/0.4/` (planning placeholder) Files created in `start9/0.3.5`: - `manifest.yaml` - `Dockerfile` - `docker_entrypoint.sh` - `healthcheck.sh` - `Makefile` - `instructions.md` - `README.md` - `DEPLOY_035.md` - `LICENSE` - `icon.png` (copied from app icon assets) Also created: - `start9/0.4/README.md` (migration intent notes) ## 3) Key packaging design decisions ### 3.1 Keep persistent data in `/data` To make upgrades/migrations safer, wrapper mounts Start9 volume at `/data` and stores SQLite DB at: - `/data/app.db` This is the most important continuity contract for migration to a future wrapper. ### 3.2 Keep runtime app files out of persistent volume App code/binaries stay in image layers; only state goes in `/data`. This prevents app updates from overwriting user data. ### 3.3 Health checks use app endpoint Wrapper health check calls: - `http://127.0.0.1:${PORT}/api/health` This checks both server and DB connectivity (as implemented by your app). ### 3.4 Backup/restore copies whole `/data` Manifest backup/restore actions copy `/data` <-> `/backup` to align with Start9 expectations and keep DB safe. ### 3.5 Future 0.4.0 migration posture Added explicit notes to preserve: - package id (`workout-log`) where compatible - DB path contract (`/data/app.db`) - backup semantics ## 4) Runtime wiring added for first boot In `docker_entrypoint.sh`: - Ensures `/data` exists. - Uses `/data/app.db` as `DATABASE_URL` target. - If `/data/app.db` does not exist on first run, copies a seeded template DB from image (`/app/prisma/data/app.db`). - Starts app with `node /app/server.js`. Why this matters: - First launch has a ready DB + default user. - Subsequent restarts/upgrades keep existing `/data/app.db` untouched. ## 5) Build issues discovered and fixes applied ## Issue A: Prisma/OpenSSL runtime failure on ARM musl Observed error during ARM container validation: - Prisma engine expected OpenSSL compatibility; DB access failed. Fix applied in wrapper `Dockerfile`: - Install `openssl` in **builder** stage. - Install `openssl` in **runner** stage. Result: - Prisma client loads correctly at runtime. ## Issue B: First-run DB missing tables (`main.User` does not exist) Root cause: - Repo `.dockerignore` excludes local `.db` files, so no seeded DB was copied from source tree. Fix applied in wrapper `Dockerfile` build stage: 1. Generate Prisma client. 2. Create temporary DB: `DATABASE_URL=file:/tmp-seed/app.db npx prisma db push --skip-generate` 3. Seed it: `DATABASE_URL=file:/tmp-seed/app.db npm run db:seed` 4. Copy seeded DB into image: `/app/prisma/data/app.db` Result: - First boot can copy seeded DB into `/data/app.db`. - Health endpoint reports DB connected. ## 6) Validation steps run Successfully validated: 1. ARM image build from wrapper: - `make -C start9/0.3.5 image-arm` 2. Local smoke run from built image tar: - `docker load -i start9/0.3.5/image.tar` - run container and query `/api/health` Final smoke result: - HTTP `200` - JSON contained `status: ok` and `database: connected` ## 7) Why `start-sdk pack` failed on this machine `start-sdk pack` failed with: - `fatal: not a git repository (or any of the parent directories): .git` Meaning: - `start-sdk` expects to run from inside a Git repository so it can compute metadata (commit/hash). This is unrelated to your app logic; it is a packaging environment requirement. ## 8) What “Step 1” means (plain English) When I said “initialize under git,” I meant: - The folder where you run `start-sdk pack` must be inside a Git repo. If your `Workout-log` folder is just a normal folder today, do this once: ```bash cd /Users/macpro/Projects/Workout-log git init git add . git commit -m "Initial commit for Start9 packaging" ``` After that, this should work: ```bash make -C start9/0.3.5 package ``` You do **not** need your local dev server on port `3000` running for packaging. Packaging builds a Docker image and `.s9pk` artifact separately. ## 9) Install flow on StartOS 0.3.5 1. Build package: ```bash cd /Users/macpro/Projects/Workout-log make -C start9/0.3.5 package ``` 2. In StartOS UI (0.3.5), sideload: - `start9/0.3.5/workout-log.s9pk` 3. Install + start service. 4. Open service UI and login: - `admin@local` / `workout123` 5. Change password immediately. 6. Run a manual backup. ## 10) Reusable checklist for your next app Use this sequence next time: 1. Copy known-good wrapper structure (`manifest`, `Dockerfile`, `entrypoint`, `healthcheck`, docs, makefile). 2. Define persistent data contract first (`/data/...`). 3. Ensure first boot initializes DB/schema (migration or seeded template). 4. Verify health endpoint checks both app + DB. 5. Build ARM image and smoke-test locally before Start9 sideload. 6. Ensure repo is a Git repo before `start-sdk pack`. 7. Document migration invariants for future StartOS versions (ID, DB path, backup format). ## 11) Files to edit before publishing/distributing In `start9/0.3.5/manifest.yaml`, replace placeholder values: - `wrapper-repo` - `upstream-repo` - `support-site` - `marketing-site` - `license` (if you choose MIT or another license)