Engineering docs
dooh-platform — Design Doc (v1, 2026-08-27)
Last updated Aug 27, 2026 · 6 sections
Locked premises
- P1 One mall first. Everything is single-operator, multi-venue-ready but not multi-tenant SaaS.
- P2 Web before mobile. The advertiser portal is responsive web; a Flutter app is phase 3+.
- P3 Measurement is aggregates-only. No personal data anywhere in the system (see CLAUDE.md rule 3).
- P4 The play ledger is the invoice. Append-only, exportable, auditable (CLAUDE.md rule 1).
- P5 Simulator before hardware. The full cloud + portal stack is built and demoed against simulated screens; real RK3588 players replace them without any API change.
- P6 No payment rails in v1. Bookings track payment status; money moves offline (UPI/bank) and ops marks it paid. Razorpay when demand proves out.
- P7 Loop model: each screen runs a fixed loop (default 8 slots × 10 s = 80 s). A booking buys one or more slots on one or more screens for a date range. Sold-out = all 8 slots booked that day.
- P8 Gate: before real capex, the pilot must show ≥50% loop fill at rate card on the first mall (mirrors the entry-memo month-4 gate).
System overview
Three surfaces, one backend:
- Advertiser portal (web
/): signup/login → business profile → upload creative (image/video) → automatic validation → pick screens + dates + slots → book (payment pending) → live dashboard (plays, watchers, attention, demographics, proof gallery) → CSV export. - Ops portal (web
/ops, role-gated): creative approval queue · screen & venue registry · device pairing (enter code shown on screen) · fleet health (last_seen, sync lag, uptime) · bookings/payments admin · all-campaign metrics. - Devices (simulator now, player later): pair once → poll
GET /device/schedule→ play loop → batch-upload plays + audience summaries → server writes ledger.
Device pairing flow (TV-style)
- Player boots unpaired → generates
device_uid+ secret, callsPOST /device/pairing/start→ gets 6-char code, shows it on screen. PollsPOST /device/pairing/poll. - Ops user enters code in ops portal →
POST /ops/devices/claim {code, screen_id}binds device to a screen (venue/zone/position already registered). - Poll returns a device token (opaque, hashed at rest, revocable). All later calls use
Authorization: Device <token>. - Revocation: ops portal → device gets 401 → shows pairing screen again.
Data model (core tables)
users(email, password_hash, role: advertiser|ops),advertiser_profiles(business name, GSTIN?)venues→zones→screens(loop_slots=8, slot_seconds=10, rate_per_slot_day, orientation)devices(device_uid, token_hash, screen_id nullable until claimed, last_seen_at, status)creatives(owner, kind: image|video, file, duration_s, status: pending|approved|rejected, validation json, reject_reason)campaigns(advertiser, name) →bookings(campaign, screen, slot_count, start_date, end_date, price_total, payment_status: pending|paid|waived, status: draft|confirmed|cancelled)schedule_entries(screen, date, slot_index, booking, creative) — materialized on booking confirm- Ledger:
plays(device, screen, creative, booking, started_at, duration_ms, batch_id) andaudience_summaries(play_id, ots, watchers, attention_ms_total, age band counts json, gender counts json) — both append-only. ingest_batches(device, batch_seq, received_at, state: recorded|duplicate|rejected, reason)rollups_daily(booking, screen, date, plays, ots, watchers, attention_ms, demographics json)
Metrics shown (advertiser dashboard)
Live: now playing per screen + watchers now (from latest events). Campaign: plays delivered vs booked · OTS → watchers → avg attention funnel · attention rate · age band / gender split · hour×day heatmap · per-creative comparison · per-screen breakdown · proof gallery (screenshots, phase 2) · CSV export reproducing the ledger.
Phases
- Foundation (this build): backend + advertiser portal + ops portal + simulator; money-path E2E green with simulated screens.
- Edge: RK3588 player + vision service replacing simulator; bench accuracy study.
- Pilot install: 2–3 screens in mall, ground-truth study, first advertisers.
- Later: Razorpay, Flutter app, geofenced vehicle SKU, programmatic export, landlord footfall view.