Juridisch

Sub-processors

Deze pagina is een rendering van het canonical bestand docs/security/sub-processors.md. Elke wijziging gaat via een PR die zowel dit bestand als het DPA-appendix aanpast en je tenminste 30 dagen vooraf op de hoogte stelt.

Sub-processors (Karet)

Last updated: 2026-09-01 · Owner: Founders · Reviewed: PRD-0035, PRD-0039, PRD-0090

This file is the canonical inventory of every third-party service that processes Karet data on our behalf. ADR-0011 §5 makes adding a new sub-processor a PR that touches this file plus the DPA appendix; a PR that imports a new third-party SDK without a matching entry here fails CI (scripts/check-sub-processors.ts).

The list is also mirrored on the public /legal/sub-processors page (PRD-0036) for user-facing transparency. Customer notice before adding a new sub-processor is 30 days (DPA appendix).

Active sub-processors

NamePurposeData categoriesRegionRetentionSDK / hostnames
VercelHosting, edge, blob, AI gatewayAll app data, request logs, blob (receipts + voice memos)EU (FRA1 / DUB1)90 days logs; blob per PRD-0036 retention@vercel/blob, @vercel/*, vercel.sh, vercel-storage.com, ai-gateway.vercel.sh
NeonPostgres (primary data store)All transactional + journal dataEU (Frankfurt)While workspace exists; 30-day delete window@neondatabase/serverless, neon.tech
AnthropicLLM (Claude) for chat, detectors, agentsPrompt content + workspace context (anonymised where possible)EU processing via AI Gateway; zero-retention contract0 days (no-prompt-logging)via ai + @ai-sdk/anthropic → ai-gateway.vercel.sh
OpenAILLM (GPT) fallback + embeddingsPrompt content + chat snippetsEU processing via AI Gateway; zero-retention contract0 days (no-prompt-logging)via ai + @ai-sdk/openai → ai-gateway.vercel.sh
ResendTransactional email (magic links, digests, security alerts)Recipient address, subject line, rendered bodyUS storage. Resend offers no EU data region; the eu-west-1 setting on a sending domain controls only where mail is routed and sent from, not where data is stored. Transfers under SCCs + EU–U.S. Data Privacy Framework (incl. UK/Swiss extensions); Art. 28 DPA in force.30 days e-mail + log data (active account); deleted within 90 days after termination; backups persist 7 daysresend, api.resend.com
Salt EdgePSD2 AIS bank sync (sole bank-data provider)Bank account metadata (IBAN, holder name, balances) + transactions for connected accountsUK entity holds the PSD2 AISP licence; EU data processingWhile connection active; credentials + tokens deleted on disconnect, consent expires per PSD2 (90 days, re-consent required)direct REST → www.saltedge.com/api/v6, hosted Connect flow on saltedge.com
Saxo BankBrokerage data (positions, trades)Account positions + trade historyEU (HQ Copenhagen)While connection activedirect REST → *.saxobank.com
Interactive BrokersBrokerage data via Flex QueriesAccount positions + activity statementUS (server-side Karet processes; user opts in)While connection activedirect REST → ibkr.com
BitvavoCrypto exchangeBalances + trade historyEU (Netherlands)While connection activedirect REST → bitvavo.com
SnapTradeAggregator fallback (DEGIRO, long-tail brokers)Account snapshot dataCanada (DPA in place)While connection activedirect REST → snaptrade.com
Have I Been Pwnedk-anonymity breach-password lookupFirst 5 chars of SHA-1 hash of candidate password onlyCloudflare globaln/a (stateless)direct REST → api.pwnedpasswords.com
StripeBilling (subscriptions, invoicing, tax)Customer (email, BTW, KvK, billing address), payment-method reference (no PAN), invoice line itemsEU (Stripe Payments Europe Ltd, Ireland)7 years invoices (NL art. 52 AWR); 30 days webhook bodiesstripe, *.stripe.com
PostHog (EU)Product analytics, funnelsdistinct_id (userId UUID), event names, non-PII propertiesEU Cloud (eu.i.posthog.com, AWS eu-central-1)13 months (also mirrored in telemetry_events)posthog-node, eu.i.posthog.com

Notes on recent additions (2026-05)

  • Stripe — DPA archived at docs/security/dpas/stripe.pdf. PCI-DSS scope kept to SAQ A via hosted Checkout — Karet never touches PAN. Sub-sub-processors per Stripe's published list.
  • PostHog (EU) — DPA archived at docs/security/dpas/posthog.pdf. Session Replay disabled at project level. No PII in event properties (lint enforced at src/lib/billing/telemetry.ts).

Correction — Resend region and retention (2026-09)

The Resend row previously read "EU region (eu-west-1)" and "90 days delivery logs". Both were wrong, and both overstated our privacy posture:

  • Region. eu-west-1 is the sending region chosen per domain. Resend's own GDPR page states plainly that it offers no EU data residency and that all customer data is stored in the US, and that the domain region "controls where email is routed and sent from" — explicitly not where data lives. We had conflated the two.
  • Retention. 30 days is the active-account retention for e-mail and log data. 90 days is the post-termination deletion window, which is a different thing.

Source: https://resend.com/security/gdpr (checked 2026-09-01).

Open item for the founders, not resolvable in code. The marketing trust strip, trust section and /trust page all state "Geen data buiten de EU" with the supporting detail "Vercel fra1 en Neon Frankfurt". That claim is not accurate while Resend is in the stack: every magic link, security alert and digest carries a recipient address and a rendered body to US storage. The options are to qualify the claim, to move transactional e-mail to an EU-resident sender, or to accept and disclose the transfer. Nobody should edit that copy without deciding which.

Notes on the PSD2 provider change (2026-08)

  • Salt Edge replaced Tink (Visa) as the bank-data provider. PRD-0039 migrated all PSD2 AIS to Salt Edge and removed the Tink and GoCardless integrations; no Tink code, credentials or endpoints remain. Tink is therefore removed from the table above rather than kept as historical.
  • The PSD2 AISP licence is held by Salt Edge's UK entity — Karet operates under it rather than holding its own AISP authorisation.

Pending (not yet active, listed because referenced in code)

  • Sentry (EU) — error reporting. EU region required if/when adopted.

Known gap in the CI check

scripts/check-sub-processors.ts matches npm package imports against the allowlist below. Every vendor Karet talks to over plain fetch — Salt Edge, Saxo, Interactive Brokers, Bitvavo, SnapTrade, Have I Been Pwned — is invisible to it. That is exactly why the Tink→Salt Edge swap passed unnoticed: no SDK was added or removed, so there was nothing for the check to catch.

Until the check also scans outbound hostnames, a direct-REST vendor change is a manual review item on any PR touching src/lib/connectors/.

Allowlist (scripts/check-sub-processors.ts)

These package-name prefixes are recognised as "third-party SDK" imports by the CI check. A PR adding an import that matches one of the prefixes but has no row above will fail CI.

@anthropic-ai/         → Anthropic
openai                 → OpenAI
@ai-sdk/anthropic      → Anthropic (via Vercel AI SDK)
@ai-sdk/openai         → OpenAI (via Vercel AI SDK)
@ai-sdk/gateway        → Vercel AI Gateway
@ai-sdk/react          → Vercel AI SDK (client hooks; no PII leaves)
ai                     → Vercel AI SDK
@vercel/               → Vercel (blob, kv, postgres, etc.)
@neondatabase/         → Neon
posthog-               → PostHog
@sentry/               → Sentry
@aws-sdk/              → AWS (KMS / SES)
stripe                 → Stripe
@upstash/              → Upstash (Redis-as-a-service)
resend                 → Resend
@react-email/          → React Email (rendering; not a sub-processor itself)
better-auth            → Better Auth (open source; runs in our process)
drizzle-orm            → Drizzle ORM (open source; runs in our process)

@react-email/, better-auth, and drizzle-orm are libraries that run inside our process and don't ship data to a third party — listed here so the lint doesn't flag them. Anything new matching the prefix patterns above must be either added to the active table or explicitly exempted in EXEMPT_PACKAGES in the script.