Redbeanco Detailing

Project Status Dashboard

Implementation Phases

Timeline: 8+ weeks (comprehensive) Start: Phase 0 begins immediately

Phase status (concise)

PhasePlanStatus
0 Foundation + accountsWeek 1🟡 Partial — repo/dev/schema/prices done; Forgejo remote + some accounts missing
0.5 Garage (k3s)Week 1⬜ Not started (cross-repo nixos-redbeanco)
1 Zola frontend + i18nWeek 2✅ Done (minus Lighthouse verify, deploy)
2 D1 + Workers coreWeek 3✅ Done
3 Stripe integrationWeek 4✅ Done (minus prod webhook registration)
4 NotificationsWeek 5🟡 Partial — core done; delivery-status/fallback/inbound pending
5 Staff PWAWeek 5–6🟡 Partial — wiring done; CF Access + ntfy push pending
6 Surcharge/refund cronWeek 6🟡 Partial — core done; ntfy alert + dead-letter pending
7 Rust async serviceWeek 7🟡 Partial — code done; cf_metrics stub; deploy pending
8 Monitoring & alertsWeek 7–8🟡 Partial — dashboards done; alert rules pending
9 Security hardeningWeek 8🟡 Partial — headers done; rate-limit/CSP/JWT/CI-audit pending
10 Testing & QAWeek 9🟡 Partial — unit done; E2E/integration/load-run/security pending
11 Launch prepWeek 10⬜ Not started
12 Post-launchOngoing⬜ Not started

Legend: ✅ Done · 🟡 Partial · ⬜ Not started.

Phase 0: Foundation & Account Setup (Week 1)

Accounts to Create

  • Cloudflare account
  • Register domain via Cloudflare Registrar (e.g., redbeanco.com.au or dedicated domain)
  • Stripe account (Australia) — start with test mode, get production keys after ABN verification
  • ClickSend account — register Sender ID with ACMA
  • Resend account — verify domain for email sending
  • Forgejo repo: redbeanco-detailing (on existing Forgejo instance)

Software Repo Setup

  • git init in ~/Projects/redbeanco-detailing/
  • flake.nix with flake-parts, crane, fenix, worker-build inputs
  • flake-modules/packages.nix — Zola build, Worker WASM builds (via worker-build), Rust service (via crane)
  • flake-modules/dev.nix — devShell with rust, wrangler, zola, cargo-watch
  • flake-modules/ci.nix — om ci config (build + test + lint + audit)
  • .forgejo/workflows/ci.yaml — build + test + lint + audit
  • .forgejo/workflows/deploy-cf.yaml — deploy to CF Pages + Workers
  • .forgejo/workflows/build-image.yaml — build + push Rust service container image
  • prices.nix — vehicle × service pricing matrix (with bilingual labels)
  • schema/001_initial.sql — D1 bookings table with idempotency constraints
  • docs/architecture.md — document all decisions (already done)

Infrastructure Repo

  • Add redbeanco-detailing as flake input in nixos-redbeanco/flake.nix
  • Create cluster/applications/detailing-service.nix (empty placeholder)
  • Create cluster/applications/garage.nix (empty placeholder)

Exit Criteria

  • nix develop works (devShell functional)
  • nix flake check passes
  • CI pipeline runs (build + test passes, even if no code yet)
  • All external accounts created and accessible

Phase 0.5: Deploy Garage (Week 1)

  • npins add container dxflrs/garage --tag v2.2.0 in nixos-redbeanco
  • Write cluster/applications/garage.nix (following minio.nix pattern)
  • Add import to cluster/env/prod.nix
  • Create OpenBao secret: kvv2/redbeanco/garage
  • nixidy build .#prod → commit → push → ArgoCD deploys
  • Initialize Garage: garage status, garage layout assign
  • Create bucket: garage bucket create detailing-backup
  • Create access key: garage key create detailing-service
  • Configure S3 lifecycle: 30-day expiry on detailing-backup bucket
  • Verify S3 access: aws s3 ls --endpoint http://100.64.0.3:30060

Exit Criteria

  • Garage pod running on k3s
  • detailing-backup bucket created
  • S3 lifecycle configured
  • Access key stored in OpenBao

