- dedicated git repo at /home/avi/Projects/Lumen (main)
- TypeScript strict (target ES2022, bundler, exactOptionalPropertyTypes, noUncheckedIndexedAccess)
- ESLint 9 + typescript-eslint strictTypeChecked + eslint-plugin-boundaries for B-1..B-7, no-restricted-globals/syntax for B-1/B-7
- Prettier 3.5
- Structure per IMPLEMENTATION-CONTRACT.md §4 (src/platform/idb|cache|sw, storage, data, sync/{transport,verifier}, domain/{emergency,schedule,map,festival,readiness,clock,favorites}, ui/{components,views,router,render}, app, emergency-baseline, assets, public, content, pipeline, tests, scripts)
- CI: .github/workflows/ci.yml (typecheck + lint + format + test)
- Boundary tests: tests/unit/boundaries.test.ts (4 tests) + scripts/check-boundaries.ts
- No feature code, no PWA/IDB/sync/mesh/accounts per contract Stage 1
842 lines
62 KiB
Markdown
842 lines
62 KiB
Markdown
# Lumen — Discovery & Requirements Analysis
|
||
|
||
- **Phase:** Discovery & Requirements Analysis ONLY (no architecture lock-in, no implementation)
|
||
- **Date:** 2026-08-30
|
||
- **Status:** Complete — ready to inform a deliberate architecture phase
|
||
- **Author:** Lead architect / senior product engineer (automated discovery)
|
||
- **Scope guard:** No application code was written during this phase. No placeholder code exists. This document is the only artifact produced.
|
||
|
||
---
|
||
|
||
## 1. Project / Environment Findings
|
||
|
||
### 1.1 Project directory
|
||
|
||
`/home/avi/Projects/Lumen/` **exists but is effectively empty.** It was created on
|
||
Aug 30 20:11 and contains exactly one file:
|
||
|
||
| File | Content | Project-relevant? |
|
||
|------|---------|-------------------|
|
||
| `.directory` | KDE folder metadata (`[Desktop Entry] Icon=folder-yellow`) | No — desktop folder-color marker, not project work. Left untouched. |
|
||
|
||
Findings:
|
||
|
||
- **No source code**, no `package.json`, no lockfiles, no config files.
|
||
- **No README, design docs, or prior architecture.**
|
||
- **No tests, CI, or build tooling.**
|
||
- Nothing to preserve, migrate, or work around. This is a greenfield start.
|
||
|
||
### 1.2 Version control
|
||
|
||
- There is **no git repository inside** `/home/avi/Projects/Lumen/`.
|
||
- The directory sits inside a parent git repository rooted at `/home/avi`
|
||
(branch `master`, **zero commits**, no remotes). Any file added to Lumen is
|
||
technically visible to that parent repo, but there is no meaningful history.
|
||
- **Open question (VCS-1):** Should Lumen initialize its own dedicated git
|
||
repository? Recommended default: yes, at the start of the architecture phase.
|
||
|
||
### 1.3 Development environment
|
||
|
||
| Item | Value |
|
||
|------|-------|
|
||
| OS | Linux (development machine) |
|
||
| CPUs | 8 |
|
||
| RAM | 15 GiB |
|
||
| Disk | 1.8 TB, ~1.4 TB free (22% used) |
|
||
| Node.js | v22.23.2 |
|
||
| npm | 10.9.8 |
|
||
| pnpm | 11.24.0 |
|
||
| bun | 1.4.0 |
|
||
| git | 2.53.0 |
|
||
|
||
Any modern JS toolchain can run locally. No environment constraints detected.
|
||
|
||
### 1.4 Owner ecosystem context (non-binding)
|
||
|
||
Sibling projects in `/home/avi/Projects/` were lightly inspected for convention
|
||
signals only:
|
||
|
||
- `Folio` — Electron desktop app, TypeScript 5.5, esbuild.
|
||
- `Hammock` — Android/AOSP Kotlin project.
|
||
- `BookRead` — Python web app.
|
||
- Others: Nostr client, chess, misc.
|
||
|
||
**No existing web/PWA conventions exist** in the owner's workspace. TypeScript
|
||
appears to be the owner's preferred language, which may inform (but must not
|
||
predetermine) later technology evaluation.
|
||
|
||
---
|
||
|
||
## 2. Existing Files & Relevant Discoveries
|
||
|
||
There is no existing Lumen work product. The only discoveries that constrain the
|
||
project are external:
|
||
|
||
1. **Browser platform reality (as of Aug 2026)** — verified via research, detailed
|
||
in §12. The most consequential facts:
|
||
- iOS Safari enforces a **7-day inactivity eviction cap** on all script-writable
|
||
storage (localStorage, IndexedDB, Cache API, **service worker registrations**)
|
||
for sites used in Safari tabs. **Installed home-screen PWAs are exempt.**
|
||
- There is **no `beforeinstallprompt` on iOS** — installation is a manual,
|
||
multi-step "Add to Home Screen" flow that the product must teach.
|
||
- **No Background Sync / Periodic Background Sync on iOS Safari.** Sync only
|
||
happens while the app is open.
|
||
- Eviction, when it happens, is **all-or-nothing per origin** — IndexedDB,
|
||
Cache API, localStorage, and SW registrations are deleted together.
|
||
- Web Push on iOS requires iOS 16.4+, a home-screen-installed PWA, and is
|
||
region-dependent (EU behavior differs). Android Chrome push is full-featured.
|
||
- Safari 17+ storage quotas are generous on paper (~60% of disk per origin) but
|
||
are **best-effort**; `navigator.storage.persist()` is granted by heuristic
|
||
(home-screen install helps). Practical guidance from the field still recommends
|
||
keeping precached bundles lean (tens of MB, not hundreds).
|
||
2. **No existing festival data source exists.** The authoritative data pipeline
|
||
(who authors schedule/map/emergency content, in what format, hosted where)
|
||
must be designed from scratch or supplied by the festival organization.
|
||
3. **HTTPS is mandatory** for service workers, geolocation, and the Storage API.
|
||
Hosting must be HTTPS-capable (this does not predetermine a host).
|
||
|
||
---
|
||
|
||
## 3. Product Understanding
|
||
|
||
**Lumen** is a mobile-first **Progressive Web App** — explicitly *not* a native
|
||
app — serving as an offline-first festival companion for **~300–500 attendees**.
|
||
|
||
Core principle: **"Everything attendees need at the festival must work with zero
|
||
internet connectivity."** The festival environment is hostile to connectivity
|
||
(no cell/Wi-Fi, overloaded networks, airplane mode) and hostile to usability
|
||
(bright sun, darkness, noise, stress, low battery, movement).
|
||
|
||
The product is organized around four primary destinations, in priority order:
|
||
|
||
1. **EMERGENCY** — highest priority; must work with absolutely zero connectivity;
|
||
must remain reachable from everywhere in the app.
|
||
2. **SCHEDULE** — lineup, now/next, personal (local) favorites, filters, search.
|
||
3. **MAP** — festival-provided custom map with key locations; **not** online map
|
||
tiles; GPS never required.
|
||
4. **FESTIVAL** — general information (rules, FAQ, logistics, vendors, contacts).
|
||
|
||
Content should be treated as **versioned data** (a Festival Data Package), not
|
||
hardcoded logic. Connectivity is an **enhancement** (updates, announcements,
|
||
schedule changes) and must never be a prerequisite for critical functionality.
|
||
Future mesh networking is a **clean extension point only** — nothing mesh-related
|
||
is built or assumed in V1.
|
||
|
||
Success condition: an attendee who installs Lumen before arriving can use every
|
||
critical feature for the entire festival with their phone in airplane mode.
|
||
|
||
---
|
||
|
||
## 4. Explicit Requirements
|
||
|
||
IDs are traceable and will carry into the architecture phase.
|
||
|
||
### 4.1 Platform
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-P1 | Lumen is a mobile-first PWA; primary targets are iOS Safari and Android Chrome. |
|
||
| R-P2 | No App Store / Play Store distribution; no native code in V1. |
|
||
| R-P3 | Served over HTTPS (required for SW, geolocation, storage APIs). |
|
||
| R-P4 | Desktop support only where it comes free; never a design target. |
|
||
|
||
### 4.2 Navigation & core UX
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-N1 | Four primary destinations: EMERGENCY, SCHEDULE, MAP, FESTIVAL. |
|
||
| R-N2 | Navigation must be extremely simple and obvious. |
|
||
| R-N3 | Emergency must be easily accessible from every other part of the app. |
|
||
| R-N4 | UX optimized for one-handed use, gloves-free large touch targets, sunlight and darkness, stress, and low attention. |
|
||
|
||
### 4.3 Emergency
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-E1 | Emergency baseline must be available with **zero** connectivity, always. |
|
||
| R-E2 | Static emergency data (procedures, contacts, locations, address/coordinates) bundled locally with the app/dataset. |
|
||
| R-E3 | Dynamic emergency data (e.g., updated contacts) may update when online but must never *depend* on a server. |
|
||
| R-E4 | Must support at minimum: 911 dialing, security contact, first aid & AED locations, emergency exits, muster points, procedures (weather/fire/lost person), festival address + GPS coordinates. |
|
||
| R-E5 | Emergency dialing (`tel:`) must work from installed standalone mode. |
|
||
|
||
### 4.4 Schedule
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-S1 | Full schedule (artists, stages, events, workshops, times) available offline. |
|
||
| R-S2 | "Happening Now" and "Up Next" computed locally from local data. |
|
||
| R-S3 | Personal schedule (favorites) created and stored locally. |
|
||
| R-S4 | Filtering (stage, type) and search work offline. |
|
||
| R-S5 | Correct behavior analysis required for: device time, festival timezone, and incorrect device clocks. |
|
||
|
||
### 4.5 Map
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-M1 | Festival map works offline; core map must NOT depend on online tiles. |
|
||
| R-M2 | Festival supplies custom map artwork/geometry. |
|
||
| R-M3 | Point-of-interest locations: stages, bathrooms, food, water, vendors, first aid, AEDs, security, entrances/exits, parking, camping, VIP, muster points, other infrastructure. |
|
||
| R-M4 | Browser Geolocation may be used as an enhancement; **never required** for critical functionality. |
|
||
| R-M5 | Map representation (SVG/vector vs raster vs canvas) to be evaluated, not yet chosen. |
|
||
|
||
### 4.6 Festival info
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-F1 | General info (rules, FAQ, bring/don't-bring, parking, camping, transport, accessibility, vendors, merch, contacts, hours, venue) available offline. |
|
||
| R-F2 | Content is data-driven (part of the Festival Data Package). |
|
||
|
||
### 4.7 Offline-first architecture
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-O1 | App shell loads with no network at all (after first successful install/load). |
|
||
| R-O2 | Service worker caches shell + assets; local storage holds all datasets. |
|
||
| R-O3 | Local data is the source of truth while offline. |
|
||
| R-O4 | App startup must never require network success. |
|
||
| R-O5 | Updates apply **atomically**; a failed/interrupted update preserves the last known-good dataset. |
|
||
| R-O6 | The app can self-assess dataset completeness/consistency → honest **OFFLINE READY** state. |
|
||
| R-O7 | Must survive: browser restart, phone restart, storage pressure; must degrade gracefully when storage was evicted (detect + re-bootstrap). |
|
||
| R-O8 | Respect browser storage limits: budget total footprint, monitor via `navigator.storage.estimate()`, request persistence. |
|
||
| R-O9 | Data versioning, integrity validation, and migration path for schema changes. |
|
||
|
||
### 4.8 Festival Data Package (concept)
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-D1 | Festival content is versioned data, not hardcoded logic, wherever practical. |
|
||
| R-D2 | Package includes manifest + integrity/version metadata at minimum; exact structure TBD in architecture phase. |
|
||
| R-D3 | Define: authoritative source, publishing flow, delivery, validation, local storage, version tracking, atomic apply, failure handling, rollback. |
|
||
|
||
### 4.9 Future extensions
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-X1 | Online extras (announcements, schedule changes, weather) may exist later; never gate V1 critical features. |
|
||
| R-X2 | Architecture must expose a clean transport-abstraction seam for future mesh without contaminating core logic. |
|
||
| R-X3 | No mesh technology is chosen, implemented, or library-dependended-upon in V1. |
|
||
|
||
### 4.10 Security & reliability
|
||
|
||
| ID | Requirement |
|
||
|----|-------------|
|
||
| R-G1 | Initial threat analysis performed (§18); realistic vs theoretical threats distinguished. |
|
||
| R-G2 | Festival data integrity protected (validation of updates before activation). |
|
||
| R-G3 | No unnecessary security complexity; no secrets in the client. |
|
||
| R-R1 | Behavior defined for every failure scenario in §19. |
|
||
|
||
---
|
||
|
||
## 5. Architectural Constraints
|
||
|
||
Non-negotiable principles (from the brief, adopted verbatim in intent):
|
||
|
||
1. Critical V1 functionality works without internet.
|
||
2. Emergency baseline always locally available.
|
||
3. Startup never requires network success.
|
||
4. UI never directly depends on a remote API for critical V1 features.
|
||
5. Festival content = versioned data, not hardcoded feature logic (where practical).
|
||
6. Local data is the offline source of truth.
|
||
7. Connectivity is an enhancement, not a prerequisite.
|
||
8. No partially-applied datasets.
|
||
9. Failed update ⇒ last known-good preserved.
|
||
10. App can determine whether it holds a complete, consistent offline dataset.
|
||
11. No mesh implementation in V1.
|
||
12. Mesh concerns never leak into feature/UI logic.
|
||
13. GPS never required for critical functionality.
|
||
14. No speculative complexity in V1.
|
||
15. Prefer the simplest architecture that satisfies the requirements.
|
||
|
||
**Constraints derived during discovery (to be ratified in architecture phase):**
|
||
|
||
- **C-16 (Install-first):** Because iOS storage eviction and push both hinge on
|
||
home-screen installation, *getting users installed before the festival* is a
|
||
load-bearing product requirement, not a nice-to-have.
|
||
- **C-17 (Bootstrap honesty):** True offline capability requires at least one
|
||
successful online bootstrap (app shell + dataset) before arrival. The
|
||
"pre-festival distribution" problem is therefore a real requirement area.
|
||
- **C-18 (Eviction survival):** The app must detect "my storage was wiped" on
|
||
launch and degrade to a clear recovery state rather than a broken blank app.
|
||
- **C-19 (Foreground-only background model):** Nothing may depend on background
|
||
execution, background sync, or push wake-ups for correctness on iOS.
|
||
- **C-20 (Single origin):** All app shell + data must live on one HTTPS origin
|
||
(storage and SW scope are per-origin; third-party hosting of assets has
|
||
partitioning/quota consequences).
|
||
|
||
---
|
||
|
||
## 6. Non-Requirements (V1)
|
||
|
||
Explicitly **out** of V1 scope:
|
||
|
||
| # | Non-requirement | Notes |
|
||
|---|-----------------|-------|
|
||
| NR-1 | Mesh networking (any form) | Seam only; no protocol, discovery, routing, store-and-forward, BLE/WebRTC mesh. |
|
||
| NR-2 | Native iOS/Android apps | PWA only. |
|
||
| NR-3 | App Store / Play Store presence | Not needed; avoid TWA/wrapper complexity too. |
|
||
| NR-4 | User accounts / authentication | No sign-in in V1; favorites are device-local. |
|
||
| NR-5 | Cross-device sync of favorites | Consequence of NR-4; revisit later. |
|
||
| NR-6 | Real-time collaboration / social features | None. |
|
||
| NR-7 | GPS-dependent features | Location is enhancement-only. |
|
||
| NR-8 | Online map tile services as core map | Core map is local/custom. |
|
||
| NR-9 | In-app payments / merch checkout | None. |
|
||
| NR-10 | User-generated content | None. |
|
||
| NR-11 | Multi-language i18n | Assumed English-only for V1 (A-09). |
|
||
| NR-12 | Background sync / periodic sync | Not reliably available cross-platform (C-19). |
|
||
| NR-13 | Push notifications as a critical channel | At best an enhancement; cannot be depended on (iOS install-only, region quirks). |
|
||
| NR-14 | Admin/CMS authoring UI | Authoring flow TBD; V1 may use a simple file-based pipeline (§15). |
|
||
| NR-15 | Analytics beyond minimal, privacy-safe diagnostics | Data minimization (§18). |
|
||
|
||
---
|
||
|
||
## 7. Assumptions
|
||
|
||
Every assumption below is **explicit and reversible**. None has been silently
|
||
converted into a requirement.
|
||
|
||
| ID | Question / Assumption | Impact | Recommended Default |
|
||
|----|-----------------------|--------|---------------------|
|
||
| A-01 | Festival is a single annual/one-off event (not multi-venue, multi-year platform). | Shapes data packaging & versioning scope. | Single-event V1; package keyed by festival edition so multi-event is a later, cheap extension. |
|
||
| A-02 | Attendee count 300–500; modest dataset size (schedule ≤ ~1k items; map assets ≤ ~50 MB total). | Storage budget, update strategy, performance targets. | Design for ≤ 50 MB total local footprint; verify with real content. |
|
||
| A-03 | One language (English). | All content schemas simpler. | English only; keep content strings in data (not code) so i18n is later possible. |
|
||
| A-04 | Festival can supply: schedule data, custom map artwork + POI coordinates, emergency info, general info — in some digital form. | The entire data pipeline depends on this. | Request structured inputs (spreadsheet/JSON + map file); define intake templates in architecture phase. |
|
||
| A-05 | Users will have internet access **before** the festival (at home/town) to install & bootstrap. | If false, need on-site distribution (gate kiosk, QR posters, local LAN server). | Assume pre-arrival bootstrap; plan on-site bootstrap as contingency (open question OQ-3). |
|
||
| A-06 | Attendees use reasonably modern phones: iOS 16.4+ / Android Chrome ~last 3 years. | Determines baseline APIs (SW, IDB, Intl, standalone). | Baseline: iOS 16.4+ Safari, Android Chrome 110+; graceful degradation below that to "basic static page still readable". |
|
||
| A-07 | No user accounts; device-local personalization only. | Eliminates auth, server-side state, sync conflicts. | Confirm; if accounts ever required, they'd be additive, not foundational. |
|
||
| A-08 | Emergency phone numbers/contacts are known and provided by organizers before build. | Emergency feature content. | Require a signed-off emergency content sheet as a project gate. |
|
||
| A-09 | No offline-update delivery via local LAN/mesh in V1; updates arrive only via internet when available. | Update architecture simplicity. | Internet-only updates in V1. |
|
||
| A-10 | Festival timezone is a single known IANA zone; schedule stored as absolute instants (UTC) + rendered in festival tz. | Now/Next correctness. | UTC storage + festival IANA zone rendering; device-tz toggle optional. |
|
||
| A-11 | The app is served from a single domain the organizers control. | SW scope, storage origin, update trust. | Single dedicated origin (e.g., `app.<festival>.tld`). |
|
||
| A-12 | V1 does not need push notifications to be "critical"; announcements are pull-based when app is opened online. | Avoids iOS push fragility as a dependency. | Push = optional enhancement post-V1. |
|
||
| A-13 | Low-end Android devices are in the audience; performance budget must include them. | Rendering choice for map, list virtualization, JS budget. | Budget: interactive in < 3s on a 2021 mid-range Android over local cache; map pan/zoom ≥ 30fps. |
|
||
| A-14 | "Call 911" is US-context (911). If festival is elsewhere, number differs. | Emergency correctness. | Make the primary emergency number **data-driven**, not hardcoded (911 default). |
|
||
| A-15 | Festival map is a fixed geographic area (no need for world-scale panning). | Map tech can be bounded/simple. | Bounded custom map; no slippy-world tiles. |
|
||
| A-16 | Organizers accept that dynamic emergency updates require the user to have opened the app online at least once to receive them. | Expectation-setting. | Document this limitation in ops runbook. |
|
||
| A-17 | Development is solo/small-team; ops simplicity matters (static hosting likely sufficient). | Backend complexity budget. | Prefer static-file distribution for V1 updates; dynamic backend only if a real need appears. |
|
||
|
||
---
|
||
|
||
## 8. Ambiguities
|
||
|
||
Items where the brief is genuinely underspecified. For the five most
|
||
consequential, the four-part analysis follows the table.
|
||
|
||
| ID | Ambiguity | Four-part analysis |
|
||
|----|-----------|--------------------|
|
||
| AMB-1 | **How do users get the app + dataset before/off-site?** (QR? link in email? gate Wi-Fi? pre-fest "install party"?) | **Unclear:** distribution channel & first-run experience. **Matters:** offline-first is impossible without a successful first online bootstrap (C-17); iOS makes install a manual multi-step flow. **Default:** URL/QR campaign before the festival + printed install instructions; app shows a clear "not yet offline ready" state until dataset downloaded. **If wrong:** large fraction of attendees arrive without data → product fails its core promise; mitigations (on-site LAN bootstrap) take real work. |
|
||
| AMB-2 | **Who authors/maintains festival data, and in what format?** | **Unclear:** authoritative source of schedule/map/emergency/info. **Matters:** determines the entire publishing pipeline and integrity model (§15). **Default:** organizers fill provided templates (CSV/JSON + map asset); Lumen build/publish tooling converts to signed Festival Data Package. **If wrong:** if organizers have an existing CMS/API, an adapter is needed; schema must stay import-friendly. |
|
||
| AMB-3 | **What does the festival map actually look like / what format will organizers provide?** (illustrated PNG? SVG? GIS data?) | **Unclear:** input artifact for the map subsystem. **Matters:** drives R-M5 evaluation (SVG vs raster vs canvas) and POI coordinate scheme. **Default:** illustrated raster background + separately authored POI coordinates in map-local coordinate space; SVG overlays if vector art is supplied. **If wrong:** if only GIS/venue data exists, a conversion step is needed; if map is enormous, tiling/splitting needed. |
|
||
| AMB-4 | **Are there any server-side capabilities at all?** (Is even static hosting available? Is there internet at the festival site for organizers?) | **Unclear:** whether updates/announcements are deliverable on-site. **Matters:** shapes whether "dynamic" features are realistic at all, and whether organizer comms need their own offline tooling. **Default:** static hosting on a CDN before + during event; assume organizer connectivity is as poor as attendees'. **If wrong:** if organizers have a site LAN, a local update mirror becomes a high-value later feature (and connects to the future transport seam). |
|
||
| AMB-5 | **When exactly is "OFFLINE READY" evaluated and shown, and what's the recovery UX when not ready?** | **Unclear:** indicator semantics, granularity (per-section vs global), and remediation UI. **Matters:** this is the app's honesty contract with users. **Default:** global indicator with per-section detail; re-bootstrap path when data missing; never claim ready unless manifest, all sections, assets, and integrity checks pass. **If wrong:** users trust a false "ready" and hit empty screens in emergencies — unacceptable. |
|
||
| AMB-6 | Festival duration / daily hours (affects schedule model, multi-day UX). | Default: support multi-day schedule; confirm dates. |
|
||
| AMB-7 | Whether schedule changes mid-festival are expected (and how urgent they are). | Default: yes, via dataset updates + optional announcements; urgency channel TBD. |
|
||
| AMB-8 | Branding/visual identity availability (icons, colors, splash). | Default: simple high-contrast house style until brand arrives. |
|
||
| AMB-9 | Whether the emergency number is 911 (US) or another jurisdiction. | Default: data-driven field; confirm jurisdiction. |
|
||
| AMB-10 | Accessibility commitments beyond legal baseline (WCAG AA?). | Default: WCAG 2.1 AA as target. |
|
||
| AMB-11 | Whether "lost person" includes a child-reunification workflow (active feature vs info page). | Default V1: informational procedure only; active reunification is a large feature and out of scope. |
|
||
| AMB-12 | Data retention/lifetime — does the app matter after the festival (memories/next-year teaser) or can it be ephemeral? | Default: keep working post-festival with the last dataset; cheap and harmless. |
|
||
|
||
---
|
||
|
||
## 9. Open Questions
|
||
|
||
Items that cannot be sensibly defaulted — they need stakeholder answers:
|
||
|
||
1. **OQ-1:** What is the festival's name, location, dates, daily hours, and IANA timezone?
|
||
2. **OQ-2:** What are the exact emergency contacts (security, first aid org, medical provider), muster points, and venue address/GPS? (Required before Emergency can be finalized.)
|
||
3. **OQ-3:** What distribution channels exist to reach attendees before arrival (email list, socials, ticketing partner)? Is on-site connectivity available for a bootstrap station?
|
||
4. **OQ-4:** Who owns content updates during the festival, and on what device? (Defines authoring/publishing UX requirements.)
|
||
5. **OQ-5:** Does the festival have an existing site/brand/domain to host under, and who controls DNS/hosting?
|
||
6. **OQ-6:** What map artifacts can organizers actually produce? (Artist brief? Existing venue plan?)
|
||
7. **OQ-7:** Are there legal/liability review requirements for emergency content (who approves the wording)?
|
||
8. **OQ-8:** Is there any budget for hosting/CDN or is fully static free-tier hosting required?
|
||
9. **OQ-9:** Expected minimum OS versions in the audience (ticket-purchase analytics may reveal)?
|
||
10. **OQ-10:** Should Lumen support post-festival feedback/contact channel, or is it read-only forever?
|
||
11. **OQ-11:** Will there be multiple stages with simultaneous programming? (Now/Next conflict UX.)
|
||
12. **OQ-12:** Are quiet hours / overnight periods relevant to the schedule model?
|
||
|
||
---
|
||
|
||
## 10. Major Architectural Decisions (to be made deliberately in the next phase)
|
||
|
||
Numbered for later reference; **none are decided here.**
|
||
|
||
| # | Decision | Why it matters | Candidates / considerations |
|
||
|---|----------|----------------|------------------------------|
|
||
| AD-1 | Frontend framework & rendering model | DX, bundle size, low-end device perf, ecosystem | No framework (vanilla TS + web components) vs Svelte/Solid (compile-away) vs React/Preact vs others. Must evaluate: JS budget, offline SW integration quality, team familiarity. |
|
||
| AD-2 | Service-worker strategy & tooling | The offline mechanism itself | Hand-written SW vs Workbox vs custom; precache vs runtime-cache split; SW update/activation UX (skipWaiting? user-prompted update?). |
|
||
| AD-3 | Local storage architecture | Data layer durability & query ergonomics | IndexedDB (raw vs wrapper like idb/Dexie) vs localStorage-only (too small) vs OPFS files vs SQLite-in-WASM (e.g., cr-sqlite/wa-sqlite) — evaluate iOS reliability carefully. |
|
||
| AD-4 | Festival Data Package schema & format | Interop, validation, size | JSON documents vs MessagePack vs SQLite file vs hybrid; one file vs section files; asset manifest design. |
|
||
| AD-5 | Integrity & authenticity model | Security (§18) | SHA-256 hashes in signed manifest only vs full package signature (e.g., Ed25519 public key baked into shell); key custody process. |
|
||
| AD-6 | Update apply/rollback mechanism | R-O5, R-O6 | Dual-slot (A/B dataset) with active pointer vs staged-write-then-commit vs version-keyed stores with GC of old versions; must survive mid-update kill. |
|
||
| AD-7 | Map representation | R-M5 | SVG (crisp, styleable, DOM-accessible, pan/zoom via transforms) vs raster tiles (pre-cached, memory-heavy when large) vs Canvas (perf, worse accessibility) vs layered hybrid; plus POI overlay model and map-local coordinate system. |
|
||
| AD-8 | Time handling strategy | R-S5 | UTC instants + festival IANA zone via Intl; optional server-time offset capture for skew detection; wrong-clock UX (warn vs auto-correct). |
|
||
| AD-9 | Hosting & distribution topology | R-P3, updates | Static CDN hosting (likely sufficient) vs tiny server; versioned immutable asset URLs; cache headers strategy. |
|
||
| AD-10 | Publish pipeline | R-D3 | Build-time generator (repo of JSON/CSV → signed package) vs lightweight admin tool vs CMS adapter; who presses "publish". |
|
||
| AD-11 | Transport abstraction seam shape | R-X2 | Interface boundaries so "InternetTransport" is the only V1 implementation and future local/mesh transports slot under a messaging/sync layer without touching UI. Must be minimal — risk of over-engineering. |
|
||
| AD-12 | Announcements design (even if V1-deferred) | Pull-based inbox model vs banner; storage of stale announcements; authenticity. |
|
||
| AD-13 | Observability without surveillance | R-G data minimization | No/low telemetry; on-device diagnostics screen; optional manual "share diagnostics" rather than automatic upload. |
|
||
| AD-14 | Repo/VCS & CI setup | VCS-1 | Own git repo; CI for build + Lighthouse/PWA checks + device-matrix tests. |
|
||
|
||
---
|
||
|
||
## 11. Technology Questions Requiring Evaluation
|
||
|
||
Each needs a small spike or documented evaluation in the next phase — not a
|
||
decision now.
|
||
|
||
1. **TQ-1 Framework perf on low-end Android:** measure schedule-list rendering and
|
||
map pan/zoom with candidate stacks on a throttled mid-range device.
|
||
2. **TQ-2 IndexedDB reliability on iOS WebKit:** transaction failure history,
|
||
large-write behavior near quota, behavior after forced kill; compare against
|
||
OPFS and localStorage+files hybrid.
|
||
3. **TQ-3 SQLite-WASM viability:** does it earn its complexity for querying
|
||
(search/filter) vs plain IDB indexes? iOS Safari WASM memory limits?
|
||
4. **TQ-4 Service worker update semantics on iOS:** activation timing,
|
||
`clients.claim` behavior, SW killed mid-update frequency; how to make "app
|
||
update during festival" safe.
|
||
5. **TQ-5 SVG pan/zoom approach:** pointer-event based transforms, pinch zoom
|
||
quality, accessibility of interactive SVG elements with VoiceOver/TalkBack.
|
||
6. **TQ-6 Asset compression:** WebP/AVIF support matrix for map art and icons;
|
||
real-world size of the festival map at legible quality.
|
||
7. **TQ-7 Persistent storage grant rates:** measure `navigator.storage.persist()`
|
||
outcomes on iOS (installed) and Android; define behavior when denied.
|
||
8. **TQ-8 Install-funnel tooling:** detection of standalone mode, iOS install
|
||
instruction UI patterns, measuring install conversion.
|
||
9. **TQ-9 Package signing practicality:** WebCrypto Ed25519 support matrix on
|
||
target browsers (verify availability on iOS 16.4 baseline) vs hash-only
|
||
manifest validation as fallback.
|
||
10. **TQ-10 Search implementation:** client-side fuzzy search over ≤ ~1k items —
|
||
naive filter vs lightweight index (e.g., precomputed trigrams); cost/benefit.
|
||
11. **TQ-11 Build tooling:** bundler choice (vite/esbuild/etc.) and PWA plugin
|
||
maturity vs hand-rolled SW; dev-server HTTPS fidelity to production.
|
||
12. **TQ-12 Geolocation behavior:** permission persistence in iOS standalone
|
||
mode, time-to-first-fix in crowds, battery impact; whether to surface it at
|
||
all in V1.
|
||
13. **TQ-13 Testing strategy:** real-device matrix (iOS 16.4, latest iOS, low-end
|
||
Android, latest Android), airplane-mode test scripts, storage-eviction
|
||
simulation techniques.
|
||
14. **TQ-14 Timezone correctness:** Intl behavior for the festival zone on
|
||
baseline devices; DST edge cases if relevant.
|
||
|
||
---
|
||
|
||
## 12. PWA / Browser Risks
|
||
|
||
Verified platform facts → risks → mitigations to carry into architecture.
|
||
|
||
| # | Risk | Evidence / Mechanism | Severity | Mitigation direction |
|
||
|---|------|----------------------|----------|----------------------|
|
||
| PW-1 | **7-day storage eviction (iOS)** for users who browse in a tab before the festival and don't install. | Safari ITP: 7-day cap on all script-writable storage incl. SW registration; installed home-screen apps exempt. | High | Install-first UX; re-cache shell on every launch; detect empty storage and re-bootstrap; never assume prior state. |
|
||
| PW-2 | **Manual-only install on iOS** (no `beforeinstallprompt`). | iOS requires Share → Add to Home Screen (4+ taps). | High | Custom install coach with screenshots; QR flow; pre-festival campaign; measure funnel. |
|
||
| PW-3 | **All-or-nothing eviction** can wipe dataset between install and festival (storage pressure). | Eviction deletes all origin data at once. | Medium | Keep footprint lean; `persist()` request; honest OFFLINE READY check on every launch; one-tap re-download. |
|
||
| PW-4 | **No background sync on iOS**: updates only happen while app is open. | Background Sync / Periodic Sync unsupported on iOS Safari. | Medium | Sync-on-open pattern; opportunistic updates when online; never schedule-dependent. |
|
||
| PW-5 | **Push is not a reliable channel** (iOS: installed-only, region quirks; both: permission opt-in). | Web Push iOS 16.4+ installed PWAs only; EU behavior differs. | Medium | Treat announcements as pull-based in V1 (NR-13). |
|
||
| PW-6 | **SW lifecycle fragility**: iOS kills SWs aggressively; mid-update kills possible. | Documented WebKit behavior; SWs not long-lived processes. | High | Keep SW logic small, idempotent, resumable; no in-SW long computations; atomic staged downloads (AD-6). |
|
||
| PW-7 | **Quota surprises**: best-effort storage; `persist()` not guaranteed. | Quotas are estimates; eviction under pressure is silent. | Medium | `storage.estimate()` monitoring, budget alarms, graceful degradation UI. |
|
||
| PW-8 | **Standalone-mode quirks on iOS**: no browser back/forward UI, external links can strand users, safe-area insets, keyboard viewport behavior. | Known standalone behavior. | Medium | In-app back affordances; intercept/label external links; `viewport-fit=cover` + safe-area CSS; test on device. |
|
||
| PW-9 | **HTTPS requirement** excludes casual local-HTTP testing/distribution; any on-site LAN bootstrap would need TLS or localhost tricks. | SW + Geolocation + Storage API require secure contexts. | Low–Med | Plan TLS for any distribution point; localhost-only for dev. |
|
||
| PW-10 | **First-load bootstrap race**: user goes offline before first full cache completes. | Network-dependent first run. | Medium | Download critical sections in priority order (Emergency → Schedule → Map → Info → assets); resumable; show progress and what's ready. |
|
||
| PW-11 | **Browser version skew**: older iOS/Android missing APIs. | Baseline assumption A-06. | Medium | Feature-detection layer; minimum viable static fallback page; state supported baseline clearly. |
|
||
| PW-12 | **App update during festival**: new app shell + old dataset (or vice versa) compatibility. | Independent update channels for shell vs data. | Medium | Manifest declares min/max compatible app/dataset versions; refuse to activate incompatible combos (R-O6). |
|
||
| PW-13 | **Storage partitioning / third-party assets**: cross-origin assets complicate caching & quota. | Chrome 115+/Safari partitioning. | Low | Single-origin everything (C-20). |
|
||
| PW-14 | **Private browsing**: ephemeral/restricted storage in private mode. | Safari private mode limits persistence. | Low | Detect where possible; advise normal mode for install. |
|
||
| PW-15 | **iOS EU regulatory variability**: PWA behavior differs in EU regions. | Documented iOS 17.4+ EU differences. | Low | Assume worst case (tab behavior); install-first mitigates. |
|
||
|
||
---
|
||
|
||
## 13. Offline Risks
|
||
|
||
| # | Risk | Notes | Mitigation direction |
|
||
|---|------|-------|----------------------|
|
||
| OF-1 | **Bootstrap dependency**: offline-first only works after one successful online session. | Fundamental; see C-17, AMB-1. | Pre-festival distribution; on-site contingency (OQ-3); printed core info as physical fallback (poster/handout) — organizers' call. |
|
||
| OF-2 | **Storage evicted between install and festival** (PW-3). | Highest-likelihood data-loss path. | Launch-time integrity check; guided re-bootstrap; emergency data additionally embedded in app shell where feasible (see OF-3). |
|
||
| OF-3 | **Emergency data loss on eviction** is unacceptable. | Static emergency baseline could be *embedded in the app bundle itself* (bundled JSON in the JS/HTML artifact) so it survives even total dataset loss. | Evaluate: ship immutable emergency baseline in shell + optional dynamic overlay from dataset. This is a strong candidate architectural decision. |
|
||
| OF-4 | **Partial download / interrupted update** leaves inconsistent state. | Mobile reality: screens off, radios flaky. | Section-level downloads with hashes; activate only complete validated sets; keep previous set until new set committed (AD-6). |
|
||
| OF-5 | **Corrupted local data** (bit rot unlikely; buggy writes more likely). | IDB transaction bugs. | Integrity check on launch (checksums per section); quarantine + re-download; never trust unchecked data for Emergency display. |
|
||
| OF-6 | **Quota exhaustion mid-download** (large map assets). | QuotaExceededError on write. | Pre-check `storage.estimate()`; size budgets in manifest; abort cleanly and keep last-good. |
|
||
| OF-7 | **Wrong device clock** breaks Now/Next and "is festival live". | Airplane-mode phones drift; users change clocks. | §15.7 analysis; server-offset capture when online; festival-timezone anchoring; explicit "your clock looks wrong" UX when skew detected. |
|
||
| OF-8 | **Stale dynamic data mistaken for fresh** (schedule changed, user never online). | Honesty problem. | Show dataset age/version everywhere relevant; "data as of …" labels; organizers announce changes redundantly (PA/boards). |
|
||
| OF-9 | **App-shell update breaks against stored data** (schema drift). | Mid-festival app updates. | Versioned schemas + migrations run at activation; dataset manifest declares schema version; rollback path (OF-10). |
|
||
| OF-10 | **Rollback mechanics** after a bad update is applied. | Rare but must be defined. | Retain N=1 previous dataset slot until new one proven; "restore previous" action; app can always fall back to embedded emergency baseline. |
|
||
| OF-11 | **Phone restart / browser restart** mid-session loses in-memory state. | Favorites unsaved, draft state. | Write-through persistence for any user mutation; no memory-only truth. |
|
||
| OF-12 | **Low-battery-induced cold starts** repeatedly re-warm the app. | Users force-quit to save battery. | Fast cold start budget (< ~2s to usable), minimal re-hydration cost. |
|
||
| OF-13 | **Users clearing "website data"** thinking it's harmless. | iOS Settings → Safari → clear. | Nothing to prevent; re-bootstrap must be painless; embedded emergency baseline (OF-3) limits blast radius. |
|
||
|
||
---
|
||
|
||
## 14. Emergency-System Risks
|
||
|
||
| # | Risk | Severity | Notes / Mitigation direction |
|
||
|---|------|----------|------------------------------|
|
||
| EM-1 | **Emergency content wrong or stale** (wrong security number, moved first-aid tent). | Critical | Content sign-off gate (A-08, OQ-2, OQ-7); version stamp visible on every emergency screen; dynamic overlay updates only additive/corrective; static baseline reviewed per edition. |
|
||
| EM-2 | **Emergency data missing after eviction**. | Critical | Embed static baseline in app shell (OF-3) so *some* emergency info exists even at zero storage. |
|
||
| EM-3 | **`tel:` link fails** (no SIM, airplane mode). | High | Note that emergency calls may still work without SIM (region-dependent); display numbers as copyable text too; never present dialing as the only path. |
|
||
| EM-4 | **GPS coordinates wrong** → rescuers sent to wrong location. | High | Coordinates come from validated dataset; display in multiple formats (decimal + what3words-style fallback TBD); human-readable address always primary. |
|
||
| EM-5 | **User panic UX**: fumbling, mis-taps, unreadable in sunlight. | High | Emergency screen design: giant targets, maximum contrast, zero clutter, works in dark and direct sun, reachable one-handed; test with real users. |
|
||
| EM-6 | **False sense of capability** ("offline ready" shown when it isn't). | High | OFFLINE READY semantics must be provable (R-O6); emergency has its own always-true baseline regardless (EM-2). |
|
||
| EM-7 | **Legal/liability of procedural content** (first-aid instructions, evacuation guidance). | Medium | Procedures written/approved with qualified parties; app positions itself as *information access*, not medical/emergency service; disclaimers reviewed (OQ-7). |
|
||
| EM-8 | **Emergency updates race**: contact changes mid-festival but user is offline. | Medium | Accept as residual risk (A-16); mitigate with physical redundancies (signage/PA) owned by organizers; dynamic overlay fetched aggressively whenever any connectivity appears. |
|
||
| EM-9 | **Localization of emergency comprehension** under stress. | Low–Med | Plain language, short imperatives, icons + text (English-only V1 per A-03; revisit if audience differs). |
|
||
| EM-10 | **Misuse/false-emergency**: app must not itself trigger emergency services accidentally. | Low | Explicit user action only for dialing; no auto-call features ever. |
|
||
|
||
---
|
||
|
||
## 15. Data Architecture Questions
|
||
|
||
### 15.1 Authoritative source & publishing
|
||
- Who authors each content class, and what tooling do they get? (OQ-4)
|
||
- Default direction: **content-as-data in a repo or simple store → build/publish
|
||
step produces a signed Festival Data Package → uploaded to static hosting →
|
||
app pulls by version**. No dynamic backend required for V1 (A-17).
|
||
- Must define publishing roles (who can publish emergency changes) and an
|
||
audit trail even in a minimal pipeline.
|
||
|
||
### 15.2 Package content (conceptual — structure TBD)
|
||
Candidate sections, each independently versioned & hashed:
|
||
- `manifest` (package id, edition, version, section inventory, hashes, sizes, min/max app compatibility, timestamp)
|
||
- `emergency` (static baseline candidates also embedded in shell — OF-3)
|
||
- `schedule` (stages, slots, artists, events, tags, change metadata)
|
||
- `map` (geometry/artwork reference, POI list with map-local coordinates, categories)
|
||
- `locations` (POI detail records; may merge with map — boundary TBD)
|
||
- `festival-info` (structured info pages/sections)
|
||
- `announcements` (V1: optional pull inbox; NR-13)
|
||
- `assets` (map images, artist images?, icons) — **asset weight budget needed** (A-02)
|
||
|
||
### 15.3 Delivery & validation
|
||
- Immutable, versioned URLs (content-addressed or `/v{n}/…`).
|
||
- Per-section hash verification; whole-package signature if WebCrypto Ed25519
|
||
baseline allows (TQ-9); else hash-chain in signed manifest.
|
||
- Size caps enforced client-side (DoS hygiene, §18).
|
||
|
||
### 15.4 Local storage model
|
||
- Likely split: **Cache Storage** for shell + immutable assets; **IndexedDB**
|
||
for structured datasets; possibly small **localStorage** for flags/preferences.
|
||
- Must decide: datasets as parsed objects vs raw blobs parsed on read (memory
|
||
vs speed trade-off).
|
||
- Must define store layout that supports **A/B slots** (AD-6).
|
||
|
||
### 15.5 Atomic apply & rollback
|
||
- Download → verify → write to staging slot → integrity check → flip active
|
||
pointer → GC old slot after N successful launches.
|
||
- Interruption at any step must leave previous active slot intact.
|
||
- Decision needed: is the "pointer flip" an IDB transaction or an SW-managed
|
||
cache rename? (Both have iOS failure modes to spike — TQ-2/TQ-4.)
|
||
|
||
### 15.6 Migrations & compatibility
|
||
- Dataset schema version + app compatibility range in manifest.
|
||
- App-shell updates may require data migration at activation; migrations must
|
||
be idempotent and reversible-where-possible (or gated behind backups).
|
||
|
||
### 15.7 Time model (R-S5 analysis)
|
||
- **Store** all event times as UTC instants; **render** in festival IANA
|
||
timezone via `Intl` (works offline; tz database ships with the OS/browser).
|
||
- **Device clock risk:** wrong clock ⇒ wrong Now/Next and wrong "festival live"
|
||
state. Strategy candidates (choose later):
|
||
1. Trust device clock (simplest; wrong clocks silently wrong).
|
||
2. Capture server-time offset whenever online; store offset + captured-at;
|
||
use offset when device skew exceeds threshold; show warning when stale.
|
||
3. Organizers broadcast clock corrections via announcements (social fix).
|
||
- Recommended direction: (2) with graceful degradation to (1), plus visible
|
||
dataset timestamp so users can reason about staleness.
|
||
- Edge cases: DST transitions near festival dates; multi-day events crossing
|
||
midnight; user traveling across zones before arrival.
|
||
|
||
### 15.8 Favorites / user state
|
||
- Separate local-only store (never part of festival package; never overwritten
|
||
by dataset updates; survives dataset rollback).
|
||
- Schema keyed by stable event IDs → **event IDs must be stable across dataset
|
||
versions** (requirement to push onto the data schema).
|
||
- On dataset update, handle: event moved (keep favorite, update time), event
|
||
cancelled (mark), event removed (orphan policy).
|
||
|
||
### 15.9 Size budgeting (open, needs real content)
|
||
- Working hypothesis: shell < 1 MB JS+CSS; schedule+info JSON < 2 MB; map
|
||
artwork dominates (target ≤ 30 MB across zoom levels); total < 50 MB (A-02).
|
||
- Must validate against actual map art early (it is the top size risk).
|
||
|
||
---
|
||
|
||
## 16. Synchronization Questions
|
||
|
||
| # | Question | Current thinking (not a decision) |
|
||
|---|----------|-----------------------------------|
|
||
| SY-1 | Is sync ever bidirectional in V1? | No. V1 is **pull-only** (device ← package). No user writes leave the device (NR-4/NR-5). This eliminates conflict resolution entirely. |
|
||
| SY-2 | When does the app check for updates? | On open, when connectivity detected; opportunistic; never blocking; never in background (C-19). |
|
||
| SY-3 | Update granularity? | Section-level diffs vs whole-package re-download; decide after size budgeting (§15.9). Whole-package may be simplest if < ~10 MB delta. |
|
||
| SY-4 | Announcements: pull inbox semantics? | Fetch list when online; store locally; expiry rules; dedupe by id; authenticity (§18). |
|
||
| SY-5 | What if two dataset versions arrive back-to-back? | Monotonic version acceptance; never downgrade from network (rollback is local-only action). |
|
||
| SY-6 | How is "freshness" communicated? | Dataset version + generated-at + fetched-at shown in status screen; staleness thresholds for warnings. |
|
||
| SY-7 | Should favorites ever sync cross-device later? | If ever: export/import (QR/text) before considering accounts; keep IDs stable to allow it. |
|
||
| SY-8 | Does anything need server-side state for updates? | Not for static package hosting. Dynamic backend only if live features materialize post-V1. |
|
||
|
||
---
|
||
|
||
## 17. Future Mesh Architectural Questions
|
||
|
||
**Scope reminder:** V1 implements nothing here. These questions shape the seam
|
||
(AD-11) and nothing else.
|
||
|
||
### 17.1 Browser feasibility analysis (honest assessment)
|
||
- **BLE:** Web Bluetooth exists on Android Chrome only (central role), **not on
|
||
iOS Safari**, and cannot do background advertising or device-to-device relay.
|
||
Not a viable mesh substrate in-browser for our primary targets.
|
||
- **Wi-Fi Direct / ad-hoc / mDNS:** not exposed to browsers. No LAN discovery
|
||
APIs in Safari/Chrome on mobile.
|
||
- **WebRTC data channels:** peer-to-peer capable in principle, but requires
|
||
signaling (normally internet or a shared rendezvous), NAT traversal help, and
|
||
user gestures; no background operation on iOS. A *local-network* WebRTC link
|
||
is conceivable but fragile and complex.
|
||
- **Conclusion:** a meaningful mesh capability will almost certainly require
|
||
**something outside the browser**: a native companion app, an OS-level local
|
||
network service, dedicated hardware relay, or organizer-run LAN
|
||
infrastructure. The browser app's job is to be **transport-agnostic**, not to
|
||
own the mesh.
|
||
|
||
### 17.2 Seam design questions (for later)
|
||
| # | Question |
|
||
|---|----------|
|
||
| ME-1 | Is the abstraction over **transports** (byte pipes) or over **sync/messaging semantics** (deliver named updates, reconcile state)? The latter is more useful and keeps mesh concerns out of features (principle 12). |
|
||
| ME-2 | Should the data layer model content as **immutable versioned documents + operations log** so any transport can carry them? (Aligns with Festival Data Package design — one design serving two futures.) |
|
||
| ME-3 | Where does transport selection/plumbing live? Proposal: a single `SyncChannel` interface injected into the data layer; V1 ships exactly one implementation (`HttpPullChannel`). UI only ever sees "data freshness", never transports. |
|
||
| ME-4 | Security boundary: every transport must deliver **signed/verified** payloads — validation stays in the data layer so an untrusted mesh cannot inject content (§18, SE-9). |
|
||
| ME-5 | What identity/trust would a future mesh need (device pairing? organizer-signed relay nodes?) — entirely deferred, but argues for keeping signing keys/scheme transport-independent now. |
|
||
| ME-6 | Cost control: the seam must cost ~near-zero in V1. If an abstraction demands speculative machinery, defer the abstraction itself until a concrete second transport exists (principle 14/15). |
|
||
|
||
**Recommendation for next phase:** keep the seam *conceptual and minimal* — a
|
||
clean module boundary and interface definition — rather than building framework
|
||
plumbing. Revisit only when a concrete transport requirement appears.
|
||
|
||
---
|
||
|
||
## 18. Security Questions & Initial Threat Analysis
|
||
|
||
### 18.1 Threat inventory (realistic vs theoretical)
|
||
|
||
| ID | Threat | Class | Analysis |
|
||
|----|--------|-------|----------|
|
||
| SE-1 | **Malicious/tampered festival data package** (MITM of update, compromised hosting) | Realistic | Primary supply-chain threat. Mitigation: HTTPS + content hashes in signed manifest (AD-5); refuse activation on verification failure. |
|
||
| SE-2 | **Compromised publish credentials** pushing bad data | Realistic | Custody: minimal publishers, signed packages, version monotonicity, fast manual rollback runbook. |
|
||
| SE-3 | **XSS/content injection via festival content** (info pages, announcements, artist bios) | Realistic | Render content as data/text; no raw HTML ingestion; if rich text needed, strict sanitizer allowlist. |
|
||
| SE-4 | **Malicious announcements** (fake evacuation order) | Realistic, high-impact | Announcements inherit package signing/authenticity; never render unsigned network content as emergency content. |
|
||
| SE-5 | **Replay of old (stale) signed package** | Realistic, low-impact | Monotonic version checks; never accept lower version from network. Stale-but-valid data is an honesty issue (OF-8) more than a security one. |
|
||
| SE-6 | **Local data tampering** (user or malware on shared device) | Theoretical-low | No high-value secrets stored; integrity checks catch accidental corruption; deliberate local tampering only harms that user. |
|
||
| SE-7 | **Location privacy leakage** | Low in V1 | Geolocation is optional, on-device only, never transmitted (no server writes in V1). Ensure no URL/query leakage of coords. |
|
||
| SE-8 | **User privacy / tracking** | Low by design | No accounts, no analytics by default (AD-13); if any diagnostics, on-device only, opt-in, no identifiers. |
|
||
| SE-9 | **Future mesh injection** (malicious peer content) | Future | Rule now: data layer validates signatures regardless of transport; no transport bypasses validation (ME-4). |
|
||
| SE-10 | **DoS via oversized update / asset bomb** | Realistic-medium | Manifest-declared sizes enforced pre-download; per-section caps; abort + keep last-good. |
|
||
| SE-11 | **Service worker hijack / scope confusion** | Low | Single origin (C-20); strict scope; subresource integrity for any external scripts (ideally zero external scripts). |
|
||
| SE-12 | **Secrets in client** | N/A by design | No API keys, no signing private keys, no credentials ship to the browser (R-G3). |
|
||
| SE-13 | **Emergency-number spoofing in dynamic overlay** | Realistic, high-impact | Dynamic emergency overlay must be package-signed like everything else; static baseline only replaceable via reviewed build. |
|
||
| SE-14 | **Dependency supply chain** (npm) | Standard | Lockfiles, minimal deps, review; standard hygiene — not a novel architecture concern. |
|
||
| SE-15 | **TLS misconfiguration / downgrade** | Standard | HSTS; HTTPS-only endpoints; no mixed content. |
|
||
|
||
### 18.2 Open security questions
|
||
- **SQ-1:** Ed25519 via WebCrypto on baseline devices? (TQ-9) If gaps exist,
|
||
fall back to SHA-256 hash manifest over TLS without weakening the model.
|
||
- **SQ-2:** Key custody: who holds the package signing key; how is it rotated;
|
||
what's the recovery if lost (requires app-shell update to ship new key)?
|
||
- **SQ-3:** Is there any scenario where the app accepts *unsigned* dynamic
|
||
content? Proposed answer: **never for emergency; never for executable
|
||
content; display-only, clearly labeled** for anything else — ideally none.
|
||
- **SQ-4:** Logging policy: keep on-device diagnostic log with PII scrubbed;
|
||
define what "scrubbed" means before any export feature exists.
|
||
- **SQ-5:** If an announcements inbox exists, retention/expiry and maximum
|
||
message size limits.
|
||
|
||
---
|
||
|
||
## 19. Reliability Questions — Failure-Scenario Matrix
|
||
|
||
Required behavior for each scenario (these become acceptance criteria):
|
||
|
||
| # | Scenario | Required Lumen behavior |
|
||
|---|----------|--------------------------|
|
||
| FA-1 | No internet / airplane mode | All critical features work from local data; connectivity status shown honestly; no error walls; no feature silently disabled without indication. |
|
||
| FA-2 | No Wi-Fi, no cellular | Identical to FA-1 (no distinction needed by the app beyond "offline"). |
|
||
| FA-3 | GPS unavailable | App fully functional; map usable without blue dot; no prompts blocking anything. |
|
||
| FA-4 | GPS inaccurate | If shown, location displayed with accuracy caveat; never used for safety-critical guidance ("go to X" is always user-read map + signage). |
|
||
| FA-5 | Browser restarted | Instant restore: shell from cache, data from IDB, favorites intact, OFFLINE READY re-verified in background without blocking first paint. |
|
||
| FA-6 | Phone restarted | Same as FA-5; cold-start budget applies (OF-12). |
|
||
| FA-7 | Browser storage unavailable (private mode/quota 0) | Detect; run degraded: render whatever ships in the app bundle (shell + embedded emergency baseline); explain limitation; advise normal mode/install. |
|
||
| FA-8 | Storage evicted | Detect empty/corrupt state at launch; show recovery screen: what's lost, one-tap re-download when online; embedded emergency baseline still available (OF-3); never blank-screen. |
|
||
| FA-9 | Corrupted local data | Integrity check fails → quarantine affected section → fall back to previous slot if present → else re-download → else embedded baseline + honest status. |
|
||
| FA-10 | Interrupted update | Staging slot discarded or resumed; active slot untouched; user never sees partial content; retry idempotently. |
|
||
| FA-11 | Failed update (hash mismatch / signature fail) | Reject; keep last-good; surface non-alarming status ("update unavailable — current data still works"); log diagnostics locally. |
|
||
| FA-12 | Invalid festival data published | Client-side validation rejects at activation; organizers need server-side validation + staging preview in the pipeline (publishing runbook). |
|
||
| FA-13 | Incorrect device time | Now/Next uses corrected offset when available; otherwise device time with visible dataset timestamp; warn on detected large skew (TQ-14). |
|
||
| FA-14 | Schedule changes | Dataset update flow; cancelled/moved handling (§15.8); announcements if enabled; organizers use PA/signage for urgency. |
|
||
| FA-15 | Emergency information changes | Dynamic overlay update when online; static baseline immutable per build; version stamp visible (EM-1). |
|
||
| FA-16 | App update during festival | Compatibility ranges in manifests (PW-12); staged SW activation; user-informed update prompt if mid-session; never break current session. |
|
||
| FA-17 | Low battery | Dark-mode default at night; no background timers; fast interactions; no unnecessary geolocation polling; respect `prefers-reduced-motion` and battery-driven perf degradation. |
|
||
| FA-18 | First launch, then immediate offline before bootstrap completes | Show exactly which sections are ready; Emergency baseline works regardless; guide user to reconnect for the rest (PW-10). |
|
||
|
||
---
|
||
|
||
## 20. UX Questions
|
||
|
||
### 20.1 Structural
|
||
- **Home:** four huge, obvious destinations (Emergency visually dominant). No
|
||
dashboard clutter, no onboarding maze.
|
||
- **Emergency accessibility from everywhere:** persistent affordance (e.g.,
|
||
always-visible emergency entry point in the nav/chrome) — decide pattern in
|
||
architecture/design phase; must not depend on which tab is active.
|
||
- **Navigation model:** bottom tab bar (thumb-reach) + explicit in-app back;
|
||
deep-links for emergency/muster info; standalone-mode safe (PW-8).
|
||
|
||
### 20.2 Environmental
|
||
- Sunlight: max contrast, large type, avoid fine gray detail.
|
||
- Night: true dark theme (OLED-friendly), red-shift option? (open UX question),
|
||
avoid flash-on-open.
|
||
- Rain/movement: oversized touch targets (≥ 48dp recommended), forgiving hit
|
||
areas, confirm destructive/important taps.
|
||
- Noise: don't rely on audio for anything critical.
|
||
|
||
### 20.3 Accessibility
|
||
- WCAG 2.1 AA target (AMB-10): screen-reader labels for all POIs and schedule
|
||
items; logical heading structure; focus management on tab switches;
|
||
`prefers-reduced-motion` honored; map needs a **non-visual or list-based
|
||
equivalent** (e.g., "nearest facilities list") — also serves GPS-free use.
|
||
- Interactive SVG accessibility if SVG route chosen (TQ-5).
|
||
|
||
### 20.4 State honesty
|
||
- **OFFLINE READY ✓** must be a provable composite state: shell cached +
|
||
dataset present + all sections validated + compatibility confirmed (+
|
||
optionally assets complete). Show degraded states distinctly:
|
||
`READY` / `PARTIAL (list what)` / `RECOVERY NEEDED` / `EMERGENCY ONLY`.
|
||
- Dataset age/version visible in one predictable place.
|
||
- No spinners pretending; no fake progress; loading states only where real.
|
||
|
||
### 20.5 Open UX questions
|
||
- UX-1: Does Emergency get a dedicated always-on button vs a tab (vs both)?
|
||
- UX-2: Install coach design & timing (first visit? pre-festival email asset?).
|
||
- UX-3: How much map interactivity in V1 (search POI, categories toggle,
|
||
"nearest" list) vs static labeled map?
|
||
- UX-4: Now/Next presentation when device clock untrusted.
|
||
- UX-5: Favorites conflict UX when favorited events overlap in time.
|
||
- UX-6: What does the app show **after** the festival ends (schedule in past
|
||
tense? memorial mode? next-edition placeholder?) — AMB-12.
|
||
- UX-7: Onboarding: zero-step ideal; is a 3-line "how to use offline" card
|
||
enough?
|
||
- UX-8: Theming: is brand available (AMB-8) or high-contrast utility style?
|
||
- UX-9: Haptics (`navigator.vibrate`) for emergency confirmations — Android
|
||
only; degrade silently.
|
||
|
||
---
|
||
|
||
## 21. Recommended Next Architectural Investigation
|
||
|
||
Ordered by risk-reduction value. Each is a **spike/investigation, not a
|
||
commitment**:
|
||
|
||
1. **SPIKE-1 — Storage & eviction test harness on real iOS hardware.**
|
||
Measure: IDB reliability near quota, `persist()` grant behavior, 7-day ITP
|
||
reproduction on tab vs installed PWA, all-or-nothing eviction recovery UX.
|
||
*(Retires PW-1/3/7, TQ-2/7 — highest-severity unknowns.)*
|
||
2. **SPIKE-2 — Atomic dataset update prototype.** Service worker + IDB A/B slot
|
||
with staged download, hash verify, pointer flip, kill-mid-update survival,
|
||
rollback. Include app-shell vs dataset compatibility gating. *(Retires AD-6,
|
||
OF-4/9/10, FA-10/11/16.)*
|
||
3. **SPIKE-3 — Map representation bake-off.** Realistic sample festival map in
|
||
SVG vs tiled raster vs canvas on a low-end Android + iPhone: pan/zoom fps,
|
||
memory, accessibility, authoring effort. *(Retires AD-7, AMB-3.)*
|
||
4. **SPIKE-4 — Festival Data Package schema draft v0** with sample content:
|
||
manifest, sections, hashes, stable event IDs, time model (UTC + festival tz),
|
||
favorites-overlay behavior. *(Retires most of §15.)*
|
||
5. **SPIKE-5 — Install & bootstrap funnel.** iOS install coach prototype,
|
||
standalone detection, first-run prioritized download order, OFFLINE READY
|
||
state machine draft. *(Retires PW-2/10, AMB-1/5.)*
|
||
6. **SPIKE-6 — Framework shortlist benchmark** (2–3 candidates) on throttled
|
||
mid-range Android: cold start, 1k-row schedule render, search latency, JS
|
||
size. *(Retires AD-1, TQ-1.)*
|
||
7. **SPIKE-7 — Time-handling validation.** Intl timezone behavior on baseline
|
||
devices, server-offset capture design, wrong-clock UX prototype. *(Retires
|
||
R-S5, FA-13.)*
|
||
8. **Content intake templates** for organizers (schedule CSV shape, POI sheet,
|
||
emergency content sheet, map art spec) — unblocks A-04, OQ-2/6 in parallel
|
||
with technical spikes.
|
||
|
||
Then, and only then: architecture decision records for AD-1…AD-14.
|
||
|
||
---
|
||
|
||
## 22. Preliminary V1 Boundaries
|
||
|
||
**Proposed V1 = "the airplane-mode promise":** an installed PWA that, after one
|
||
successful online bootstrap, provides Emergency, Schedule (with local favorites),
|
||
Map, and Festival Info entirely offline, with honest readiness states and safe,
|
||
atomic data updates when connectivity exists.
|
||
|
||
### In scope (V1)
|
||
- PWA: manifest, service worker, install coaching (esp. iOS), offline shell.
|
||
- Four destinations + persistent Emergency access.
|
||
- Festival Data Package: versioned, hashed, atomically applied, rollback-safe.
|
||
- Local storage: datasets + favorites + preferences + integrity/version metadata.
|
||
- OFFLINE READY state machine with honest degraded states.
|
||
- Static emergency baseline embedded in app shell + dynamic signed overlay.
|
||
- Schedule: browse, now/next (local), favorites, filters, search.
|
||
- Map: custom offline map + POIs; no GPS requirement; list-based facility
|
||
access for accessibility.
|
||
- Festival info pages (data-driven).
|
||
- Pull-based updates (app shell + data) over HTTPS from static hosting.
|
||
- Failure behaviors per §19 matrix.
|
||
- Baseline device support: iOS 16.4+ Safari (installed), Android Chrome ~110+.
|
||
|
||
### Out of scope (V1) — revisit only with evidence
|
||
- Mesh networking of any kind (seam definition only, per §17).
|
||
- Push notifications as a relied-upon channel.
|
||
- Accounts, cross-device sync, user-generated content.
|
||
- Live map/GPS features, geofencing, navigation.
|
||
- Payments, ticketing, merch checkout.
|
||
- Native wrappers (TWA/App Store), CMS with live backend.
|
||
- Multi-language, multi-edition/multi-festival tenancy (schema should not
|
||
block it — A-01 — but no feature work).
|
||
- Announcements: **design the schema seam, implement only if time permits**;
|
||
never gate V1 on it.
|
||
|
||
### V1 success criteria (draft)
|
||
1. Cold start → usable Emergency info in ≤ 3 seconds on a 2021 mid-range phone,
|
||
airplane mode, 20 launches in a row.
|
||
2. Full dataset (shell + all sections + map assets) verifiable OFFLINE READY on
|
||
iOS Safari installed PWA and Android Chrome after one bootstrap session.
|
||
3. Kill-switch test: kill app/SW/phone at every stage of an update → last-good
|
||
dataset always survives; no user-visible corruption.
|
||
4. Zero critical features reachable only through network calls.
|
||
5. Emergency baseline survives total storage wipe (embedded in shell).
|
||
|
||
---
|
||
|
||
## Appendix A — Self-Review Checklist
|
||
|
||
| Check | Result |
|
||
|-------|--------|
|
||
| No application implemented | ✅ Only this document exists |
|
||
| No placeholder application code | ✅ Verified: directory contents = `.directory` (pre-existing, untouched) + `DISCOVERY.md` |
|
||
| Existing project directory inspected | ✅ §1–§2 |
|
||
| No native-mobile assumptions leaked | ✅ PWA-only; native explicitly non-requirements (NR-2/3); mesh-in-native analyzed as future, not V1 |
|
||
| Browser treated as first-class constraint | ✅ §5 (C-16…C-20), §12, §13 |
|
||
| Offline operation treated as core requirement | ✅ §4.7, §13, §19, §22 |
|
||
| Emergency treated as core requirement | ✅ §4.3, §14, OF-3/EM-2 embedded-baseline concept |
|
||
| Mesh treated as future architecture only | ✅ §17, NR-1, R-X2/X3 |
|
||
| Unresolved requirements identified | ✅ §8 ambiguities, §9 open questions, §10 decisions |
|
||
| No final technology decisions made | ✅ §10/§11 are questions/candidates only |
|
||
|
||
## Appendix B — Platform Fact Sheet (verified Aug 2026, informs §12)
|
||
|
||
- iOS Safari ITP: 7-day cap on all script-writable storage incl. SW
|
||
registrations for non-installed sites; **installed home-screen apps exempt**.
|
||
- Safari 17+ quotas: ~60% of disk per origin (browser apps; home-screen web apps
|
||
included), 80% overall; best-effort; eviction is LRU under pressure and
|
||
**all-or-nothing per origin**.
|
||
- `navigator.storage.persist()`: WebKit grants by heuristic (home-screen app
|
||
helps); Chrome auto-grants installed PWAs.
|
||
- No `beforeinstallprompt` on iOS; manual Add-to-Home-Screen only.
|
||
- No Background Sync / Periodic Background Sync on iOS Safari.
|
||
- Web Push: iOS 16.4+, installed PWA only, region-dependent; Android Chrome
|
||
full support. Declarative Web Push in Safari 18.4+.
|
||
- iOS 26: home-screen-added sites default to opening as web apps.
|
||
- Web Bluetooth / Web NFC: unavailable on iOS Safari.
|
||
- Chrome: up to ~60% of disk per origin; storage partitioning since Chrome 115.
|
||
- All storage APIs + geolocation + SW require secure contexts (HTTPS).
|
||
|
||
*End of discovery document. Await instruction to proceed to architecture.*
|