Skip to content
NLEN
Illustration: Aligning log retention with zero data retention

Aligning log retention with zero data retention

By Ivo Donker — compiled with AI assistance · August 7, 2026

In production environments, conflicting design requirements regularly arise as soon as an organization integrates LLM APIs. On the one hand, a strict privacy architecture requires that no privacy-sensitive data or intellectual property remain on external servers. On the other hand, compliance frameworks and incident protocols require that all processed transactions be demonstrable, traceable, and auditable. This leads to an apparent contradiction between minimizing data storage and building an airtight audit trail.

Zero data retention guarantees that the API provider does not permanently store the content of prompts and generated answers on its infrastructure. See zero data retention API configurations for setting the retention parameters on the LLM provider's side. Audit logging involves immutably recording system transactions to guarantee accountability and legal evidentiary value. See audit logging and compliance for the legal burden of proof and retention requirements within AI applications. To resolve this conflict, the architecture must draw a sharp distinction between what the provider retains and what your own API gateway processes locally. See managing API keys securely to align access rights to specific log files with the authorizations of your API keys.

This article does not cover configuring zero data retention at a provider itself; see zero data retention API configurations. This article does not work out the legal reasoning behind retention periods; see the GDPR privacy checklist on gids.llmnet.nl.

The actual scope of zero data retention versus local management

When an organization activates Zero Data Retention (ZDR), whether contractually or via API parameters, this only relates to the storage of the unstructured payload at the external provider. The provider removes the actual text input (prompts) and the text generated by the model (completions) directly from volatile memory as soon as the HTTP response is complete. ZDR prevents this data from being used for model training or persistently stored on the vendor's disk units.

However, ZDR does not mean the external provider records nothing at all. From an operational and financial standpoint, providers always keep a minimal set of infrastructure data (illustrative, as of August 2026):

The misconception that ZDR automatically leads to a completely log-free chain is a compliance risk. The responsibility for meeting retention obligations and privacy rules shifts entirely from the provider to your own infrastructure through ZDR. Anything the provider no longer stores must be processed by your own API gateway in a secure and controlled way. When response caching is used on your own gateway, this data falls under additional retention rules. See caching LLM responses to determine which retention category temporarily stored model responses fall under.

To prevent your own gateway from unintentionally permanently storing sensitive data or personal data (PII) in temporary log files, an active separation must be established at the edge of the network. See input validation and output filtering to filter harmful payloads and PII before they reach the logging pipeline.

Architectural separation: observability versus audit logging

To meet both the requirement of minimal data storage and the requirement of demonstrable control, a strict architectural separation between observability signals and audit logs is necessary. Combining these two functions in one central log database leads to privacy risks or unmanageable storage costs.

Observability signals focus on the operational health of the application and the runtime performance of the LLM integration. Read observability and logging for setting up operational performance tracking and error tracing. This data is volatile in nature, has a high density, and mainly contains metrics, response times (latency), and aggregated error rates. Observability logs may and should be automatically cleaned up after a short period.

Audit logging, on the other hand, serves a legal and organizational purpose: providing irrefutable proof that a specific action was performed at a specific moment by an authorized user or entity. Audit logs must be immutable, equipped with cryptographic integrity checks, and support a long retention period. In multi-tenant environments, the log structure must also prevent data from different end customers from getting mixed up. See multi-tenant LLM apps to learn how to strictly isolate log streams per end customer from each other.

The three retention streams: analysis by production pattern

A robust log architecture divides all outgoing and incoming telemetry into three separate streams. Each stream has its own failure mode, detection mechanism, mitigation strategy, and cost profile.

Stream 1: Operational log stream (observability & diagnostics)

The operational log stream collects telemetry data to check whether the LLM integration is functioning correctly, what the average response time is, and where any latency spikes occur.

Stream 2: Audit and compliance log stream (accountability)

The audit log stream records who initiated which category of LLM processing at what moment and what the legal or contractual basis for it was.

Stream 3: Security and incident response log stream (forensic & anomaly detection)

The security log stream records detailed interaction patterns to be able to analyze and reconstruct security incidents, such as API key abuse, data leakage, or advanced prompt injection attacks.

Pseudonymization and encryption as a bridge

To resolve the conflict between "keep nothing" and "demonstrably prove," encryption and pseudonymization form the technical bridge. By transforming data on your own API gateway before storage, the requirements of data minimization are met without losing the evidentiary value of the audit trail.

The two techniques below form the foundation of this bridge:

  1. Salted HMAC hashing for audit traceability: Hashing the input text with a secret key (HMAC-SHA256) creates a unique fingerprint. If a user later claims a specific request was not submitted by them, the organization can re-hash the input and compare it with the hash in the WORM audit log. The original text can, however, never be recovered from the hash.
  2. Envelope encryption for forensic storage: The payload is encrypted immediately upon receipt with a unique symmetric data key (AES-GCM-256). This data key is then encrypted with a master key from a Key Management Service (KMS). Only in the event of a formal security incident is the key released to decrypt the specific transaction for forensic investigation.

Implementation: fault-tolerant log classification and audit writer

The provider-independent pseudocode below demonstrates a fault-tolerant implementation of a log processor on an API gateway. The code splits incoming LLM transactions into the three defined streams, applies hashing and encryption, and includes explicit handling for network timeouts and processing errors. When a secondary log stream fails, the primary LLM stream remains operational, while the error is caught without data loss in the audit trail.

