Skip to main content

Compliance & Governance

Scope

This covers the organizational and legal layer of production AI in regulated industries: DPIA, data retention, legal review, model governance, audit log architecture, change management, and cost controls. These are the concerns that determine whether a regulated buyer signs the contract — and whether they renew it.


DPIA — Data Protection Impact Assessment

When a DPIA is mandatory (GDPR Article 35)

A DPIA is required before processing that is "likely to result in a high risk" to individuals. For AI systems in regulated industries, this means:

ScenarioDPIA required?
Processing health data, biometric data, or data about criminal convictionsYes — always
Automated decision-making with legal or similarly significant effectsYes — always
Large-scale processing of personal dataYes — always
Systematic monitoring of individualsYes
Processing of data of vulnerable persons (patients, children)Yes
AI-generated recommendations that affect insurance coverage or claimsYes
Internal RAG over employee dataLikely yes
RAG over public regulatory documents only (no personal data)No

For Project 3 (Insurance Copilot): DPIA is mandatory. Claims processing involves personal data + automated decision support with significant effects on individuals.

DPIA structure

## DPIA — Insurance Compliance Copilot v1.0

### 1. Description of the processing
**Purpose:** AI-assisted analysis of insurance claims and policy coverage to support underwriter decisions.
**Data categories:** policyholder name, address, date of birth, policy details, claim history, medical information (where applicable), financial information.
**Data subjects:** policyholders, claimants.
**Recipients:** underwriters (internal), compliance officers (internal), no external recipients.
**Retention:** [see Data Retention section].
**Transfers:** no cross-border transfers. Data stays within EU. [If using a Frontier API: DPA with vendor required; specify data processing location.]

### 2. Necessity and proportionality
**Purpose limitation:** data used only for claim analysis and policy coverage determination.
**Data minimisation:** only the fields required for the specific claim or query are retrieved. The full policyholder record is not loaded unless explicitly needed.
**Accuracy:** source data comes from the policy management system (single source of truth). AI outputs are recommendations, not final decisions — a human underwriter makes the final call.

### 3. Risk assessment

| Risk | Likelihood | Severity | Mitigation |
|------|------------|----------|------------|
| AI produces incorrect coverage recommendation → wrong claim decision | Medium | High | Human-in-the-loop mandatory for all decisions. AI output labeled as recommendation, not decision. |
| Indirect prompt injection via claim documents → data leak | Low | High | Content sanitization, marker tokens, output classifier, DLP |
| Tenant isolation failure → wrong policyholder data retrieved | Low | Critical | Per-tenant indexes, row-level filtering, quarterly penetration test |
| Data breach of vector store | Low | High | Encryption at rest, access controls, backup encryption |
| Excessive data retention → GDPR breach | Medium | Medium | Automated retention enforcement (see Data Retention) |
| Model bias → discriminatory decisions | Medium | High | Bias evaluation on demographic subgroups, mandatory human review |

### 4. Residual risks and measures
Residual risk after mitigations: **Low — acceptable for deployment**.
Measures taken: [list all mitigations above with implementation status].

### 5. DPO consultation
Consulted: [DPO name], [date]. Opinion: [summary].

### 6. Supervisory authority consultation
Required if residual risk remains high after measures. [Not required here — residual risk is Low.]

### 7. Review schedule
DPIA to be reviewed: annually, or when processing changes materially (new data categories, new model, new use cases).

**Approved by:** [Data Controller representative]
**Date:** YYYY-MM-DD
**Version:** 1.0

DPIA register

Maintain a DPIA register (required under GDPR Article 30 for large organizations):

| Processing activity | DPIA ref | Date | Status | Next review |
|---------------------|----------|------|--------|-------------|
| Insurance Copilot — claim analysis | DPIA-2026-001 | 2026-08-01 | Approved | 2027-08-01 |
| Knowledge Platform — employee queries | DPIA-2026-002 | 2026-09-01 | In review ||

Data Retention

Retention policy by data category

