---
title: "Architecture & data flow"
version: "1.0"
effective_date: "2026-04-17"
source: https://www.harbingerexplorer.com/trust/architecture
---

> **What this page is for.** A self-service security assessment companion. Every box maps to a real system; every arrow maps to a real flow. Share it with your DPO, with internal security, with your customer's procurement team. The Mermaid sources are downloadable so you can re-render the diagrams in your own tooling.

## 1. System architecture

```mermaid
flowchart TB
    subgraph Browser["End-user browser (EU origin)"]
        UI["Next.js UI<br/>React + DuckDB-WASM"]
        DUCK["DuckDB-WASM<br/>(in-browser DB)"]
        UI <--> DUCK
    end

    subgraph EU["EU region — Vercel + Google Cloud (europe-west)"]
        EDGE["Vercel Edge<br/>Next.js routes"]
        APP["Next.js server runtime<br/>API routes"]
        FB["Firestore (EU)<br/>account, projects, audit"]
        STO["Vercel Blob / GCS (EU)<br/>large derived artefacts"]
        VTX["Vertex AI<br/>(europe-west)"]
        EDGE --> APP
        APP <--> FB
        APP <--> STO
        APP <--> VTX
    end

    subgraph Outside["Outside EU — minimal, opt-in surfaces only"]
        STR["Stripe (EU billing entity)<br/>SCC + DPF"]
        SEN["Sentry (EU residency)"]
        LOO["Loops (US, SCC + DPF)"]
        ANL["GA4 / Clarity / Ad pixels<br/>(consent-gated)"]
    end

    Browser -- HTTPS / TLS 1.2+ --> EDGE
    APP -- "billing webhooks" --> STR
    APP -- "scrubbed errors" --> SEN
    APP -- "lifecycle email" --> LOO
    UI -. "consent-gated only" .-> ANL
```

**Read this diagram as:** the data plane is **EU-only**. External vendors (Stripe, Sentry, Loops, ad pixels) receive **scoped, well-typed payloads** — never the customer's uploaded data.

## 2. Data flow — uploaded files

This is the diagram that proves the marketing claim "your files stay in the browser":

```mermaid
sequenceDiagram
    autonumber
    participant U as User browser
    participant DB as DuckDB-WASM
    participant API as Server API
    participant V as Vertex AI (EU)
    participant FS as Firestore (EU)

    U->>U: 1. User picks CSV/XLSX/JSON/PDF
    U->>DB: 2. Parse + load into in-browser DuckDB
    Note right of DB: Raw data NEVER leaves the browser
    U->>API: 3. Schema-only payload (column names, types, sample-row hashes)
    API->>FS: 4. Persist schema metadata (encrypted)
    U->>U: 5. User asks the agent a question
    U->>API: 6. Question + governance-allow-listed context
    API->>V: 7. Pseudonymised prompt (schema + aggregated stats)
    V-->>API: 8. SQL or analysis plan
    API-->>U: 9. SQL stream (no raw data echo)
    U->>DB: 10. Execute SQL locally on raw data
    U->>U: 11. Render result; only aggregate metrics ever leave
```

**Trust-critical points** (numbered to match the diagram):

- **Step 2.** Raw CSV/XLSX content never leaves the browser. The `frontend/lib/duckdb-pipeline.ts` module loads the file directly into DuckDB-WASM running in the browser memory.
- **Steps 3–4.** Only schema metadata (column names, inferred types, sample-row hashes) is ever persisted server-side, and it is encrypted at rest with AES-256-GCM (`lib/server/crypto.ts`).
- **Step 7.** The pseudonymisation pipeline (`lib/agents/transform/pii.ts`) drops or hashes any column tagged `personal_data` / `sensitive` before the prompt is constructed.
- **Step 9.** The agent returns SQL or analysis steps; the server never re-echoes the raw data it never saw.
- **Step 10.** The SQL is executed locally against the in-browser DuckDB, so the raw cells stay where they were uploaded.

## 3. Authentication & authorisation

```mermaid
flowchart LR
    USER([User]) -- "email/password<br/>or Google OAuth" --> FBAUTH["Firebase Auth<br/>(EU residency)"]
    FBAUTH -- "ID token" --> APIROUTE["Server API route"]
    APIROUTE -- "verifyIdToken" --> ADMINSDK["Firebase Admin SDK"]
    APIROUTE -- "admin only:<br/>requireAdmin + appendAuditEntry" --> ADMINOPS{{"Privileged operation"}}
    ADMINOPS -- "hash-chained" --> AUDIT[("admin_audit<br/>(tamper-evident)")]
```

**Trust-critical points:**

- Every privileged route calls `requireAdmin` (`lib/server/auth.ts`) and `appendAuditEntry` (`lib/server/audit-log.ts`) before any state change.
- The `admin_audit` collection is **hash-chained**; integrity can be verified offline via `scripts/audit-verify.ts`.
- Firestore security rules (`firestore.rules`) deny client-side access to every C10 admin collection — only the Admin SDK can read or write.

## 4. Sub-processor data egress map

```mermaid
flowchart LR
    APP["App runtime<br/>(EU)"] -- "billing only" --> STR["Stripe"]
    APP -- "scrubbed errors" --> SEN["Sentry"]
    APP -- "lifecycle email" --> LOO["Loops"]
    APP -- "AI inference,<br/>pseudonymised" --> VTX["Vertex AI<br/>(europe-west)"]
    APP -- "auth, projects,<br/>audit" --> FB["Firebase / Firestore<br/>(EU)"]
    APP -- "large artefacts" --> STO["Vercel Blob / GCS<br/>(EU)"]

    BRO["Browser"] -. "consent-gated only" .-> GA["GA4"]
    BRO -. "consent-gated only" .-> CL["Clarity"]
    BRO -. "consent-gated only" .-> ADS["TikTok / Meta / Google Ads"]
```

The complete list with mechanisms (SCC 2021, DPF) is at <https://www.harbingerexplorer.com/trust/subprocessors>; the RSS feed at `/trust/subprocessors/feed.xml` notifies of changes.

## 5. Disaster-recovery topology

```mermaid
flowchart LR
    PROD["Production project<br/>(EU)"] -- "daily managed export" --> GCS[("GCS backup bucket<br/>35-day rolling")]
    GCS -- "quarterly drill" --> DR["DR project<br/>(EU)"]
    PROD -- "live replication of<br/>audit log only" --> AUD[("admin_audit<br/>tamper-evident replica")]
```

- **RPO 24h, RTO 4h.** Documented + drill-tested in `docs/runbooks/disaster-recovery.md`.
- Backups are encrypted at rest by GCS; access is restricted to the dedicated `backup-job` service account (see `docs/runbooks/service-account-least-privilege.md`).

## 6. How to share this page

- **Public link.** Anyone may read this page; no NDA needed.
- **PDF/PNG.** Use your browser's built-in "Save as PDF" or any Markdown-to-PDF tool against the [Mermaid source](/trust/architecture.md). The Mermaid CLI (`npx -p @mermaid-js/mermaid-cli mmdc`) can render each block to standalone PNG/SVG.
- **Truth-checking.** Every box and arrow corresponds to a file in the repository — feel free to ask `dpa@harbingerexplorer.com` for a guided walk-through.

---

**Document version:** 1.0 — **Effective date:** 2026-04-17

