arc42 §11 — Risks & Technical Debt
Status: Draft v0.1 · Date: 2026-07-20 Method: severity = impact × likelihood, weighted toward irreversibility. A risk that is cheap to fix later ranks below one that forecloses options.
Executive Summary — the five things that matter#
| # | Risk | Why it leads |
|---|---|---|
| R-01 | SAMA licensing for holding & disbursing funds | Potential legal blocker. Could invalidate the entire payments design. Not an engineering problem. |
| R-02 | ZATCA e-invoicing — unplanned workstream and an architectural constraint | Invoice hash-chaining is stateful and strictly ordered — it directly conflicts with horizontal scaling |
| R-03 | Cross-tenant data leak | Existential. PDPL-reportable. Silent failure mode. |
| R-04 | Video recording decision | A single unanswered product question (OQ-08) swings video cost by ~80 % |
| R-05 | Arabic search & PDF quality | Both are one-way doors cheaply de-risked now, expensively fixed later |
Category A — Regulatory & Legal (highest severity, lowest engineering control)#
R-01 — SAMA licensing may be required to hold and disburse funds 🔴 CRITICAL — POTENTIAL BLOCKER#
Risk. SRC-BRD §9.4 describes revenue flowing Customer → Qwizin Platform → Provider/Consultant. If Qwizin collects buyer funds into its own account and disburses to consultants and providers, that may constitute a regulated payment service requiring SAMA authorisation in KSA.
Impact. Not a design detail — a go/no-go input. Discovering mid-build that a licence is required is the expensive direction; the reverse is not.
Status. ❌ UNVERIFIED. Research could not confirm the KSA regime. This requires Saudi financial-services counsel, not further research.
Mitigation — architect the lower-risk posture regardless of the legal answer:
| Model | Description | Exposure |
|---|---|---|
| A — Qwizin touches the money | Buyer pays into Qwizin's account; Qwizin holds balance and disburses | Typically triggers payment-institution / e-money licensing |
| B — Qwizin never touches the money ✅ | A SAMA-licensed PSP onboards consultants as sub-merchants under its licence. Funds flow payer → PSP → consultant. Qwizin transmits split instructions and takes commission as a separate merchant transaction. | Technology provider, not payments provider |
Decision: design for Model B. It is the pattern marketplace PSPs exist to serve, and if counsel later confirms Model A is permissible, moving toward it is easy. Do not build a ledger with user-owned balances until this is settled.
Questions for counsel: Is there a commercial-agent exclusion? Is "payment facilitator / aggregator" a recognised licence category in KSA? What AML/KYC obligations attach to Qwizin when onboarding payout recipients, even under Model B?
R-02 — ZATCA e-invoicing: unplanned workstream and a scaling constraint 🔴 HIGH#
Risk. Saudi Arabia mandates e-invoicing. A VAT-registered Saudi entity issuing tax invoices to KSA businesses very likely falls in scope. No source document mentions this.
Two distinct impacts:
- Unplanned backend workstream. UBL 2.1 XML, CSR generation and CSID onboarding, ECDSA cryptographic stamping, XAdES signatures, TLV QR codes, clearance (B2B) vs reporting (B2C) API flows, archival.
- ⚠️ An architectural constraint that conflicts with horizontal scaling. ZATCA requires invoice hash chaining (each invoice embeds the previous invoice's hash) plus a monotonic counter. This is stateful and strictly ordered — it cannot be produced concurrently by multiple API pods. A naive implementation across 3+ replicas produces a broken chain and rejected invoices.
Mitigation:
- Design a single serialisation point per invoice sequence — a dedicated single-consumer queue with a distributed lock, not a horizontally-scaled code path. Raising this now is cheap; retrofitting ordering into a concurrent design is not.
- Integrate a certified ZATCA solution provider rather than implementing the cryptographic stack in-house. For a small team this is almost certainly correct.
- Unresolved sub-question with legal weight: in a marketplace, who is the supplier of record for a consultant's service, and is Qwizin self-billing on their behalf? Interacts with
R-01— same counsel conversation.
Status. ❌ UNVERIFIED. Requires a Saudi tax advisor plus review of ZATCA's Detailed Technical Guidelines. Tracked as OQ-11.
R-03 — PDPL: the legal premise is contested 🟠 HIGH#
Risk. This engagement began on the premise that in-Kingdom data residency is legally mandatory. Research materially challenges that. Specialist counsel commentary indicates PDPL permits cross-border transfer under Article 29 (SCCs, Binding Common Rules, or a Certificate of Accreditation, plus a documented transfer risk assessment). A US trade-advisory source asserts the stricter localisation reading. These conflict.
Impact. The decision (in-Kingdom hosting, ADR-0001) does not change — it remains correct because it eliminates the entire transfer-compliance workstream and pre-satisfies the CST government-data rule. But the justification changes from "legally required" to "risk and workstream elimination", which matters if cost pressure later prompts a re-examination.
Separately unverified — and more operationally urgent: PDPL's operational duties. Breach notification thresholds and deadlines, data-subject-rights response deadlines, DPO appointment threshold, retention limits, and controller registration status could not be verified. These are exactly the numbers that get encoded into runbooks and SLAs, so they were not filled in from memory.
Enforcement is live: reportedly 48 SDAIA violation decisions across 2025–2026; penalties to SAR 3m and/or 2 years' imprisonment (Art. 35) and SAR 5m per violation (Art. 36).
Mitigation. Commission a KSA privacy opinion covering: lawful bases (do not assume GDPR parity — legitimate interest may not exist as a basis), DSR deadlines, breach timelines, DPO threshold, retention vs audit-retention conflict, cross-border assessments for subprocessors (video, SMS, error tracking), and the lawful basis for employer access to employee training records — a distinct question created by the ACT-CO/ACT-EMP model.
R-04 — Recorded consultations may constitute sensitive health data 🟠 HIGH#
Risk. If consultations are recorded (OQ-08, unresolved), recordings are unambiguously stored personal data, and a consultation may incidentally disclose health information. This is the classification architects miss, because the video pipeline gets designed as a media problem rather than a sensitive-data problem.
Impact. Sensitive-data classification raises the PDPL bar (Art. 35 carries criminal liability), makes in-Kingdom storage of recordings effectively non-negotiable, and adds consent, retention, and erasure obligations.
Mitigation. Recordings and session metadata must land in in-Kingdom storage regardless of where media relays. Encrypt at the application layer. Explicit consent capture from both parties. Defined retention with automated deletion. If recording is not required for MVP, not recording is materially cheaper and lower-risk — see R-06.
Category B — Security & Data Integrity#
R-05 — Cross-tenant data leak 🔴 CRITICAL#
Risk. A missing tenant scope exposes one company's employee records, documents, or assessment results to another. Broken object-level authorisation is the most common serious flaw in multi-tenant SaaS, and it fails silently — no error, no crash, just wrong data returned.
Why the obvious mitigation is insufficient. An Eloquent global scope is bypassed by withoutGlobalScopes(), raw queries, DB::table(), and by any developer who does not know it exists. It is a safety net, not a control.
Mitigation — five layers (detailed in arc42/06-runtime-view.md Flow 6):
- PostgreSQL Row-Level Security with
FORCE ROW LEVEL SECURITY— the only layer that fails closed regardless of application bugs - A
TenantContextthat throws when unset, never defaults to "no filter" - Eloquent global scope — convenience and index usage, not a security boundary
- Architecture test asserting every
company_idtable uses the scoping trait - Adversarial CI suite — two seeded tenants, every endpoint requested with the other's IDs, build fails on any leak
⚠️ Two RLS failure modes that must be designed for now: connection reuse under pooling or persistent workers can carry a session variable across requests; and queued jobs have no request context, so every tenant-scoped job must re-establish it from its payload and throw if absent.
Design implication accepted: this is a primary reason Laravel Octane is deferred (ADR pending). Octane's persistent workers make the connection-reuse failure mode significantly more likely, trading the top quality attribute for performance the launch scale does not require.
R-06 — Assessment integrity and certificate forgery 🟠 HIGH#
Risk. Three distinct failures: (a) the answer key leaks to the client; (b) a score is tampered with; (c) a certificate is forged in a graphics editor.
Why it ranks above a typical integrity concern. These certificates assert food-safety competence. A forged certificate is not a cosmetic badge — it transfers real-world risk to restaurant patrons, and it destroys the platform's central value claim (SRC-MOM KPI 3).
Specific leak paths identified:
- ORM over-serialisation — a
Questionmodel serialised viatoArray()shipscorrect_option_id. Use explicit API resources with a field allowlist, plus$hiddenas defence in depth. This is a very common real-world breach path. - The review endpoint — showing a learner their wrong answers after completion is legitimate and frequently re-exposes the entire key.
Mitigation: server-side grading only; no score field in the submission API; append-only attempt history; question pooling with randomised option order; and a three-layer certificate defence — signed QR payload (enables offline verification), public verification endpoint with unguessable IDs and minimal PII, and revocation from day one.
R-07 — Malicious upload via the legal-document pipeline 🟠 HIGH#
Risk. Company and provider onboarding accepts arbitrary file uploads from unverified parties — by definition, since verification is what the upload is for. A stored XSS payload served same-origin would execute against the admin reviewer's session, the highest-privilege session in the system.
Mitigation: quarantine bucket → ClamAV scan → promote on clean; magic-byte validation, never extension or client MIME; served from a separate domain (not a cookie-sharing subdomain) with Content-Disposition: attachment and X-Content-Type-Options: nosniff; server-generated filenames; short-TTL signed URLs; and for PDFs, consider rendering a flattened preview so reviewers never open the original in a browser PDF engine. Fail closed — scanner unavailable means not-yet-clean, never assume-clean.
Category C — Architectural & Technical#
R-08 — Module boundary erosion 🟠 HIGH#
Risk. The modular monolith degrades into a big ball of mud, making the Phase-2 extraction promised by CONF-04 a rewrite rather than a refactor.
Why this is likely rather than hypothetical. Eloquent's ergonomics are precisely what dissolve boundaries: $user->bookings->first()->consultant->company spans four modules in one line, compiles fine, reads naturally, and is unextractable. The User model is the specific trap — in a typical Laravel app every module hangs relations off it until it imports from every namespace.
Mitigation:
Public/vsInternal/namespace split per module. One rule — "no module may reference another module'sInternal" — is worth more than a hundred pages of guidelines, because it is unambiguous and mechanically checkable.- Deptrac (⚠️ note:
qossmic/deptracis abandoned; usedeptrac/deptrac) with--fail-on-uncovered, plus Pest architecture tests. Both in CI. - No cross-module foreign keys. No cross-module Eloquent traversal. Never pass a model across a boundary — pass a DTO.
- Transactional outbox + own
EventBusinterface from week one. Payloads are primitives only, with a version field. This is the highest-leverage extraction insurance available — roughly a day of work that buys the broker swap, replay, and an audit trail. - Hard rule:
Shared/contains only value objects and interfaces. The moment it contains behaviour it becomes the new ball of mud.
Accepted debt: dropping cross-module FK constraints loses database-level referential integrity. Bought back with domain events for cascades and a reconciliation job that reports orphans as a metric rather than failing writes.
R-09 — Kubernetes operational burden vs team size 🟠 HIGH#
Risk. CON-15 (Kubernetes) directly opposes CON-01 (budget) and QAS-OPS-01 (operability by a small team). Kubernetes raises the operational floor.
Position. This was an explicit architect decision and is respected — but the consequence is recorded plainly: the observability and automation investment it forces is now a requirement, not an optional extra. Choosing Kubernetes without paying that cost converts an infrastructure choice into a permanent operational tax.
Mitigation: managed control plane (OKE); vanilla upstream primitives only; the staged hardening roadmap ordered by risk-reduction-per-hour; correlation IDs end-to-end; alerting on user-facing SLOs, not CPU graphs; single-command rollback under 10 minutes.
A deliberate position on runtime security: Falco/Tetragon is where hardening programmes usually die — default rules produce hundreds of daily alerts, nobody triages, the DaemonSet gets muted. That leaves runtime security on the architecture diagram and none in reality, which is worse than absence because it creates false assurance. If nobody will triage it, do not deploy it — spend the effort on the structural controls instead.
R-10 — Video architecture: the recording decision dominates cost 🟠 HIGH#
Risk. The original proposal (SRC-MOM §11) was self-hosted Jitsi, chosen on open-source cost grounds. That premise does not survive contact with a recording requirement.
The finding, which is derivable rather than researched: Jitsi's cost advantage for 1-to-1 calls comes largely from P2P mode, where media bypasses the server entirely (~80 % egress saving). Jibri records by joining the conference as a third participant — which makes the call no longer 1-to-1, forcing it back through the bridge. Universal recording therefore kills the main saving and adds the most expensive tier. The two effects compound, and both flow from the same product decision.
Consequence: OQ-08 (are consultations recorded?) is not a minor product question — it swings video infrastructure cost by roughly 80 % and should be answered before any video work begins.
Further findings:
- Crossover where self-hosting beats managed is above 300 concurrent sessions, plausibly well above, once engineer time is included. At launch scale, self-hosting means paying the full fixed cost of a scarce media-ops capability to serve almost no traffic.
- If self-hosting is required, LiveKit is preferred over Jitsi — pod-level autoscaling via UDP mux rather than Jitsi's
hostNetworkone-pod-per-node constraint, and better recording economics. - Jitsi's signalling plane (Prosody, Jicofo) is effectively singleton — the availability risk everyone overlooks while focusing on JVB scaling.
- TURN is mandatory, not optional — Saudi mobile networks are heavily CGNAT'd, and for a paid consultation an unconnectable call is a refund plus a churned customer.
Mitigation: the provider-agnostic port (SEAM-02) makes this reversible. Twilio's Video EOL is empirical proof that vendor exit in this market is not hypothetical.
R-11 — Arabic search quality 🟠 HIGH — one-way door#
Risk. Generic full-text search on Arabic without normalisation produces near-zero recall. The failure is silent and total: a Saudi user searching in Arabic gets nothing and concludes the directory is empty.
Two distinct requirements, usually conflated:
- Orthographic normalisation — folding alef variants (أ إ آ ٱ → ا), teh marbuta (ة → ه), alef maqsura (ى → ي), stripping diacritics and tatweel. The higher-impact half.
- Morphological stemming — Arabic agglutinates the definite article ال and clitics و، ف، ب، ل، ك.
المطاعم/مطاعم/للمطاعم/مطعمare one concept to a user and four tokens to a naive index.
⚠️ Specific concern flagged: Meilisearch's relevance model leans on prefix matching, and Arabic attaches its definite article as a prefix — so مطاعم does not prefix-match المطاعم. In a language whose most common morphological feature is prefixation, this is a structural disadvantage. (Unverified — this is precisely what the spike must measure.)
Mitigation — own the normalisation, and the engine choice stops being a one-way door. Run a normalisation pipeline in the application at index time and apply the identical pipeline to queries. Roughly 150 lines of well-tested string handling that delivers the higher-impact half on any engine, keeps Arabic expertise in code that can be tuned from real query logs, and makes the engine swappable.
Residency note: managed search SaaS is likely disqualified — marketplace listings contain personal data, so an out-of-Kingdom index is a cross-border transfer. Self-host on the cluster.
R-12 — Arabic PDF rendering 🟡 MEDIUM — but expensive if discovered late#
Risk. Arabic requires contextual glyph shaping and bidirectional layout. "Supports UTF-8" means nothing — a library can emit correct codepoints and render disconnected, backwards, unreadable Arabic. A certificate with broken Arabic is visibly amateur to every Saudi user and unusable as a credential.
Eliminates several common choices: dompdf has no Arabic shaping or bidi; wkhtmltopdf/snappy is archived and unmaintained (also a CVE liability).
Mitigation: a browser-engine renderer (HarfBuzz shaping) as a separate Kubernetes Deployment — keeps Chromium out of the app image, scales independently on burst, and contains OOM blast radius. Embed fonts in the image; never reference a webfont CDN (rendering flakiness and a cross-border request). The test that decides it: render a real bilingual certificate and show it to an Arabic speaker — not to yourself.
R-13 — Laravel has no LTS 🟡 MEDIUM#
Risk. The last Laravel LTS was version 6 (2019). All releases now get 18 months bugfix / 24 months security. There is no install-and-leave-it option. Laravel 12's bugfix window ends 2026-08-13 — three weeks from now.
Mitigation: start on Laravel 13 + PHP 8.4; budget an annual major upgrade as routine operational work, not a project. Laravel 13 emphasises minimal breaking changes, so budget days rather than weeks. Teams that skip this end up stranded on an EOL version facing a security-driven emergency migration.
R-14 — Payment gateway must support authorize/capture/void 🟡 MEDIUM#
Risk. CONF-02 (payment precedes consultant acceptance) is resolved by authorizing a hold and capturing only on acceptance, voiding on rejection or expiry. Not all KSA gateways support authorization/capture separation, particularly for mada.
Mitigation: this is now a hard selection criterion feeding OQ-01, alongside marketplace payout capability (R-01). Both must be validated before a gateway is chosen.
Category D — Requirements & Process#
R-15 — Contradictions in the source documents 🟠 HIGH#
Four unresolved conflicts, detailed in 00-requirements-baseline.md §5:
| ID | Conflict | Blocking |
|---|---|---|
CONF-01 |
Training scope: BRD excludes video e-learning, learning paths, and analytics; the Training Portal deck specifies all three plus branch comparison | OQ-04 |
CONF-02 |
Payment captured before consultant agrees | OQ-05 — resolved architecturally, needs policy ruling |
CONF-03 |
Roles matrix is internally inconsistent — Admin shown with "Book Consultation"/"Manage Availability", Service Provider with "Access Learning". Appears to be a column-alignment artifact. | OQ-06 |
CONF-04 |
Investor deck commits to POS/PMS, CRM, inventory, reservations, government integration | Not blocking — drives seam placement |
⚠️ CONF-03 is the most dangerous because §11 of the BRD is the authoritative input to the authorization model. Building from the PDF as-is would produce real privilege bugs. A corrected, machine-checkable permission matrix must be issued before authorization work begins.
R-16 — Research verification gaps 🟡 MEDIUM#
Risk. The research supporting these documents exhausted the session's web-search budget. Several material claims are explicitly marked unverified rather than filled from memory.
Principal gaps:
| Gap | Consequence |
|---|---|
OCI me-riyadh-1 per-region service matrix (managed Redis, registry, secrets, streaming) |
A missing managed Redis changes the architecture — ADR-0001 gap #1 |
| No verified 2026 KSA per-SKU pricing | Cost is the primary argument for Oracle; rests on a structural pricing-uniformity claim |
| Oracle's CST registration class | Gates all government work — could invert ADR-0001 |
| AWS KSA GA status unresolvable from AWS-owned sources | Would reshuffle the provider ranking |
| Whether any CPaaS offers KSA/GCC media presence with contractual residency | Gates the entire managed-video option |
| Per-engine Arabic support (all four search engines) | R-11 — resolved by a 2–3 day spike |
| Arabic PDF library maintenance status | R-12 — resolved by a 1 day spike |
Mitigation. Gaps are stated rather than papered over. Cost and regulatory status are precisely where a plausible-sounding wrong number does damage, so no unverified figure is presented as fact. Several gaps are closed more cheaply by a spike than by more research.
Risk Matrix#
| ID | Risk | Severity | Reversible? | Owner |
|---|---|---|---|---|
R-01 |
SAMA licensing | 🔴 Critical | ❌ No | Legal counsel |
R-05 |
Cross-tenant leak | 🔴 Critical | ⚠️ Only before launch | Architect |
R-02 |
ZATCA scope + serialisation | 🔴 High | ⚠️ Costly later | Tax advisor + Architect |
R-03 |
PDPL operational duties | 🟠 High | ✅ Yes | Privacy counsel |
R-04 |
Recordings as sensitive data | 🟠 High | ⚠️ Costly later | Sponsor + counsel |
R-06 |
Assessment / certificate integrity | 🟠 High | ⚠️ Costly later | Architect |
R-07 |
Malicious upload | 🟠 High | ✅ Yes | Architect |
R-08 |
Boundary erosion | 🟠 High | ❌ Effectively no | Architect + Tech lead |
R-09 |
K8s operational burden | 🟠 High | ✅ Yes | Architect |
R-10 |
Video cost / recording | 🟠 High | ✅ Yes (port) | Sponsor (OQ-08) |
R-11 |
Arabic search | 🟠 High | ⚠️ One-way door | Architect (spike) |
R-15 |
Source contradictions | 🟠 High | ✅ Yes | Sponsor / BA |
R-12 |
Arabic PDF | 🟡 Medium | ✅ Yes | Architect (spike) |
R-13 |
No Laravel LTS | 🟡 Medium | ✅ Yes | Tech lead |
R-14 |
Gateway capabilities | 🟡 Medium | ⚠️ Costly later | Architect (OQ-01) |
R-16 |
Verification gaps | 🟡 Medium | ✅ Yes | Architect |
Recommended Immediate Actions#
Before any code is written:
- Engage Saudi counsel on
R-01(SAMA),R-02(ZATCA),R-03(PDPL). These determine what is built, not merely how. Do not spend engineering effort on payments or invoicing untilR-01andR-02are answered. - Obtain a corrected roles & permissions matrix (
CONF-03) — authorization cannot be designed from the current document. - Get a ruling on
OQ-08(recording). It swings video cost by ~80 % and changes the PDPL classification. - Get a ruling on
CONF-01(training scope) — video learning changes the storage and CDN architecture materially.
Cheap spikes that retire disproportionate risk:
- Arabic search bake-off — 2–3 days, ~500 real listings, all four engines, with the normalisation pipeline in front. One-way door; everything else proceeds in parallel.
- Arabic PDF render — 1 day. Produce one real bilingual certificate and show it to an Arabic speaker.
- Verify OCI region service matrix and CST class — hours, not days, and gates ADR-0001.
Build first, before the second module exists:
- The enforcement harness — Deptrac + architecture tests + the two-tenant adversarial suite. Retrofitting boundaries onto six modules costs roughly 20× more than starting with two.
- Transactional outbox +
EventBusinterface — about a day, and it is the cheapest extraction insurance available. - RLS policies alongside the first tenant-scoped migration — adding RLS to a populated table under load requires a maintenance window.