Data categoryRetention periodLegal basisDeletion method
User queries (text)12 monthsLegitimate interest (quality, eval)Hard delete from audit DB
LLM responses (text)12 monthsSameHard delete
Trace data (Langfuse)6 monthsOperational necessityLangfuse built-in TTL
Audit logs (access, decisions)7 years (insurance) / 5 years (GDPR baseline)Legal obligationImmutable archive, then delete
Model outputs used as training dataAs long as model is in production + 2 yearsLegitimate interestDelete from training set + re-fine-tune without
PII in corpus chunksUntil source document deleted + right-to-be-forgotten propagationContract / consentSee below
Backup dataMatch primary retention + 30 daysOperationalAutomated backup TTL
Kubernetes logs30 daysOperationalLog rotation
Security logs (auth, access)1 yearSecurity, legalImmutable archive, then delete

Retention periods for insurance data are jurisdiction-specific — confirm with legal counsel.

Automated retention enforcement

Retention must be automated — relying on manual deletion is an audit failure.

# Scheduled job: runs nightly, deletes expired records
async def enforce_retention():
cutoff_queries = datetime.utcnow() - timedelta(days=365)
deleted = await db.execute(
"DELETE FROM query_log WHERE created_at < :cutoff RETURNING id",
{"cutoff": cutoff_queries}
)
audit_log.write(
event="retention_enforcement",
table="query_log",
records_deleted=len(deleted),
cutoff=cutoff_queries.isoformat()
)
# Also delete associated embeddings and cached responses
await delete_associated_embeddings(deleted_ids=deleted)
await semantic_cache.invalidate_for_queries(deleted_ids=deleted)

Right-to-be-forgotten (GDPR Article 17)

When a data subject requests deletion of their data, the deletion must cascade through the entire AI pipeline:

Data subject request received → Legal validation (is the request valid?)

Identify all records for this subject (query_log, audit_log, corpus chunks)

Delete from source database

Delete all derived corpus chunks (WHERE source_doc_id IN (...))

Delete all embeddings (in vector DB, by document ID filter)

Invalidate semantic cache entries derived from these documents

Invalidate any prompt-cached responses that included this data

Log deletion event with timestamp and scope (for accountability)

Confirm to data subject within 30 days (GDPR deadline)

Test this propagation path in staging before any real data enters the system.


ItemWho reviewsTrigger
Data Processing Agreements (DPA) with LLM vendorsLegal + DPOBefore sending any personal data to a third-party API
Terms of Service for open model licensesLegalBefore using any open model in production
Client contracts (SLA, data processing, liability)LegalBefore onboarding first paying client
DPIADPO ± supervisory authorityBefore high-risk processing
AI Act risk classificationLegal + technicalBefore deploying any system in scope
Insurance-specific regulatory complianceCompliance officer + legalBefore any claim-affecting AI output
Employment law (if AI affects HR decisions)Legal + HRBefore any HR-related AI feature

DPA checklist for LLM vendors

When using a Frontier API (OpenAI, Anthropic, etc.) with personal data:

- [ ] DPA signed with the vendor
- [ ] Data processing location confirmed (EU or SCCs in place for US transfers)
- [ ] Vendor's sub-processor list reviewed and accepted
- [ ] Data retention by vendor confirmed (e.g., 30 days, 0 days with "no training" option)
- [ ] Security certifications verified (SOC 2 Type II, ISO 27001)
- [ ] Incident notification timeline confirmed (typically 72 hours)
- [ ] Right to audit or third-party audit report available

For Project 3 (sovereign, on-prem): no DPA needed for the LLM itself because data never leaves your infrastructure. DPAs still needed for any third-party monitoring tools, backup storage providers, etc.

Open model licensing review

Before deploying any open model in production:

| Model | License | Commercial? | Attribution required? | Training data restrictions? | Notes |
|-------|---------|-------------|----------------------|----------------------------|-------|
| Llama 3.x | Meta Community License | Yes (with terms) | No | No use for training competing models | Review 700M MAU clause |
| Qwen 3 | Apache-2.0 | Yes | Yes (in docs) | No | Cleanest license |
| Mistral | Apache-2.0 | Yes | Yes (in docs) | No | |
| DeepSeek-R1 | MIT | Yes | No | No | |

