Hearthline — Technical Specification
Constraint file: `../JAMAICA_TRUST.md` Launch geography: Jamaica (island household) + diaspora viewers (US/UK/Canada). Stack: Progressive Web App (Next.js), on-device/private extraction, WhatsApp share, no payment required for the radar.
System architecture
[Android/iOS/Desktop browser]
| HTTPS, service worker |
|---|
v [Hearthline PWA] - Camera capture (getUserMedia) - Local draft store (IndexedDB, encrypted at rest) - Last-radar cache (Cache Storage)
+---> [Extract worker] vision+layout model
| on-device when possible; |
|---|
| else private-cloud in a Jamaica-or-CARICOM-preferring region |
+---> [API: households, intervals, share-links]
+---> [WhatsApp] user-initiated share of PNG/PDF only
X---> JPS / bank / remittance APIs (forbidden) X---> Helper marketplace checkout (forbidden)
Trust boundary: the island household is the data controller. Diaspora “Circle” is read-only unless the household grants a named action (e.g. “mark pharmacy done”). Remittance never enters this system.
Core APIs (v1)
All routes are versioned /v1. Auth is WebAuthn or a WhatsApp-otp-to-phone on the island number, not email-and-password theatre.
| Method | Path | Purpose |
|---|---|---|
POST | /households | Create household (display name + parish). No card. |
POST | /evidence | Upload compressed image (multipart). Server stores only after client-side redaction option (cover address). |
POST | /evidence/{id}/extract | Return draft Interval[]. Never auto-commit. |
PUT | /intervals/{id} | Confirm, correct, assign owner, mark done/skipped/unsure. |
GET | /radar?hours=4 | Confidence-weighted intervals for the polar UI. |
POST | /shares | Create a time-boxed view link (default 72h, revocable). Returns a PNG *and* a URL. |
POST | /shares/{id}/revoke | Kill the link. |
GET | /receipts | Plain-language log (Data Protection Act access). |
POST | /export | ZIP of photos + intervals (household portable record). |
DELETE | /households/{id} | Tombstone + purge within 30 days. |
Explicitly absent: /payments, /helpers/book, /portal/login, /lynk.
Data models
type Parish =
| "Kingston" | "St. Andrew" | "St. Thomas" | "Portland" | "St. Mary" |
|---|---|---|---|---|
| "St. Ann" | "Trelawny" | "St. James" | "Hanover" | "Westmoreland" |
| "St. Elizabeth" | "Manchester" | "Clarendon" | "St. Catherine"; |
interface Household { id: string; name: string; parish: Parish; controllerPhoneE164: string; // +1876... createdAt: string; }
interface Evidence { id: string; householdId: string; kind: "discharge" | "script" | "label" | "note" | "other"; blobRef: string; capturedAt: string; capturedOffline: boolean; }
interface Interval { id: string; householdId: string; action: string; windowStart: string; windowEnd: string; confidence: number; // 0..1, first-class blastRadius: "low" | "high"; ownerMemberId: string | null; status: "draft" | "confirmed" | "done" | "skipped" | "unsure"; evidenceIds: string[]; }
interface ShareLink { id: string; householdId: string; expiresAt: string; canMarkDone: boolean; revokedAt: string | null; }
No TRN stored in v1. Optional parish badge is a later table, never a login requirement.
Security framework
- TLS 1.3 only. HSTS.
- Encryption at rest (AES-256) for evidence blobs; envelope keys per household.
- No vendor training on household photos. Contractual + technical (separate inference account).
- Data Protection Act, 2020: privacy notice in plain English; lawful basis = household consent; access, correction, deletion, portability in-product.
- Share links are capability tokens, not guessable IDs; rate-limited; watermarked PNG (“for family WhatsApp — not a payment page”).
- Threat we design for: a fake Hearthline site that asks for Lynk. Our real origin is pinned in the 876 voicemail and on the partner-desk card.
- HIPAA is not Jamaica law; we still treat health photos as special category data.
- CSP, no third-party ad pixels, no Facebook SDK.
PWA specification (universal + Jamaica)
| Item | Spec |
|---|---|
| Manifest | name: Hearthline, display: standalone, start_url: /radar, orientation: any |
| Icons | 192 / 512 maskable; no “download APK” link anywhere |
| Service worker | Precache shell; cache last radar + last 3 evidence thumbs |
| Background sync | Queue evidence uploads when Digicel/FLOW returns |
| Camera | capture=environment on file input as fallback if getUserMedia blocked |
| Storage | Budget for low-end Android; evict oldest thumbs first |
| A11y | 48px targets, 1.4 line-height, works at 200% zoom |
| Support chrome | Persistent footer: +1 876 … · parish desk hours |
Scalability plan
- Year 0: single region, 10k households, object storage + Postgres.
- Extract is async; a household of 6 does not need a GPU farm.
- Diaspora viewers are cheap (read replicas + CDN for share PNGs).
- If a public hospital ever offers FHIR, it is an optional connector behind the same confirm step — never the launch path.
What we will not integrate at launch
MyChart, Stripe-only checkout, crypto wallets, helper gig marketplaces, remittance APIs that would let us “collect for Mummy.”