Timing data extraction, YouTube chapter generation, and lap timer overlay rendering for race footage — packaged as a desktop app.
graph TB
subgraph Apps
Desktop["apps/desktop<br/><small>Electron app</small>"]
Renderer["apps/renderer<br/><small>Remotion compositions</small>"]
end
subgraph Packages
Engine["packages/engine<br/><small>Orchestration layer</small>"]
Core["packages/core<br/><small>Domain types</small>"]
Scraper["packages/scraper<br/><small>Web scraping</small>"]
Timestamps["packages/timestamps<br/><small>Timestamp calculation</small>"]
Compositor["packages/compositor<br/><small>Remotion bundler + renderer</small>"]
end
Desktop --> Engine
Desktop --> Renderer
Renderer --> Timestamps
Renderer --> Core
Engine --> Scraper
Engine --> Timestamps
Engine --> Compositor
Engine --> Core
Scraper --> Core
Timestamps --> Core
Compositor --> Core
subgraph Cloud["Cloud"]
API["apps/api<br/><small>Fastify REST API</small>"]
Admin["apps/web-admin<br/><small>Next.js dashboard</small>"]
DB["packages/db<br/><small>Drizzle ORM schema</small>"]
Infra["infra/<br/><small>AWS CDK stacks</small>"]
end
Desktop -.->|"authenticated fetch"| API
API --> DB
Admin --> DB
Infra --> API
subgraph AWS["AWS Services"]
S3["S3<br/><small>Uploads + renders</small>"]
StepFn["Step Functions<br/><small>Render pipeline</small>"]
Lambda["Lambda<br/><small>Pipeline tasks</small>"]
SQS["SQS<br/><small>Social upload queue</small>"]
SES["SES<br/><small>Email notifications</small>"]
CloudFront["CloudFront<br/><small>Signed URL downloads</small>"]
MediaConvert["MediaConvert<br/><small>Video processing</small>"]
end
API -.-> S3
API -.-> StepFn
StepFn -.-> Lambda
Lambda -.-> S3
Lambda -.-> SQS
Lambda -.-> SES
Lambda -.-> MediaConvert
CloudFront -.-> S3
| Package | Description | Docs |
|---|---|---|
@racedash/core |
Domain types and constants — no runtime dependencies | README |
@racedash/scraper |
Fetches and parses timing data from AlphaTiming | README |
@racedash/timestamps |
Offset parsing, lap timestamp calculation, and YouTube chapter formatting | README |
@racedash/compositor |
Remotion bundler/renderer abstraction, GPU detection, and FFmpeg codec validation | README |
@racedash/engine |
Orchestration layer — composes scraper, timestamps, and compositor | README |
@racedash/db |
Drizzle ORM schema — PostgreSQL tables for users, licenses, credits, jobs | README |
| App | Description | Docs |
|---|---|---|
@racedash/desktop |
Electron app with project library, creation wizard, editor, and video preview | README |
@racedash/renderer |
Remotion compositions for overlay styles (banner, esports, geometric-banner, minimal, modern) | README |
@racedash/api |
Fastify REST API — deployed as AWS Lambda | README |
@racedash/web-admin |
Next.js admin dashboard with Clerk auth | README |
| Component | Description | Docs |
|---|---|---|
@racedash/infra |
AWS CDK stacks, Lambda functions, Step Functions pipeline, LocalStack testing | README |
| Tool | Install | Verify |
|---|---|---|
| Node.js v20+ | nodejs.org (LTS) | node --version |
| pnpm | npm install -g pnpm |
pnpm --version |
| FFmpeg | macOS: brew install ffmpeg · Windows: winget install ffmpeg · Linux: sudo apt install ffmpeg |
ffmpeg -version |
Windows support for
racedash renderis experimental. The Windows render path uses a transparent VP9 WebM overlay internally, so ProRes support is not required.
git clone https://github.com/your-org/racedash.git
cd racedash
pnpm installpnpm desktop:devpnpm turbo build # Build all packages
pnpm turbo test # Run tests across all packages
pnpm turbo typecheck # Type-check everything
pnpm lint # LintThe API runs on your host (not in Docker) with hot-reload. Docker provides the infrastructure it depends on: PostgreSQL and LocalStack (emulated AWS services).
| Service | Port |
|---|---|
| API | 3000 |
| Admin dashboard | 3001 |
| PostgreSQL | 5433 |
| LocalStack | 4566 |
1. Start infrastructure
pnpm local:up # Start Postgres + LocalStack (waits for readiness)This creates S3 buckets, SQS queues, SES identity, and the Step Functions state machine automatically.
2. Configure environment
pnpm setup:envAn interactive script that generates apps/api/.env.local. LocalStack vars are auto-populated from infra/localstack-init/env.localstack. You'll be prompted for:
- DATABASE_URL — Press Enter to accept the default (
postgresql://racedash:racedash_local@localhost:5433/racedash_local) - CLERK_SECRET_KEY — From dashboard.clerk.com → API Keys (starts with
sk_test_) - CLERK_WEBHOOK_SECRET — Optional. Needed for user sync. Requires ngrok (see Webhooks below)
- STRIPE_SECRET_KEY — From Stripe dashboard → Developers → API Keys (starts with
sk_test_) - STRIPE_WEBHOOK_SECRET — Optional. Use
stripe listenCLI (see Webhooks below) - STRIPE_PRICE_* — From Stripe dashboard → Products → price IDs (starts with
price_) - ADMIN_APP_ORIGIN — URL of the admin app for CORS. Default
http://localhost:3001
3. Push the database schema
DATABASE_URL="postgresql://racedash:racedash_local@localhost:5433/racedash_local" \
pnpm drizzle-kit push --force4. Start the API
cd apps/api && pnpm dev # Runs on localhost:3000 with hot-reload5. Point the desktop app at the local API
cp apps/desktop/.env.example apps/desktop/.env
# Set VITE_API_URL=http://localhost:3000
# Set VITE_CLERK_PUBLISHABLE_KEY=pk_test_...The API receives webhooks from Clerk (user sync) and Stripe (payments). For local development, you need tunnels to forward these to your host.
Clerk webhooks (ngrok):
ngrok http 3000Copy the https://xxx.ngrok-free.app URL, then in dashboard.clerk.com → Webhooks → Add Endpoint:
- URL:
https://xxx.ngrok-free.app/api/webhooks/clerk - Events:
user.created - Copy the signing secret → set as
CLERK_WEBHOOK_SECRETin.env.local
Stripe webhooks (Stripe CLI):
stripe listen --forward-to localhost:3000/api/webhooks/stripeThe CLI prints the webhook signing secret directly — set it as STRIPE_WEBHOOK_SECRET in .env.local.
pnpm local:up # Start Postgres + LocalStack
pnpm local:down # Stop everything
pnpm local:fresh # Wipe volumes and restart clean
pnpm local:logs # Tail all container logs
pnpm local:logs:localstack # Tail LocalStack logs only
pnpm local:logs:postgres # Tail Postgres logs only
pnpm local:version-check # Warn if pinned LocalStack is outdated
pnpm local:ses # Inspect sent emails in LocalStack
pnpm local:sfn:list # List Step Functions executions
pnpm local:sfn:execute # Start/check SFN executions
pnpm setup:env # Interactive .env.local generatorcd infra
pnpm localstack:up # Start standalone LocalStack
pnpm test:local # Run integration tests
pnpm test:local:watch # Watch mode
pnpm localstack:down # Stop container| Style | Description |
|---|---|
| banner | Full-width top bar with accent band and central lap timer. Practice/qualifying: last-lap and session-best panels flanking the timer. Race: position counter and lap counter only. Timer background flashes purple/green/red on lap completion. Configurable accent, text, and timer colours. |
| esports | Floating card (default: bottom-left) with gradient accent bar, position badge, and lap counter. Two icon-badged time panels (last lap, session best) above a current elapsed time bar. |
| geometric-banner | Full-width top bar with five coloured SVG polygon sections. Each section holds a data point (position, last lap, timer, previous lap, lap count). In race mode, collapses to three sections. Timer section flashes with lap performance colours. |
| minimal | Compact floating card (default: bottom-left) with a lap number badge, large italic elapsed time, and three stat columns (position, last lap, session best). Same layout across all modes. |
| modern | Horizontal bar (default: bottom-centre) with a subtle diagonal stripe pattern. Large elapsed time on the left, position/last lap/session best stats on the right separated by a thin divider. |