# Billver — Mail Verify v1.5 (particuliere ontvangers)

Status: ontwerp + veilige MVP-kern + lokale simulator. Geen echte mailboxen gekoppeld, geen inbound mail actief, niets gepubliceerd. Aanvulling op integratiedossier v1.4 (blijft geldig).

## 1. Provider-feasibility matrix

| Provider | Officieel integratiemodel | Bijlage lezen na expliciete klik | Toestemming / kost | Doorsturen neemt PDF mee? | Haalbaarheid |
|---|---|---|---|---|---|
| Gmail (web, Android, iOS) | Google Workspace Add-on (Gmail contextual trigger) via Marketplace | Ja, met `gmail.addons.current.message.readonly` (alleen geopend bericht) | OAuth-consent per gebruiker; publieke Marketplace-listing vereist Google OAuth-verificatie (restricted scope → mogelijk CASA security assessment, jaarlijks) | "Doorsturen" in Gmail neemt bijlagen standaard mee; "Doorsturen als bijlage" alleen op web | Hoog technisch, middel organisatorisch |
| Outlook.com / Hotmail / Live, Outlook nieuw/web, Outlook mobiel | Office Add-in (unified manifest), Microsoft AppSource | Ja: `Office.context.mailbox.item.getAttachmentContentAsync`, permissie `ReadItem` | Geen Graph-OAuth nodig voor huidig item; AppSource-validatie; consumentenaccounts ondersteund in nieuw Outlook/web; mobiel beperkt (add-in-ondersteuning iOS/Android gedeeltelijk) | Doorsturen neemt bijlagen mee; "als bijlage" (.eml) op web/desktop | Hoog |
| Telenet (webmail, IMAP) | Geen publiek add-in- of extensiemodel bekend | Nee | — | Ja via webmail/mailapp; ontvanger gebruikt vaak Gmail/Outlook/Apple Mail als client | Alleen doorstuurflow |
| Proximus (webmail, IMAP) | Geen publiek add-in-model bekend | Nee | — | Idem | Alleen doorstuurflow |
| Apple Mail / iOS Mail | Geen add-in-model voor bijlagen lezen (alleen Mail-extensies op macOS, beperkt) | Nee | — | Ja; deelblad "Delen" kan PDF naar een web-app sturen | Doorstuur + upload/deel |

Conclusie: er bestaat **geen universele add-in**. Telenet en Proximus hebben (voor zover publiek gedocumenteerd) geen extensiemodel. Alleen Gmail en Outlook bieden officiële, toestemmingsgebaseerde integratie. Niet geverifieerd tegen live providers in dit project; te bevestigen bij implementatie.

`mailto:` kan **geen bijlagen** meesturen (RFC 6068 kent geen attachment-parameter); een mailto-knop is dus onbruikbaar om een PDF door te geven. Of "Doorsturen" de bijlage meeneemt hangt af van client en instellingen: de flow controleert altijd of er werkelijk een PDF ontvangen werd en meldt anders "Geen bijlage gevonden".

## 2. MVP-keuze

Provider-onafhankelijk: **"Doorsturen naar controleadres"**. De ontvanger stuurt de factuurmail door (bij voorkeur als bijlage) naar een persoonlijk, kortlevend adres; het resultaat verschijnt op een mobiele pagina "Controleer deze factuur" op ons officiële domein. Daarna Outlook-add-in, daarna Gmail-add-on (zwaardere verificatie).

**Infrastructuurstatus:** het beheerde e-mailplatform van dit project ondersteunt alleen *uitgaande* mail. Inbound mail is **niet beschikbaar** → de MVP is een **lokale simulator** (`/mail-verify`) die exact dezelfde parsing/vergelijkingscode gebruikt als een toekomstige inbound-webhook.

### Benodigd voor echte inbound (BLOCKED, vraag aan jullie)
1. Definitief domein + subdomein voor inbound, bv. `check.<domein>` met MX-records.
2. Een inbound-mailprovider met webhook (bv. Postmark Inbound, SendGrid Inbound Parse, Mailgun Routes, Cloudflare Email Workers) + de bijhorende webhook-signing secret in Project Settings → Secrets.
3. DPA met die provider (verwerking van mailinhoud in de EU).
4. Daarna: route `/api/public/inbound/mail` die handtekening controleert, `checkAlias` toepast en `parseMail` + vergelijking aanroept.

## 3. Architectuur

```text
Ontvanger ── doorsturen ──> check+<sessie>.<exp>.<hmac>@check.<domein>
                                   │ (MX → inbound provider → ondertekende webhook)
                                   ▼
            verify signature → checkAlias (HMAC, verloopt) → rate limit per alias/IP
            parseMail (MIME, nested rfc822, base64/QP, limieten) → PDF via magic bytes
            per PDF: compareUpload(tokenless exact hash) ; link-token alleen van eigen HTTPS-origin
            verdict → resultaat gekoppeld aan sessie → mobiele pagina op officieel domein
            ruwe mail: alleen in geheugen, nooit opgeslagen of gelogd
```

Code: `src/lib/guard/mail-verify.ts` (puur), `src/lib/mail-verify.functions.ts` (simulator-servercall), `src/routes/mail-verify.tsx` (UI).

### Inbound adressen
`check+<sessionId>.<expiry36>.<hmac20>@domein`. Sessie-ID is willekeurig per controle (geen gebruikersaccount, geen e-mailadres als sleutel). HMAC met servergeheim, vergelijking constant-time, vervalt (voorstel 30 min). Geen geldig alias → mail stil weggegooid (geen bounce = geen enumeratie, geen backscatter).