Phase 1: Zola Frontend + i18n (Week 2)

  • Zola config.toml: default_language = "en", [languages.zh] with translations
  • Content files: _index.md + _index.zh.md, faq.md + faq.zh.md, privacy.md + privacy.zh.md, terms.md + terms.zh.md
  • prices.nix → Zola template injection (Nix generates a config file consumed by Zola's load_data)
  • Booking form: vehicle type + service type selectors (prices update live)
  • Mobile-first responsive SASS (no framework, <500KB page weight)
  • Accessibility: WCAG 2.1 AA (form labels, ARIA, contrast, keyboard nav)
  • Staff PWA pages: dashboard, detail, surcharge, complete, photo upload
  • PWA manifest + service worker (offline cache for staff pages)
  • QR code SVG (static, print-ready, error correction level H)
  • CI: zola buildwrangler pages deploy
  • CF Pages project setup (dev + prod environments)

Exit Criteria

  • Zola site builds and deploys to CF Pages (builds ✅, deploy ⬜)
  • Booking form renders with live price calculation
  • Both EN and ZH pages render correctly
  • Staff PWA pages render (no backend yet, mock data)
  • Lighthouse score > 90 (performance, accessibility, best practices)

Phase 2: D1 + Workers Core (Week 3)

  • wrangler d1 create redbeanco-detailing (prod + dev databases)
  • Migration pipeline: wrangler d1 migrations apply
  • Apply schema/001_initial.sql to both databases
  • Booking Worker (Rust → WASM):
    • Cargo.toml: worker = { features = ["d1"] }
    • #[event(fetch)] Router with POST /api/booking
    • Input validation with validator crate (serde + validator derive)
    • D1 prepared statements (d1.prepare("...").bind(&[...]))
    • Read prices from build-time injected module (generated from prices.nix)
    • Create Stripe Checkout Session via reqwest to Stripe API
    • Return Checkout URL
  • Photo Worker:
    • Cargo.toml: worker = { features = ["r2"] }
    • POST /api/photos/presign (staff only, CF Access)
    • R2 presigned PUT URL generation
  • Security headers middleware (CSP, HSTS, X-Frame-Options, etc.)
  • wrangler dev local development with D1 local
  • Unit tests: cargo test for validation + pricing logic
  • CI: wrangler deploy for Workers

Exit Criteria

  • Booking Worker creates Stripe Checkout Sessions (test mode)
  • D1 writes work (booking records created)
  • Photo Worker generates presigned R2 URLs
  • All D1 queries use prepared statements
  • Unit tests pass in CI (tests pass, CI not running)

Phase 3: Stripe Integration (Week 4)

  • Stripe Checkout Session creation (line items from prices.nix)
  • Apple/Google Pay support (automatic with Checkout)
  • GST-inclusive pricing in Stripe line items
  • Webhook Worker: #[event(fetch)] — verify Stripe signature → enqueue to CF Queues
    • Cargo.toml: worker = { features = ["queue"] }
    • HMAC-SHA256 signature verification
    • Enqueue to CF Queues
    • Return 200 to Stripe immediately
  • Queue Consumer: #[event(queue)] — dequeue → idempotent D1 write → send confirmation
    • stripe_event_id unique constraint for idempotency
    • Write booking record: status = 'confirmed'
    • Write audit_log entry
  • Stripe test mode for dev/CI
  • Restricted Stripe keys: Checkout Sessions:write, Refunds:write, Charges:read
  • Webhook endpoint registration in Stripe dashboard
  • Integration tests with Stripe test mode

Exit Criteria

  • Full payment flow works in dev (form → Stripe test payment → D1 record created)
  • Webhook signature verification works
  • Idempotency verified (duplicate webhooks don't create duplicate records)
  • Audit log entries created

Phase 4: Notifications (Week 5)

  • ClickSend integration: SMS booking confirmation
  • ClickSend integration: MMS photo delivery (after service completion)
  • ClickSend integration: SMS surcharge payment links
  • Resend integration: email booking confirmation
  • Bilingual templates (EN/ZH based on booking language preference)
  • Template system: parameterized, no injection risk
  • ACMA Sender ID registration verification
  • Delivery status tracking (webhook from ClickSend/Resend → D1 audit log)
  • Inbound SMS support (customer support replies)
  • Fallback logic: ClickSend fails → Resend; Resend fails → ClickSend

Exit Criteria

  • SMS confirmation sent after payment (bilingual)
  • Email confirmation sent after payment (bilingual)
  • SMS delivery status tracked in D1
  • Fallback logic tested (simulate provider failure)

Phase 5: Staff PWA (Week 5-6)

  • Staff dashboard: booking list with status filters (pending, confirmed, surcharge_pending, completed, cancelled)
  • Booking detail view: customer info, vehicle, parking, flight, status timeline
  • Surcharge workflow: create Stripe Checkout → send SMS → D1 records surcharge_expires_at
  • Photo upload: presigned R2 URL → direct phone→R2 upload → store URL in D1
  • Complete workflow: update D1 → generate R2 read URLs → send MMS
  • Offline capability: service worker caches, IndexedDB action queue, background sync
  • Photo compression (canvas API before upload, max 1920x1080, JPEG 85%)
  • CF Access protection on all staff routes
  • Push notifications for new bookings (ntfy /redbeanco-detailing topic)

Exit Criteria

  • Staff can log in via CF Access
  • Staff can view and filter bookings
  • Staff can upload photos (direct to R2)
  • Staff can complete service (sends MMS)
  • PWA works offline (cached booking list, queued actions)

Phase 6: Surcharge & Refund Cron (Week 6)

  • Cron Worker: #[event(scheduled)] with crons = ["*/5 * * * *"]
    • Cargo.toml: worker = { features = ["d1"] }
    • Query D1 for expired pending surcharges
    • Call Stripe Refunds API (partial refund retaining $60 call-out fee)
    • Update D1: surcharge_pendingcancelled
    • Customer notification (SMS: refund processed, $60 retained)
    • Alert on refund failures (ntfy urgent)
    • Dead letter handling for failed refunds
  • Verify: this runs on CF Cron ONLY (not self-hosted Rust service)
  • Test: create surcharge, fast-forward expiry, verify cron processes refund

Exit Criteria

  • Cron executes every 5 minutes
  • Expired surcharges are refunded correctly (payment - $60 call-out fee)
  • Customer receives SMS notification of refund
  • Failed refunds trigger ntfy urgent alert
  • Alert fires if cron hasn't run in 10 min

Phase 7: Rust Async Service (Week 7)

  • Cargo.toml: axum, aws-sdk-s3 (Garage compatible), reqwest, prometheus, tracing, tokio
  • D1 mirror: poll D1 REST API → write JSON to Garage S3 bucket
  • R2 mirror: list R2 objects → copy to Garage S3
  • Prometheus metrics: booking count, revenue, status distribution, mirror lag, sync errors
  • Config from env vars (12-factor #3): D1_API_URL, D1_API_TOKEN, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, etc.
  • Health probes: /health (k8s readiness), /metrics (Prometheus scrape)
  • Graceful shutdown on SIGTERM
  • Crane build in flake → container image → push to Forgejo container registry
  • npins add container forgejo.redbeanco.org/redbeanco/detailing-service in nixos-redbeanco
  • Write cluster/applications/detailing-service.nix — nixidy module
  • ExternalSecret for OpenBao secrets
  • ArgoCD deployment via existing GitOps pipeline
  • Daily business summary (ntfy at 8am AEST)

Exit Criteria

  • Rust service running on k3s
  • D1 data mirrored to Garage S3 every 15 min
  • R2 objects mirrored to Garage S3
  • Prometheus scraping /metrics (visible in Grafana)
  • Daily summary arrives via ntfy

Phase 8: Monitoring & Alerts (Week 7-8)

  • Add Prometheus scrape target for detailing-service (extend monitoring.nix)
  • Add Garage scrape target
  • Alert rules (in redbeanco-detailing/monitoring/alerts/, imported into monitoring configMap):
    • DetailingServiceDown, GarageS3Down, MirrorLagHigh, RefundCronFailure
    • DetailingSyncErrors, DetailingSurchargeStuck, DetailingHighMemory
  • Grafana dashboards: booking overview, payment metrics, service health
  • ntfy routing: critical → urgent, warnings → high
  • CF Workers structured logging (JSON)
  • Rust tracing with JSON formatter → Promtail → Loki
  • Uptime monitoring (UptimeRobot free tier: customer site + Worker health)
  • Configure CF Dashboard alerts (Worker errors, D1 latency, Queue depth, Cron failures)

Exit Criteria

  • All alerts fire correctly (tested by stopping services)
  • Grafana dashboards show real data
  • ntfy notifications arrive on phone
  • Uptime monitoring active

Phase 9: Security Hardening (Week 8)

  • OWASP Top 10 checklist review against implementation (see security.md)
  • CF Workers security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
  • Rate limiting: CF rules on /api/booking (10/min/IP), /api/photos/presign (30/min/user)
  • Input validation audit (every field, every endpoint)
  • D1 query audit (all parameterized)
  • R2 bucket policy: public read for photos only, private write, 15-min presigned URL expiry
  • Booking ID: nanoid (21 chars, unguessable) — verify no sequential IDs anywhere
  • Stripe key permission verification (restricted keys, not secret keys)
  • cargo audit in CI (blocking on fail)
  • cargo clippy -- -D warnings in CI (blocking)
  • OWASP ZAP automated scan in CI
  • gitleaks secret scanning in CI
  • Manual penetration testing checklist

Exit Criteria

  • OWASP Top 10 checklist all green
  • cargo audit clean
  • cargo clippy clean
  • OWASP ZAP scan passes
  • No secrets in git (gitleaks clean)

Phase 10: Testing & QA (Week 9)

  • Unit tests: cargo test (validation, pricing, refund math, template rendering) — aim for >80% coverage
  • Integration tests: wrangler dev + D1 local + Stripe test mode
  • E2E tests: Playwright (full booking flow EN + ZH, staff workflow, surcharge, cancellation)
  • Load tests: k6 (100 concurrent bookings, 500/min form submissions) — scripts written, not run
  • Security tests: OWASP ZAP, cargo audit, manual pen test
  • Smoke tests: post-deploy health check script
  • Test data: Stripe test cards, fake D1 data, ClickSend test numbers
  • Test both EN and ZH flows end-to-end

Exit Criteria

  • All unit tests pass
  • All integration tests pass
  • All E2E tests pass (EN + ZH)
  • Load test: p95 < 500ms, 0 errors
  • Security tests pass

Phase 11: Launch Preparation (Week 10)

DNS & Domain

  • Domain registered via CF Registrar
  • DNS: A/AAAA record for customer-facing domain → CF Pages
  • DKIM/SPF records for Resend email deliverability
  • DKIM/SPF records for ClickSend SMS (if applicable)

Production Secrets

  • Stripe production keys (restricted permissions)
  • Cloudflare Worker secrets (via wrangler secret put --env production)
  • OpenBao secrets seeded (kvv2/redbeanco/detailing/*)
  • ExternalSecret applied to k3s

Production Backing Services

  • D1 production database created + migrations applied
  • R2 production bucket created (lifecycle rule pending)
  • CF Queues production
  • CF Cron Trigger production (5-min schedule)
  • Garage detailing-backup bucket + lifecycle configured

Deployments

  • CF Pages production deployment
  • CF Workers production deployment
  • Rust service production deployment (via ArgoCD)

Physical

  • QR code printed (weatherproof material, 10cm x 10cm)
  • QR code placed at airport (multiple locations)

Documentation & Training

  • Runbook finalized
  • Staff training (PWA walkthrough, photo capture, surcharge flow)
  • Legal: Privacy Policy + ToS published on site (EN + ZH)
  • Data consent checkbox verified on booking form

Verification

  • Backup verification: Garage mirror working, Restic backup
  • Rollback plan tested (revert a deploy, verify it works)
  • End-to-end test on production (test booking with real payment, then refund)
  • Monitoring verified (all alerts would fire if something breaks)

Exit Criteria

  • Production environment fully operational
  • One test booking completed end-to-end (including photos + MMS)
  • All monitoring active
  • QR code in place at airport

Phase 12: Post-Launch (Ongoing)

First Week (Intensive Monitoring)

  • Monitor metrics and alerts daily (Grafana + ntfy)
  • Verify daily business summaries arrive
  • Watch for any error patterns in CF Workers logs
  • Collect customer feedback (SMS survey link post-service)
  • Verify refund cron processes correctly (if any surcharges expire)

Ongoing

  • Customer feedback collection (SMS survey post-service)
  • Iterative improvements based on real usage data
  • Feature flagging (D1-backed, for gradual rollouts)
  • Regular dependency updates:
    • Renovate for flake.lock (weekly)
    • cargo update (monthly)
    • npins for container images (weekly scheduled)
  • Quarterly security review (OWASP checklist, dependency audit, pen test)
  • BAS/tax reporting automation (Rust service /reports/bas endpoint)
  • Monthly backup verification (restore drill)
  • Quarterly Garage disk space review