DRAFT — not for execution
Data Processing Agreement (DRAFT — not for execution)
1. Scope and definitions
This Data Processing Agreement (DPA) governs the processing of personal data by the controller (the customer) and the processor (Altvisor) under EU/EEA data protection law, including the General Data Protection Regulation (Regulation (EU) 2016/679, **GDPR**) and applicable national implementing legislation.
Capitalised terms used and not defined have the meanings given in Article 4 GDPR.
2. Parties
- **Controller:** the customer identified in the signed order form or service agreement.
- **Processor:** Altvisor (legal entity to be inserted at execution time), acting under the customer's documented instructions.
3. Processing details
| Field | Value |
|---|---|
| Subject matter | Automated generation of WCAG 2.1 AA alt-text; storage of generated alt-text, image metadata, and audit records; production of accessibility statements and compliance audit PDFs. |
| Duration | The term of the underlying service agreement plus the technical-documentation retention period defined in §6. |
| Nature and purpose | Provide the Altvisor service to enable customer compliance with the European Accessibility Act (Directive 2019/882). |
| Categories of data subjects | Customer's website end-users (where image alt-text may indirectly reference them) and customer's authorised users of the Altvisor dashboard. |
| Categories of personal data | Account data (email, hashed password, OAuth provider IDs), usage telemetry (timestamps, costs, model identifiers), and customer-uploaded image content (processed in-memory; not persisted — see image lifecycle in §5). |
| Special categories | None processed by design. Customer must not upload images depicting health, ethnicity, religion, sexual orientation, biometrics, or other Art. 9 categories. |
4. Subprocessors
The processor engages the subprocessors listed below. The customer is notified at least 30 days before any change to this list; customer may object in good faith for documented data-protection reasons.
| Subprocessor | Purpose | Location | Transfer basis |
|---|---|---|---|
| Mistral AI (La Plateforme) | Pixtral 12B inference for Free and Pro alt-text generation | France (EU) | Same jurisdiction (EU/EEA) — no transfer mechanism required. |
| Anthropic (Claude API) | Claude Haiku 4.5 inference for Business tier alt-text generation | United States | EU-US Data Privacy Framework + Standard Contractual Clauses (Module 2) |
| Supabase | Managed Postgres, authentication, storage (eu-west-1) | Ireland (EU) | Same jurisdiction (EU/EEA) — no transfer mechanism required. |
| Vercel | Edge runtime, serverless functions, static asset hosting | United States (with EU edge regions including fra1) | EU-US Data Privacy Framework + Standard Contractual Clauses |
| Stripe | Subscription billing, payment processing, EU VAT collection (Stripe Tax) | Ireland (EU) — with US parent processing | Standard Contractual Clauses + EU-US Data Privacy Framework |
| Resend | Transactional email (password reset, magic link, account notifications) | United States — with EU sending region | Standard Contractual Clauses |
| PostHog | Product analytics, web vitals, error tracking, session replay, LLM observability, and application logs. EU-hosted project; ingestion proxied via /ingest/* to avoid third-party origin in the browser. | Germany (EU) — eu.i.posthog.com | Same jurisdiction (EU/EEA) — no transfer mechanism required. |
| Google Analytics (GA4) | Marketing-site traffic measurement. Loaded only on /(marketing) routes; gated by Consent Mode v2 (default 'analytics_storage: denied'); cookies dropped only after user accepts via the marketing-page consent banner. | United States | EU-US Data Privacy Framework + Standard Contractual Clauses |
| GitHub (Microsoft Corp.) | Content hosting + CMS authentication. Hosts the public marketing blog MDX and authenticates the content partner via the Keystatic GitHub App. Only public marketing content and the partner's GitHub login identity flow here — no customer or data-subject personal data. | United States | EU-US Data Privacy Framework + Standard Contractual Clauses |
This list must remain identical to the table at `/legal/subprocessors`. CI enforces parity (`__tests__/subprocessor-dpa-parity.test.ts`).
5. Image lifecycle and data minimisation
Customer-uploaded images flow through the Altvisor platform in a strictly bounded lifecycle. Customer warrants that uploaded images do not contain Article 9 GDPR special categories.
- The browser obtains a signed upload URL from the Altvisor API.
- The browser uploads the image directly to Supabase Storage.
- The server processes the upload: resize to ≤1568px, compute SHA-256 hash, deduplicate.
- The server invokes the configured AI subprocessor (Mistral or Anthropic) to generate alt-text.
- The server stores the generated alt-text, image hash, and dimensions in the Altvisor database.
- **The server deletes the image file from Supabase Storage.** Only the hash, dimensions, and alt-text remain.
The original image is never persisted beyond the duration of step 5. Subprocessor calls forward only the bytes necessary for inference and rely on the subprocessor's documented data-retention posture (Mistral: ≤30 days for abuse review; Anthropic: ≤30 days). Customer accepts the residual retention at the subprocessor layer.
6. Retention
| Data class | Retention | Basis |
|---|---|---|
| Alt-text records (incl. version history via `superseded_by`) | Duration of the service + 5 years after termination | EAA Annex IV — technical documentation supporting the self-assessment |
| Audit reports (PDF + ledger row) | 5 years from generation | EAA Annex IV |
| Audit log (every state-changing action) | Duration of the service + 5 years | EAA Annex IV + GDPR Art. 5(2) accountability |
| Account data | Duration of the service + 30 days for orphan-cleanup pg_cron | Service delivery |
| Demo rate-limit bucket counters | ≤48 hours | Abuse prevention; deletes hourly |
| Daily-rotating IP salt | ≤2 days | Salt rotation cycle |
| Raw IP addresses | **Never stored.** Hashed with the daily salt at receipt and discarded. | Data minimisation (Art. 5(1)(c) GDPR) |
7. Cookies and tracking inventory
| Cookie | Purpose | Attributes | Retention | Lawful basis (ePrivacy Art. 5(3)) |
|---|---|---|---|---|
| `sb-<project-ref>-auth-token` | Session token issued by Supabase Auth | HttpOnly, Secure, SameSite=Lax, Path=/ | 30 days (sliding) | Strictly necessary — sign-in required for service |
| `altvisor_locale` | User-selected interface language | HttpOnly, Secure, SameSite=Lax, Path=/, 1 year | 1 year | Strictly necessary — user-selected preference |
| `altvisor_demo_session` | Per-session rate-limit bucket on the anonymous demo | HttpOnly, Secure, SameSite=Lax, Path=/api/v1/demo, 24 hours | 24 hours | Strictly necessary — abuse prevention |
| `altvisor_ga_consent` | Records whether the user accepted or rejected Google Analytics on marketing pages | Secure, SameSite=Lax, Path=/, 1 year | 1 year | Strictly necessary — records the user's own consent choice (CNIL guidance) |
| `ph_*` (PostHog) | Anonymous distinct-id + session identifier for product analytics & error tracking. EU project; ingestion proxied via /ingest/* (no third-party origin in the browser). | Secure, SameSite=Lax, Path=/ | 1 year (session id 30 min) | Legitimate interest — service-improvement analytics, EU-hosted, no advertising use; reviewed under GDPR Art. 6(1)(f) |
| `_ga`, `_ga_<container_id>` (Google Analytics 4) | Visitor identifier + session identifier on marketing pages only. Dropped only after user accepts via the marketing-page consent banner. | SameSite=Lax, Path=/, 2 years (`_ga`) / 90 days (`_ga_*`) | Per Google Analytics defaults | Consent (ePrivacy Art. 5(3)) — Consent Mode v2 default = denied; cookies only set on explicit Accept |
Analytics posture: PostHog runs on legitimate interest (EU-hosted, no advertising use, ingestion proxied through same origin). Google Analytics runs on consent only — Consent Mode v2 default `analytics_storage: 'denied'` means cookieless pings until the user accepts via the marketing-page banner. No advertising cookies, no third-party trackers beyond what is disclosed in §4.
8. Security measures
- All data in transit: TLS 1.3 (Caddy / Vercel edge / Supabase).
- All data at rest: Postgres encryption at rest (Supabase-managed).
- Authentication: Email + password (bcrypt via Supabase), federated OAuth (Google, GitHub), magic-link (Supabase OTP). Password minimum length: 12 characters.
- Authorisation: Row-Level Security on every customer-scoped table; service-role access restricted to signup bootstrap, webhooks, and `pg_cron` jobs.
- Audit log: append-only, `INSERT`-only RLS — `UPDATE` and `DELETE` blocked except via service role.
- Secret management: environment-variable injection at deploy time; no secrets in source.
- Vulnerability response: Dependabot weekly; Sentry error budget.
9. Subject rights
The processor will assist the controller in fulfilling subject-access, rectification, erasure, restriction, portability, and objection requests in line with Articles 12–22 GDPR. Tooling:
- Organisation hard-delete endpoint cascades through all customer-scoped tables and emits a final usage-report email.
- Per-user audit-log export endpoint (forthcoming with `add-audit-log-writer`).
- Personal-data export endpoint (forthcoming).
10. Incident notification
The processor notifies the controller without undue delay (no later than 72 hours) of any personal-data breach, as defined in Art. 4(12) GDPR, including the categories and approximate number of data subjects, the likely consequences, and the mitigation measures taken.
11. Signatures
| Party | Name | Title | Date | Signature |
|---|---|---|---|---|
| Controller | ||||
| Processor |
*This template is maintained in `apps/web/legal/dpa-template.md`. Any change to the subprocessor table must be made in lockstep with `apps/web/legal/subprocessors.ts` — the CI parity test will fail otherwise.*