### Parsing en veiligheid
- Max 25 MB ruwe mail, 40 MIME-delen, 10 bijlagen, 15 MB per PDF, 3 niveaus doorgestuurde mail.
- PDF herkend op `%PDF`-bytes, nooit op bestandsnaam. Gevaarlijke extensies (exe, js, html, docm, …) worden gemarkeerd en nooit geopend → minstens Nakijken.
- PDF's gaan door de bestaande upload-hardening (geen encryptie, JavaScript, ingesloten bestanden).
- Malware-scan: bij echte inbound de AV-scan van de inbound-provider vereisen of een scanner toevoegen (OPEN). PDF's worden nooit gerenderd of uitgevoerd, alleen gelezen.
- Abuse: rate limit per IP (simulator 10/10 min), per alias en per afzender; spam-score van provider → weigeren boven drempel.

### Verdicts
| Situatie | Verdict |
|---|---|
| Bijlage byte-identiek aan geregistreerd origineel dat zelf VERIFIED is | VERIFIED |
| Kritieke betaalafwijking (IBAN, bedrag, munt, mededeling, betaal-QR) of origineel BLOCKED | BLOCKED |
| Hash verschilt, betaalgegevens gelijk; verified + onbekende bijlage; gevaarlijke bijlage; link naar vreemd domein | REVIEW |
| Geen onafhankelijk origineel gevonden / geen PDF | NON_VERIFIABLE (nooit groen) |

Tokenloos zoekt alleen op exacte hash (geen cross-tenant lookup op btw+nummer, zoals v1.4 R7). Tokens uit de mail worden alleen gebruikt als de link exact op onze geconfigureerde HTTPS-origin staat; zonder geconfigureerd domein wordt geen enkel token vertrouwd.

## 4. Anti-phishing
- **Vervangen verificatielink:** een link naar een ander domein met ons padformaat wordt expliciet getoond als waarschuwing; nooit gevolgd.
- **Vervalst afzenderadres:** afzender wordt getoond maar telt nooit mee voor groen.
- **SPF/DKIM/DMARC:** alleen informatief ("geen garantie") — doorsturen breekt SPF, en een gecompromitteerde echte mailbox slaagt wel.
- **Vervalste QR:** QR's uit de ontvangen PDF worden alleen vergeleken met server-gedecodeerde QR's van het origineel.
- **Vertrouwd kanaal:** de ontvanger moet het controleadres en de resultaatpagina kennen *los van de factuurmail* (website van leverancier, eerdere communicatie, app/bookmark). Een controleadres dat in de verdachte mail zelf staat, bewijst niets.
- Gecompromitteerd leveranciersaccount blijft restrisico (v1.4 §provenance).

## 5. Mailclient-integratieconcepten
**Outlook add-in:** knop "Controleer deze factuur" in leesvenster → `getAttachmentContentAsync` voor PDF-bijlagen van het open bericht → POST naar onze API → taakvenster toont verdict. Permissie `ReadItem`, geen mailboxbrede toegang, geen achtergrondscan.
**Gmail add-on:** contextuele trigger op geopend bericht, scope `gmail.addons.current.message.readonly` + `gmail.addons.execute`; alleen na klik; Card-UI met verdict.
Beide: geen opslag van mailinhoud, alleen hash + verdict-events; OAuth/consent uitsluitend expliciet per gebruiker.

## 6. Privacy / DPA / retentie
- Grondslag: verzoek van de ontvanger (uitdrukkelijke actie). Geen automatische scans.
- Ruwe mail en niet-matchende PDF's: niet opgeslagen. Bewaard: verificatie-event met IP-hash, verdict, hash van bijlage (voorstel 90 dagen; retentiejob is nog OPEN).
- Geen e-mailinhoud, volledige IBAN of tokens in logs.
- Inbound-provider = subverwerker → DPA-annex nodig.

## 7. Kosten (indicatief, niet geverifieerd)
Inbound provider: typisch gratis tier tot enkele duizenden mails/maand, daarna laag per 1.000. Gmail add-on publiek: OAuth-verificatie + mogelijk jaarlijkse CASA-assessment (kost derde partij). AppSource: geen listingkost, wel validatietijd.

## 8. DONE / OPEN / BLOCKED
**DONE:** feasibility matrix; MIME-parser (multipart, doorgestuurde rfc822, base64/QP, limieten, gevaarlijke bijlagen); alias-ontwerp met HMAC + verval; link-originecontrole; verdictlogica met NON_VERIFIABLE; simulator `/mail-verify` met 5 synthetische scenario's + eigen .eml; tests.
**OPEN:** malware-scan; retentiejob; Outlook-add-in bouwen; Gmail-add-on bouwen; per-alias rate limit (vereist echte inbound).
**BLOCKED:** inbound-mailinfrastructuur + secret; definitief domein (tokens uit mail worden pas dan vertrouwd); Google/Microsoft publisher-accounts.

## 9. Tests
`bun test src/lib/guard/` → 118 pass / 0 fail (waarvan 15 nieuw in `mail-verify.test.ts`: magic bytes, gevaarlijke bijlage, doorgestuurde mail, auth-info, te grote mail, link-origine, alias roundtrip/tamper/verval, verdicts origineel/IBAN/onbekend/link vervangen/meerdere bijlagen/hash-only/geen bijlage). Dit zijn unit-tests zonder database. De simulator draait tegen de echte database (tokenloze exacte-hashvergelijking).

## 10. Release gates Mail Verify
| Gate | Status |
|---|---|
| Parser- en verdictregels | PASS (unit) |
| Simulator origineel → Geverifieerd, gewijzigd → nooit groen | zie testrapport chat |
| Echte inbound met ondertekende webhook | NOT TESTED (BLOCKED) |
| Malware-scan | NOT TESTED |
| Add-ins Outlook/Gmail | NOT TESTED |
