A security-first, single-user coaching app scaffold for training context, Strava imports, coach memory, and proactive Telegram nudges.
- Strava-first workout ingestion
- First-party dashboard as the system of record
- Coach memory and structured training context
- Telegram as the first messaging surface
- Strong privacy controls around health-adjacent data
Check out the setup runbook for detailed instructions on how to get the app up and running, either locally with Docker Compose or deployed to a host or cloud provider.
- Garmin or Coros data ingestion
- Runna or TrainingPeaks plan sync and write-back
- Multi-athlete support
- Voice, broader wellness coaching, and commercial-grade compliance work
apps/web— Next.js dashboard and API routesapps/worker— background job and messaging worker previewpackages/coach-core— shared coaching domain types, sample data, and deferred feature specspackages/db— Postgres schema, migrations, and sensitive-field controlspackages/integrations— integration helpers for Strava and Telegramscripts/create-deferred-issues.ts— GitHub issue generator for deferred MVP items
This repo now supports both:
- Direct host / cloud deployment — run the web app and worker as normal Node processes against any Postgres instance.
- Self-hosted local stack — run Postgres, migrations, the web app, and the worker together with Docker Compose.
The application code path is the same in both modes. The difference is only how the processes are started and where DATABASE_URL points.
Copy .env.example to .env and fill in the values you plan to use:
cp .env.example .envImportant values:
DATABASE_URLNEXT_PUBLIC_APP_URLSTRAVA_CLIENT_IDSTRAVA_CLIENT_SECRETTELEGRAM_BOT_TOKENTELEGRAM_CHAT_IDTELEGRAM_WEBHOOK_SECRETMODEL_PROVIDER_API_KEYMODEL_PROVIDER_BASE_URL(optional, for OpenAI-compatible providers)MODEL_PROVIDER_MODEL(optional, to override the default structured-output model)CHECKIN_INTERVAL_HOURS(optional, for the worker cadence)
Optional self-host overrides used by docker-compose.yml:
SELF_HOST_APP_URLSELF_HOST_APP_PORTSELF_HOST_POSTGRES_DBSELF_HOST_POSTGRES_USERSELF_HOST_POSTGRES_PASSWORDSELF_HOST_POSTGRES_PORT
npm install
npm run dev:web
npm run dev:worker
npm run db:generate
npm run db:migrate
npm run checkSelf-hosted stack commands:
npm run selfhost:up
npm run selfhost:down
npm run selfhost:logs
npm run selfhost:migrateThose commands use a small wrapper script that works with either docker compose or docker-compose.
- Copy
.env.exampleto.env. - Fill in the Strava, Telegram, encryption, and model-provider secrets.
- Install Docker plus Docker Compose (
docker composeplugin ordocker-composebinary). - Run
npm run selfhost:up. - Open the dashboard at
SELF_HOST_APP_URL(defaults tohttp://localhost:3000).
Notes:
- Docker Compose starts Postgres, runs migrations, then starts the web app and worker.
- The Compose stack injects its own internal
DATABASE_URL, so your host-machineDATABASE_URLcan still point somewhere else for non-Docker workflows. - If you want Strava and Telegram webhooks to hit your self-hosted app,
SELF_HOST_APP_URLmust be reachable from the public internet. For a laptop/local setup, that usually means using a tunnel such as ngrok or Cloudflare Tunnel. - For a LAN or VPS deployment, point
SELF_HOST_APP_URLat that reachable hostname instead of localhost.
If you want to run without Docker Compose:
- Provision Postgres anywhere you want.
- Set
DATABASE_URLto that database. - Run
npm run db:migrate. - Start the web app with
npm run start --workspace @coachinclaw/web -- --hostname 0.0.0.0 --port 3000. - Start the worker with
npm run start --workspace @coachinclaw/worker.
This path works for a VPS, a home server, Railway/Render/Fly-style deployments, or any other Node-friendly host.
To create GitHub Issues for the deferred roadmap:
npm run issues:deferred -- jessephus/CoachinClaw- Encrypt the database, object storage, and backups at rest.
- Use application-layer encryption for the most sensitive fields such as tokens, injury notes, and message bodies.
- Keep WhatsApp/Telegram messages concise and avoid sending detailed injury context there.
- Minimize and pseudonymize model prompts before sending them to frontier-model providers.
- Do not expose your
.envfile, bot tokens, or encryption key inside container images or public repos.
The repo includes a code-backed governance layer in packages/coach-core/src/governance.ts and apps/web/src/lib/governance.ts:
- Retention policies — per-data-class retention schedules with cutoff calculation and prunable-table mappings.
- Data export — full athlete data export (decrypted JSON) with optional re-encryption for secure transfer. Tokens are intentionally excluded from exports.
- Data deletion — scoped deletion (
full,credentials-only,messages-only,training-only,memories-only) with audit trail. - Retention pruning — runnable pruning hook that deletes rows older than their retention cutoff across all prunable tables.
- Audit coverage registry — tracks which auditable actions are implemented and surfaces coverage gaps.
- Prompt privacy review — pattern-based scanner that flags tokens, PII, medical terms, and verbose injury descriptions before they reach model prompts.
| Route | Method | Purpose |
|---|---|---|
/api/governance/status |
GET | Governance posture summary |
/api/governance/export |
POST | Export athlete data |
/api/governance/delete |
POST | Delete athlete data (scoped) |
/api/governance/prune |
POST | Execute retention-based pruning |
/api/governance/audit-summary |
GET | Audit event summary for an athlete |