If you significantly fine-tune a model, legal must assess whether you become the AI Act "provider" of a new model — with full compliance obligations including conformity assessment.


Model Governance

Model registry

Every model used in production is registered. No unregistered model serves production traffic.

## Model Registry

| ID | Model name | Version | Purpose | Risk class | Deployed | Approved by | Review date |
|----|-----------|---------|---------|-----------|---------|-------------|------------|
| M-001 | Qwen3-8B | 3.0 | RAG generator (Project 3) | High-risk | 2026-09-01 | [name] | 2027-03-01 |
| M-002 | bge-large-en | 1.5 | Embedding (all projects) | Limited risk | 2026-08-01 | [name] | 2027-02-01 |
| M-003 | BGE-Reranker-v2 | 2.0 | Reranking (all projects) | Minimal risk | 2026-08-01 | [name] | 2027-02-01 |
| M-004 | Qwen3-4B | 3.0 | Judge / classifier | Limited risk | 2026-09-01 | [name] | 2027-03-01 |

AI Act risk classification

Every model or AI system must be classified before deployment:

Risk classDefinitionObligations
UnacceptableProhibited by AI Act (social scoring, real-time biometric surveillance in public)Do not build
High-riskSignificant impact on individuals: insurance decisions, credit, employment, healthcareConformity assessment, DPIA, human oversight, logging, registration in EU database
Limited riskTransparency obligation (e.g., must disclose it's AI-generated)Disclose AI nature to users
Minimal riskEverything elseNo mandatory obligations (voluntary codes of conduct)

Project 3 (Insurance Copilot): likely High-risk under Annex III (AI systems used for insurance decisions affecting individuals). Confirm with legal counsel.

High-risk obligations checklist:

- [ ] Technical documentation prepared (Article 11)
- [ ] Logging of system operation throughout lifecycle (Article 12)
- [ ] Transparency to users — AI nature disclosed (Article 13)
- [ ] Human oversight measures implemented (Article 14)
- [ ] Accuracy, robustness, cybersecurity measures (Article 15)
- [ ] Conformity assessment completed (Article 43)
- [ ] Registration in EU AI Act database (Article 51)
- [ ] Post-market monitoring plan (Article 61)

Model change control

No model change in production without going through change control:

## Model Change Request — MCR-2026-042

**Change:** Replace Qwen3-8B with Qwen3-14B for insurance copilot generator
**Requestor:** [name]
**Date:** 2026-10-01

### Impact assessment
- Risk class change: No (still High-risk)
- Performance change: +8% groundedness on golden set (see eval report)
- Latency change: +400ms p95 (requires SLO review)
- Cost change: +35% per query (requires FinOps review)
- VRAM change: 14B at INT8 requires 14GB vs 8GB (confirm GPU capacity)

### Eval evidence
- [ ] Golden set: 95.2% groundedness (was 88.1%) — improvement confirmed
- [ ] Adversarial set: 97.8% refusal rate (was 96.1%) — improvement confirmed
- [ ] Regression: no degradation on any existing test case
- [ ] Shadow mode: ran for 7 days, results statistically better (p < 0.05)

### Approval
- [ ] Technical lead approval
- [ ] DPO sign-off (model change with same data = no new DPIA needed)
- [ ] Compliance officer sign-off (AI Act technical documentation updated)
- [ ] CISO sign-off (supply chain: Cosign signature verified for new weights)

### Rollout plan
- [ ] Canary: 5% traffic for 7 days
- [ ] Promotion: 25% → 50% → 100% over 2 weeks
- [ ] Rollback criterion: groundedness drops below 92% during canary

**Approved:** [names + date]

Audit Log Architecture

What to log

Audit logs for regulated AI systems are not application logs — they are a legal record. They must be tamper-proof, queryable, and retained for the legally required period.

EventFields to log
User querytimestamp, user_id, tenant_id, query_hash (not raw text for PII), session_id
RAG retrievalquery_id, retrieved_doc_ids, retrieval_scores, retrieval_latency_ms
LLM responsequery_id, model_name, model_version, prompt_version, response_hash, groundedness_score, latency_ms, cost_usd
Decision recordquery_id, decision_type, decision_value, human_reviewer_id (if applicable), timestamp
User authenticationuser_id, ip_address, event_type (login/logout/failed), timestamp
Data accessuser_id, resource_type, resource_id, action (read/write/delete), timestamp
Model changemodel_id, old_version, new_version, changed_by, timestamp, mcr_id
Secret accesssecret_path, accessor_id, timestamp (from Vault audit log)
Admin actionadmin_id, action, target_resource, timestamp
Data deletion (retention enforcement)records_deleted, table, cutoff_date, triggered_by

Audit log storage and tamper-proofing

# Every audit entry is immutable — no UPDATE or DELETE
# Use an append-only table with a sequential integrity chain

async def write_audit_entry(event: AuditEvent) -> None:
# Get previous entry hash for chain integrity
prev_hash = await db.fetchval(
"SELECT entry_hash FROM audit_log ORDER BY id DESC LIMIT 1"
)
# Hash this entry chained to the previous
entry_json = event.model_dump_json()
entry_hash = hashlib.sha256(
f"{prev_hash}{entry_json}".encode()
).hexdigest()

await db.execute(
"""INSERT INTO audit_log (timestamp, event_type, payload, entry_hash)
VALUES ($1, $2, $3, $4)""",
event.timestamp, event.event_type, entry_json, entry_hash
)
# Audit entries are never updated or deleted within retention period

For Project 3: ship audit logs to Loki (append-only log store) with immutable log storage enabled. Loki's chunk hashing provides tamper evidence.

Audit log access controls

compliance-officer role → read access to audit_log (query, aggregate, export)
rag-admin role → read access to audit_log for their tenant only
rag-api service account → write access only (append, never read)
no role → delete, update (enforced by DB constraint: row-level security)

Audit trail for AI Act (Project 3)

Every AI-assisted decision must be reconstructable from the audit log:

-- Reconstruct what the AI saw and said for decision DEC-2026-4521
SELECT
al.timestamp,
al.payload->>'query' AS user_query,
al.payload->>'retrieved_docs' AS sources_retrieved,
al.payload->>'model_name' AS model_used,
al.payload->>'model_version' AS model_version,
al.payload->>'groundedness_score' AS groundedness,
al.payload->>'decision_value' AS ai_recommendation,
al.payload->>'human_reviewer_id' AS reviewed_by
FROM audit_log al
WHERE al.payload->>'decision_id' = 'DEC-2026-4521'
ORDER BY al.timestamp;

This query must be answerable at any time during the retention period. Regulators, DPOs, and courts may ask for it.


Change Management

Change categories

CategoryExamplesApproval requiredChange window
StandardPre-approved, low-risk, well-tested: routine dependency update, minor configTeam leadAny time
NormalPlanned changes: prompt update, model upgrade, new featureChange Advisory Board (CAB)Scheduled maintenance window
EmergencyP1 incident fix, critical security patchCISO + Engineering lead (async)Any time, post-hoc review
SignificantNew AI capability, new data source, new model risk classCAB + Compliance + LegalPlanned, with DPIA review if needed

Change Advisory Board (CAB)

For regulated industries, the CAB includes:

  • Engineering lead
  • Compliance officer
  • DPO (for changes affecting personal data processing)
  • Security lead
  • Business owner

CAB meeting: weekly, 30 minutes. Emergency changes can be approved async by quorum (≥ 3 of 5 members).

Change request template

## Change Request — CR-2026-087

**Title:** Update RAG answer prompt from v1.2 to v1.3
**Category:** Normal
**Requestor:** [name]
**CAB meeting:** 2026-10-15

### Description
Add instruction to always cite article numbers when referencing AI Act requirements.
No model change. No data change.

### Risk assessment
- Risk: Low — additive instruction change
- Rollback: flip prompt registry alias back to v1.2 (< 1 minute, no downtime)

### Evidence
- Eval results: groundedness 96.1% (was 95.8%), citation accuracy 98.2% (was 94.1%)
- Shadow mode: ran 3 days, no regressions

### Change window
2026-10-16 22:00–22:15 UTC (low traffic, 15 min)

### Rollback criteria
If groundedness drops below 92% in the first 24h → auto-rollback via feature flag

### Post-change validation
- [ ] 20 test queries run after deployment
- [ ] Grafana dashboard checked for anomalies
- [ ] On-call engineer monitoring for 30 minutes post-change

**CAB approval:** [names + date]

Maintenance windows

Scheduled maintenance must be communicated to clients per the SLA (typically 5 business days' notice):

## Maintenance Notice — 2026-10-16

**Window:** 2026-10-16 22:00–00:00 UTC (2 hours maximum)
**Expected downtime:** < 5 minutes (rolling restart)
**Changes:** Prompt update v1.3, dependency upgrades, PostgreSQL minor version update
**Rollback plan:** Automated, < 1 minute if needed
**Contact during maintenance:** [on-call contact]

Cost Controls

Cost governance structure

LevelOwnerMechanism
Per-requestApplicationToken cap per request, semantic cache, model cascade
Per-user / per-tenantApplicationDaily token budget per user, hard kill on overrun
Per-featureEngineering leadFeature-level budget tag, per-feature dashboard
Per-environmentEngineering leadDev/staging token quotas (10× lower than prod)
Total monthlyFinance + EngineeringCloud budget alert at 50%, 80%, 100% of monthly cap

Token budgets in code

# Per-request token cap
MAX_INPUT_TOKENS = 4096
MAX_OUTPUT_TOKENS = 1024

# Per-user daily budget (in USD)
async def check_user_budget(user_id: str) -> None:
spent_today = await budget_db.get_daily_spend(user_id)
if spent_today > settings.USER_DAILY_BUDGET_USD:
raise BudgetExceededError(
f"Daily query budget exceeded. Resets at midnight UTC."
)

# Per-tenant monthly budget
async def check_tenant_budget(tenant_id: str) -> None:
spent_this_month = await budget_db.get_monthly_spend(tenant_id)
limit = await tenant_db.get_budget_limit(tenant_id)
if spent_this_month > limit * 0.90:
await notify_tenant_admin(tenant_id, spent_this_month, limit)
if spent_this_month > limit:
raise TenantBudgetExceededError("Monthly AI budget exhausted. Contact your administrator.")

Cloud cost alerts (managed cloud)

# Azure / AWS budget alert (via Terraform)
resource "aws_budgets_budget" "rag_monthly" {
name = "rag-platform-monthly"
budget_type = "COST"
limit_amount = "5000"
limit_unit = "USD"
time_unit = "MONTHLY"

notification {
comparison_operator = "GREATER_THAN"
threshold = 50
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["finops@company.com", "engineering-lead@company.com"]
}

notification {
comparison_operator = "GREATER_THAN"
threshold = 90
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = ["finops@company.com", "cto@company.com"]
}
}

Showback and chargeback

From Stage 3 (multi-tenant):

  • Showback: show each tenant their monthly AI cost (no billing adjustment — transparency only)
  • Chargeback: bill each tenant for their actual AI cost (requires metering infrastructure)
-- Monthly cost report per tenant
SELECT
tenant_id,
SUM(input_tokens) AS total_input_tokens,
SUM(output_tokens) AS total_output_tokens,
SUM(cost_usd) AS total_cost_usd,
COUNT(*) AS total_queries,
AVG(cost_usd) AS avg_cost_per_query
FROM query_cost_log
WHERE created_at >= date_trunc('month', now())
GROUP BY tenant_id
ORDER BY total_cost_usd DESC;

FinOps dashboard metrics

Track weekly:

  • Total AI cost (by model, by feature, by tenant)
  • Cost per resolved query (target: < €0.05)
  • Cache hit rate (target: > 60%)
  • Cascade routing ratio (% of queries served by SLM vs Frontier)
  • Wasted spend (queries that failed or were refused — cost with no value delivered)
  • Month-over-month cost trend vs query volume trend (efficiency ratio)

A cost that grows faster than query volume means you are getting less efficient — investigate.