// Provider-onafhankelijke pseudocode voor geclassificeerde audit-logging
import { computeHMAC, encryptPayload, sendToWORM, sendToAPM } from "gateway-crypto-lib";

interface LLMTransaction {
    transactionId: string;
    userId: string;
    tenantId: string;
    promptText: string;
    completionText: string;
    promptTokens: number;
    completionTokens: number;
    durationMs: number;
    statusCode: number;
}

async function processLLMLogs(transaction: LLMTransaction, config: GatewayConfig): Promise<void> {
    const TIMEOUT_MS = 1500;
    
    // 1. STROOM 1: Operationele Telemetrie (Gestript van payload)
    const operationalRecord = {
        transactionId: transaction.transactionId,
        tenantId: transaction.tenantId,
        promptTokens: transaction.promptTokens,
        completionTokens: transaction.completionTokens,
        durationMs: transaction.durationMs,
        statusCode: transaction.statusCode,
        timestamp: new Date().toISOString()
    };

    // Fast-fire operationele log (non-blocking)
    sendToAPM(operationalRecord).catch(err => {
        console.error("APM Logging waarschuwing: opslag mislukt", err.message);
    });

    // 2. STROOM 2: Audit-log (Gehasht & Uniek)
    const promptHash = computeHMAC(transaction.promptText, config.hmacSecret);
    const completionHash = computeHMAC(transaction.completionText, config.hmacSecret);

    const auditRecord = {
        transactionId: transaction.transactionId,
        userIdHash: computeHMAC(transaction.userId, config.hmacSecret),
        tenantId: transaction.tenantId,
        promptHash: promptHash,
        completionHash: completionHash,
        tokenCountTotal: transaction.promptTokens + transaction.completionTokens,
        timestamp: new Date().toISOString()
    };

    // Asynchrone schrijfactie naar WORM met timeout-beveiliging
    try {
        await Promise.race([
            sendToWORM(auditRecord),
            new Promise((_, reject) => 
                setTimeout(() => reject(new Error("WORM Storage Timeout")), TIMEOUT_MS)
            )
        ]);
    } catch (error) {
        // FOUTPAD: Audit-mislukking mag de API-response niet blokkeren, 
        // maar moet worden omgeleid naar een lokale nood-queue op schijf.
        console.error("CRITICAL: Audit-log kon niet naar WORM worden geschreven:", error.message);
        await emergencyDiskQueue.enqueue({
            type: "AUDIT_FAILURE_BACKUP",
            record: auditRecord,
            failedAt: new Date().toISOString(),
            reason: error.message
        });
    }

    // 3. STROOM 3: Security-log (Versleutelde Payload)
    if (config.securityLoggingEnabled) {
        try {
            const encryptedBody = await encryptPayload(
                JSON.stringify({
                    prompt: transaction.promptText,
                    completion: transaction.completionText
                }),
                config.kmsPublicKey
            );

            await sendToSecurityVault({
                transactionId: transaction.transactionId,
                encryptedData: encryptedBody,
                expiresAt: Date.now() + (90 * 24 * 60 * 60 * 1000) // 90 dagen TTL
            });
        } catch (secError) {
            console.error("SECURITY_LOG_ERROR: Encrypted payload backup mislukt", secError.message);
            // Vallen niet stil: veiligheidslog-fouten genereren een alert in de SIEM-pijplijn
            emitSIEMAlert("SECURITY_LOG_DISRUPTION", { transactionId: transaction.transactionId });
        }
    }
}

Retention matrix: combined retention schedule

The table below shows the combined retention overview that satisfies both the conditions of zero data retention at the provider and the internal and legal compliance requirements on your own infrastructure.

Log stream Content & payload Retention period Storage class Access regime ZDR compatibility
Operational Metadata, status codes, latency, token counts. No text. 14 - 30 days Standard index / APM storage DevOps & SRE teams Fully compatible (contains no prompt data)
Audit / compliance Hashes of prompt/completion, timestamps, hashed user ID. 1 - 7 years WORM object storage (S3 Lock mode) Compliance & external auditors Fully compatible (irreversible hashes)
Security / forensic Encrypted full payload (AES-256-GCM via KMS). 90 - 180 days Encrypted cold storage / vault Security officer + 4-eyes protocol Compatible provided storage is kept away from the provider
Provider-side (ZDR) Only aggregated billing measurements and IP telemetry. Per provider SLA (usually 0-30 days) Provider cloud platform Provider SRE & billing systems Standard ZDR guarantee at provider

Implementation checklist for production environments

Follow this step-by-step plan to align log retention on your own API gateway with the ZDR settings of the external LLM provider:

  1. Verify the ZDR status at the provider: Check contractually and via the API headers whether Zero Data Retention is active for the specific endpoint keys.
  2. Configure payload stripping at the gateway: Ensure the API gateway removes all prompt and completion fields from the primary application logs by default, before they are sent to log aggregators.
  3. Set up the asynchronous audit pipeline: Implement a separate, asynchronous message broker that builds audit records based on HMAC hashing.
  4. Enable WORM protection: Activate S3 Object Lock or a similar immutable storage method for the audit bucket, and set the retention period in accordance with the outcomes of the DPIA.
  5. Implement envelope encryption for incident response: If the risk profile requires forensic reconstruction, set up automated asymmetric encryption for temporary security logs.
  6. Validate error paths and emergency queues: Test whether the system correctly falls back to a secure local emergency queue if the audit database fails, without the API call to the end user blocking.
  7. Run periodic hashing audits: Check quarterly whether the hashed data in the audit log can still be consistently verified against the source data from the primary databases.