Technical and organisational measures (GDPR Art. 32) ​
Status: Template — to be reviewed by the controller's DPO / counsel. Draft of 2026-10-13; not reviewed yet.
This document describes the measures built into CodeLotse and, for the hosted edition, the measures Quavon UG (haftungsbeschränkt) applies as processor. It is Annex "TOMs" of the data processing agreement and the Art. 30(1)(g) / 30(2)(d) reference of the RoPA.
- Product measures are in the code and apply to every installation. Each has evidence: a test, a configuration file or a design record.
- Operational measures apply to the hosted edition. The hosted edition is not yet in operation; these are marked planned until they are in place. In the self-hosted edition the customer is responsible for them.
Status values: in place (implemented and tested), partial, planned.
1. Encryption and pseudonymisation (Art. 32(1)(a)) ​
| Measure | Status | Evidence |
|---|---|---|
| Credentials (Git host app keys and secrets, model API keys, OIDC client secrets, ICS feed URLs) encrypted with AES-256-GCM, bound to their row, with key ids and rotation | in place | crypto.test.ts, encrypted-columns.test.ts, keys.int.test.ts, ADR-0008 |
| The master key is never stored in the database; supplied by environment or file | in place | ADR-0008 |
| Session and CSRF tokens stored only as SHA-256 hashes; Git host user tokens encrypted and deleted at logout or expiry | in place | auth.int.test.ts |
| Sandbox snapshots encrypted per blob and deleted after the job | in place | snapshot.test.ts |
| TLS for browser traffic (Caddy) and for all outbound calls (https required for model and Git host endpoints except explicitly private model hosts) | in place | Caddyfile, endpoint-guard.test.ts |
| Data subject requests are audited with a hash of the subject, not the subject in clear | planned (DSAR) | quavon dsar |
| Encryption at rest of disks and backups (hosted) | planned | ISMS record |
2. Confidentiality (Art. 32(1)(b)) ​
| Measure | Status | Evidence |
|---|---|---|
Tenant isolation by Postgres row-level security (ENABLE and FORCE) on every tenant table; the application role cannot bypass it | in place | rls.int.test.ts, ADR-0002 |
| System-wide access only in named operations from allowlisted modules (lint rule) | in place | ADR-0006 |
| Every API route declares a permission or the API refuses to start; route-table test checks permission and audit for every route | in place | contract.test.ts, routes.int.test.ts |
| Roles derived from the Git host or from identity-provider group mappings; deny by default | in place (including OIDC) | ADR-0007 |
| Sessions limited to 8 hours; HttpOnly, Secure, SameSite cookies; CSRF double-submit | in place | auth.int.test.ts |
| Secrets masked in responses and redacted from logs; no request bodies or tokens logged | in place | redact.test.ts |
| Prompt logs off by default; on opt-in redacted, encrypted, ≤ 7 days | in place | data inventory |
| Minimisation: availability stored as from/to only; no calendar contents; no per-person scores; analytics only as team aggregates with k ≥ 5 | in place (including analytics) | ADR-0014, privacy tests |
Untrusted repository code runs only in a sandbox (gVisor runsc or Firecracker) without tokens or network | in place | escape.int.test.ts, ADR-0012 |
| Valkey access separated per component by ACL users | in place | valkey-users.acl |
| Hosted operations staff: named accounts, confidentiality commitments, break-glass access only with audit and customer approval | planned | ISMS record |
3. Integrity (Art. 32(1)(b)) ​
| Measure | Status | Evidence |
|---|---|---|
| Webhooks verified by HMAC-SHA256 over the raw body before any database write | in place | webhooks.int.test.ts |
| Append-only, hash-chained audit log for every mutation, with actor, target and client IP | in place | audit.int.test.ts, audit.int.test.ts (API) |
| Only the publisher writes to the Git host; outputs are validated, sanitised (no raw HTML, images, foreign links, mass mentions) and secret-scanned | in place | sanitize.test.ts, publisher.int.test.ts |
| Content from repositories, issues and model outputs treated as untrusted (prompt-injection containment) | in place | untrusted.test.ts, threat model |
| Supply chain: exact-pinned dependencies, licence gate, secret scanning, vulnerability scanning, CycloneDX SBOMs, image scanning | in place (defined in CI; first CI run pending) | ci.yml, licenses.test.ts, secrets.test.ts, vulns.test.ts |
4. Availability and resilience (Art. 32(1)(b)) ​
| Measure | Status | Evidence |
|---|---|---|
| Transactional outbox and idempotent handlers; no lost or duplicated effects on crashes | in place | chaos.int.test.ts |
| Rate limits per IP and session; webhook failure limits; break-glass fails closed when Valkey is down | in place | security.int.test.ts |
| Budgets per tenant and repository for model costs; tool-loop limits | in place | budget.test.ts |
| Containers non-root, read-only root filesystem, all capabilities dropped | in place | docker-compose.yml |
Health checks and diagnostics (quavon doctor) | in place | doctor.md |
| Monitoring, alerting and on-call for the hosted service | planned (observability pack) | ISMS record |
5. Restore after an incident (Art. 32(1)(c)) ​
| Measure | Status | Evidence |
|---|---|---|
| Backups of Postgres and restore procedure | planned (backup and restore commands are open) | operations.md |
| Restore drills at least twice a year (hosted) | planned | ISMS record |
| Incident response and PSIRT runbook | in place | psirt.md |
6. Regular testing and evaluation (Art. 32(1)(d)) ​
| Measure | Status | Evidence |
|---|---|---|
| Threat model reviewed with every change that adds an entry point, write path or agent capability; every mitigation mapped to a test or control | in place | threat-model.md |
| Red-team suite and tenant-isolation fuzzing | partial | threat-model.md |
| Independent penetration test | planned | — |
| Review of these measures at least yearly and after significant changes | planned | ISMS record |
7. Data protection by design and by default (Art. 25) ​
- Features that process people's data more intensively (assignment, analytics self view, availability, prompt logs, product telemetry) are off by default and need an explicit opt-in.
- Assignment stays in shadow until counsel and, for German tenants, the works council sign off (ADR-0014).
- Retention jobs run daily; defaults are in the data inventory.
Open points for the DPO / counsel ​
- Backup and restore are not yet available; the hosted edition must not start without them.
- Operational measures (staff, access reviews, monitoring) are planned and must be in place before the hosted edition processes customer data.