Non-Functional Requirements¶
Closed in Topic 5 of the requirements session (2026-04-27). Cross-cuts: V2 scope.
Change log¶
- CC-2026-05-01 — NFR-2 SMTP provider switched from Namecheap to Resend per keystone ADR-0005. Supersedes the 2026-04-27 sign-off text on email provider only. Authority: keystone is canonical for shared platform decisions; Stefan-approved 2026-05-01. The principle (single channel for V2 receipts and V3 magic-link / notifications) is unchanged.
| # | NFR | Resolution |
|---|---|---|
| NFR-1 | Hosting & stack | Deferred from this session and resolved out-of-band by the Platform Engineer (Atlas). Detail lives in the platform design proposal artifact. RBO consumes whatever runtime / DB / ingress / CI the platform proposal mandates. |
| NFR-2 | Email provider | Resend (free tier — 100 messages/day, 3,000/month) per keystone ADR-0005. Decided platform-wide on 2026-04-28; SMTP relay is smtp.resend.com:587, decoupled from any Namecheap product. DKIM issued per-domain by Resend (resend._domainkey.wagen.io); SPF and DMARC maintained at wagen.io apex by Atlas. Same channel for V2 receipt emails and (V3) magic-link / notification dispatch. Hard-cap behavior: apps must fail-soft (queue + retry); never crash the request path. |
| NFR-3 | PWA / offline | Nice-to-have, not V2. Online-only at the stringing table is acceptable. Could revisit in V3 (see V3 vision — C4). |
| NFR-4 | Backups & export | Two layers: (a) per-stringer self-service export — XLSX (familiar format) + full-context JSON (complete client-context dump). (b) Platform-level nightly pg_dump to Hetzner Storage Box. |
| NFR-5 | Browser support | Latest evergreen browsers, mobile + desktop. No legacy. |
| NFR-6 | Performance / scale | No specific p95 target — "build it robust." Realistic ceiling: ~few dozen stringers on the platform total, sub-100k rows total over the multi-year horizon. |
| NFR-7 | i18n | EN + DE. Both UI strings and PDF receipt templates. Locale rule: the user's saved preference (stringer.default_locale for V2; player.default_locale for the V3 client portal) wins over the browser Accept-Language header. The browser header is the fallback only when no saved preference is set. |
| NFR-8 | Catalogue moderation | Review every submission. Stringer marks a catalogue entry as "request shared" → enters admin inbox queue → admin (Stefan) promotes or rejects. Unread badges + admin notifications on each new submission. See use cases — UC-7. |
Notes¶
- Data sensitivity: hobby-scale, Swiss FADP basics; not a registered business; no VAT/audit retention burden beyond standard hygiene.
- Auth: auth on the listing is mandatory (M4) — eliminates V1's accidental public exposure of client names. Stringer auth onboarding is admin-issued magic link (M3).
- Multi-tenant isolation: strict per-stringer (M2). The only cross-tenant surfaces are the shared catalogue, the moderation queue, and the explicit player-share action — see data model.
- Locale stickiness: stringer locale is a profile preference (EN/DE) saved as
stringer.default_locale. Receipt templates render in the stringer's locale. Saved preference wins over browserAccept-Language; browser is fallback only. - Auth methods (M21): stringer's first sign-in is via admin-issued magic-link (registration). After that, both magic-link and password sign-in are available per stringer; neither is mandatory, both remain supported.