Terug naar Journal
9 min leestijd

Client-Side AI Governance: Architectuurpatronen voor Beleidshandhaving, C2PA-Herkomst en DLP in Next.js

Een diepgaande gids over het inbedden van productie-AI-governance in Next.js-frontends, waaronder client-side DLP, OWASP LLM-uitvoersanering, EU AI Act C2PA-herkomstondertekening en audittelemetrie.

AI GovernanceNext.jsC2PASecurityEU AI ActTypeScriptReact
Client-Side AI Governance: Architectuurpatronen voor Beleidshandhaving, C2PA-Herkomst en DLP in Next.js

Nu generatieve AI-functies en in-browser inferentiemodellen (zoals WebLLM, Vercel AI SDK en lokale worker-embeddings) standaard worden in moderne webapplicaties, is AI-governance rechtstreeks verplaatst naar de frontend-stack. Beveiliging is niet langer uitsluitend de verantwoordelijkheid van een backend-gateway; client-side applicaties moeten strikte Data Leakage Prevention (DLP) afdwingen, niet-deterministische modeluitvoer valideren tegen injectiekwetsbaarheden, voldoen aan regelgeving zoals de EU AI Act Artikel 50, en gestructureerde audittelemetrie bieden.

Vanaf augustus 2026 verplicht de EU AI Act expliciet machinaal leesbare, interoperabele openbaarmakingen en herkomstmarkering voor AI-gegenereerde inhoud. Voor webengineeringteams vereist het voldoen aan deze wettelijke en beveiligingsverwachtingen een gestructureerde defense-in-depth-architectuur die rechtstreeks is ingebed in onze React- en Next.js-applicaties.

In deze gids onderzoeken we een 4-laagse client-side AI-governancelaag gebouwd voor Next.js App Router-applicaties, compleet met TypeScript-implementaties voor prompt-DLP, op Zod gebaseerde streaming-beveiligingsrails, C2PA-metagegevensondertekening en privacyvriendelijke OpenTelemetry-auditlogs.


De 4-Laagse Frontend AI-Governancelaag

Om de sessiestatus van gebruikers te beschermen, OWASP Top 10 voor LLM-kwetsbaarheden (in het bijzonder LLM01: Prompt Injection en LLM05: Onjuiste Uitvoerbehandeling) te voorkomen en aan transparantieverplichtingen te voldoen, verdeelt onze frontend-architectuur governance in vier afzonderlijke lagen:

┌───────────────────────────────────────────────────────────────────────┐
│                    Next.js App Router Application                     │
│                                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Tier 1: Client DLP & Input Sanitization                         │  │
│  │ ├─ Regex entropy scanning & PII redaction                       │  │
│  │ └─ Prompt injection detection heuristics                        │  │
│  └────────────────────────────────┬────────────────────────────────┘  │
│                                   │                                   │
│                                   ▼                                   │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Tier 2: Edge Guardrail & Output Sanitizer                       │  │
│  │ ├─ Zod schema stream validation                                 │  │
│  │ └─ DOMPurify AST filter & XSS containment                       │  │
│  └────────────────────────────────┬────────────────────────────────┘  │
│                                   │                                   │
│                                   ▼                                   │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Tier 3: C2PA Provenance & Watermark Injector                    │  │
│  │ ├─ Article 50 manifest digital signing                          │  │
│  │ └─ Machine-readable visual provenance badge                     │  │
│  └────────────────────────────────┬────────────────────────────────┘  │
│                                   │                                   │
│                                   ▼                                   │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Tier 4: Privacy-Preserving Audit Telemetry                      │  │
│  │ ├─ SHA-256 context hashing & model latency metrics              │  │
│  │ └─ Structured OpenTelemetry audit logger                        │  │
│  └─────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────┘

Laag 1: Client-Side Data Leakage Prevention (DLP)

Voordat een gebruikersprompt naar een LLM-provider of lokale WebGPU-engine wordt gestuurd, moet deze client-side worden geëvalueerd om te voorkomen dat gevoelige referenties (API-sleutels, creditcards, JWT-tokens, PII) de browsersandbox van de gebruiker verlaten.

We bouwen een lichtgewicht React Hook (useSanitizedPrompt) die op entropie gebaseerde geheimscanning en reguliere expressiemaskers uitvoert voorafgaand aan verzending via het netwerk:

import { useMemo, useState } from "react";

interface PIIPattern {
  name: string;
  regex: RegExp;
  replacement: string;
}

const DEFAULT_PATTERNS: PIIPattern[] = [
  {
    name: "API_KEY",
    regex: /(?:sk|pk)_(?:live|test)_[0-9a-zA-Z]{24,32}/g,
    replacement: "[REDACTED_API_KEY]"
  },
  {
    name: "BEARER_TOKEN",
    regex: /Bearer\s+[A-Za-z0-9\-\._~\+\/]+=*/g,
    replacement: "Bearer [REDACTED_TOKEN]"
  },
  {
    name: "EMAIL",
    regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
    replacement: "[REDACTED_EMAIL]"
  },
  {
    name: "CREDIT_CARD",
    regex: /\b(?:\d[ -]*?){13,16}\b/g,
    replacement: "[REDACTED_CARD]"
  }
];

// Shannon Entropy check to catch high-randomness string tokens
function calculateShannonEntropy(str: string): number {
  const map: Record<string, number> = {};
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    map[char] = (map[char] || 0) + 1;
  }
  let entropy = 0;
  for (const char in map) {
    const p = map[char] / str.length;
    entropy -= p * Math.log2(p);
  }
  return entropy;
}

export function useSanitizedPrompt() {
  const [violations, setViolations] = useState<string[]>([]);

  const sanitize = (rawInput: string): { cleanPrompt: string; isSafe: boolean } => {
    let cleanPrompt = rawInput;
    const detectedViolations: string[] = [];

    // 1. Apply RegEx Redaction
    for (const pattern of DEFAULT_PATTERNS) {
      if (pattern.regex.test(cleanPrompt)) {
        detectedViolations.push(pattern.name);
        cleanPrompt = cleanPrompt.replace(pattern.regex, pattern.replacement);
      }
    }

    // 2. High Entropy Word Detection (Catch secrets not matching fixed regex)
    const words = cleanPrompt.split(/\s+/);
    const sanitizedWords = words.map((word) => {
      if (word.length > 20 && calculateShannonEntropy(word) > 4.5) {
        detectedViolations.push("HIGH_ENTROPY_TOKEN");
        return "[REDACTED_HIGH_ENTROPY_TOKEN]";
      }
      return word;
    });

    cleanPrompt = sanitizedWords.join(" ");
    setViolations(detectedViolations);

    return {
      cleanPrompt,
      isSafe: detectedViolations.length === 0
    };
  };

  return { sanitize, violations };
}

Laag 2: Streaming-Uitvoerbeveiliging en XSS-Inperking

OWASP LLM05 (Onjuiste Uitvoerbehandeling) treedt op wanneer AI-modeluitvoer rechtstreeks in de UI wordt gerenderd zonder DOM-sanering, wat leidt tot opgeslagen of gereflecteerde Cross-Site Scripting (XSS).

In Next.js App Router-applicaties moeten gestreamde LLM-voltooiingen worden gevalideerd tegen een strikt Zod-schema op de server- of edge-route, terwijl gerenderde HTML-componenten AST-fragmenten verwerken via een geconfigureerde DOMPurify-pipeline:

import { z } from "zod";
import DOMPurify from "isomorphic-dompurify";

// Define strict output expectations for structured AI component responses
export const AIComponentResponseSchema = z.object({
  componentName: z.string().max(64),
  summary: z.string().max(500),
  actionPayload: z.record(z.unknown()).optional(),
  renderedHtml: z.string().transform((val) =>
    DOMPurify.sanitize(val, {
      ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "code", "pre", "p", "ul", "li"],
      ALLOWED_ATTR: ["href", "target", "rel", "class"],
      ALLOW_DATA_ATTR: false
    })
  )
});

export type AIComponentResponse = z.infer<typeof AIComponentResponseSchema>;

export async function validateAndSanitizeStream(rawChunk: unknown): Promise<AIComponentResponse> {
  const parseResult = AIComponentResponseSchema.safeParse(rawChunk);
  
  if (!parseResult.success) {
    throw new Error(`AI Governance Guardrail Violation: ${parseResult.error.message}`);
  }
  
  return parseResult.data;
}

Laag 3: EU AI Act Artikel 50 en C2PA-Herkomstondertekening

Artikel 50 van de EU AI Act vereist dat webapplicaties die synthetische inhoud genereren (tekst, audio, code of afbeeldingen) machinaal leesbare metagegevens invoegen en duidelijke visuele openbaarmakingen tonen.

1. Server-Side C2PA-Manifestondertekening (Server Action)

We ondertekenen gegenereerde activa op het moment van aanmaak door een C2PA-manifest te bouwen met claimdetails, generatormetagegevens en digitale handtekeningen:

"use server";

import { createC2PAManifest } from "@contentauth/c2pa-node";

export interface AIContentProvenanceOptions {
  modelName: string;
  promptHash: string;
  authorOrg: string;
}

export async function signGeneratedAssetProvenance(
  assetBuffer: Buffer,
  options: AIContentProvenanceOptions
): Promise<Buffer> {
  const manifest = createC2PAManifest({
    claim_generator: `${options.authorOrg}/NextJS-AIGovernanceEngine/1.0`,
    title: "Generative AI Output Asset",
    assertions: [
      {
        label: "c2pa.actions",
        data: {
          actions: [
            {
              action: "c2pa.created",
              digitalSourceType: "https://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
            }
          ]
        }
      },
      {
        label: "org.eu.aiact.compliance",
        data: {
          article: "Article 50",
          model_name: options.modelName,
          prompt_sha256: options.promptHash,
          timestamp: new Date().toISOString()
        }
      }
    ]
  });

  // Embed signed manifest into binary buffer
  const signedBuffer = await manifest.embedIntoBuffer(assetBuffer);
  return signedBuffer;
}

2. Toegankelijke UI Herkomstbadge-Component

Elk door AI gegenereerd UI-blok moet een permanente, onopvallende badge tonen waarmee gebruikers herkomstgegevens kunnen verifiëren:

"use client";

import React, { useState } from "react";

interface ProvenanceBadgeProps {
  modelName: string;
  timestamp: string;
  isCompliant: boolean;
}

export const ContentProvenanceBadge: React.FC<ProvenanceBadgeProps> = ({
  modelName,
  timestamp,
  isCompliant
}) => {
  const [showModal, setShowModal] = useState(false);

  return (
    <div className="inline-flex items-center gap-2 text-xs font-mono text-neutral-600 bg-neutral-100 dark:bg-neutral-800 px-2.5 py-1 rounded-md border border-neutral-200 dark:border-neutral-700">
      <span className="inline-block w-2 h-2 rounded-full bg-emerald-500" aria-hidden="true" />
      <span>AI Generated ({modelName})</span>
      <button
        onClick={() => setShowModal(!showModal)}
        className="underline hover:text-neutral-900 dark:hover:text-neutral-100 transition-colors ml-1"
        aria-label="View C2PA Provenance Metadata"
      >
        Verify C2PA
      </button>

      {showModal && (
        <div
          role="dialog"
          aria-modal="true"
          aria-label="Provenance Metadata Details"
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
        >
          <div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-lg max-w-md w-full p-6 shadow-xl text-left">
            <h3 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100 mb-2">
              EU AI Act Article 50 Provenance Card
            </h3>
            <div className="space-y-2 text-xs text-neutral-600 dark:text-neutral-300 font-mono">
              <p><strong>Generator Model:</strong> {modelName}</p>
              <p><strong>Generation Timestamp:</strong> {timestamp}</p>
              <p><strong>C2PA Standard:</strong> Manifest V1.3 Signed</p>
              <p><strong>Compliance Status:</strong> {isCompliant ? "VERIFIED_COMPLIANT" : "UNVERIFIED"}</p>
            </div>
            <div className="mt-4 flex justify-end">
              <button
                onClick={() => setShowModal(false)}
                className="px-3 py-1.5 bg-neutral-900 text-white dark:bg-neutral-100 dark:text-neutral-900 rounded text-xs font-medium"
              >
                Close
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

Laag 4: Onveranderlijke Audit en Telemetrie-Redactie

Voor naleving in de onderneming en onderzoek na incidenten moet elke AI-interactie gestructureerde logs uitsturen. Het is van cruciaal belang dat de telemetrie de uitvoering van governance vastlegt zonder de invoer van gebruikers in platte tekst of persoonlijke identificatiegegevens op te slaan.

We bouwen een cryptografische audit-logger met behulp van de Web Crypto API om prompts te hashen en telemetrie-gebeurtenissen te registreren:

export interface GovernanceAuditEvent {
  sessionId: string;
  modelIdentifier: string;
  promptHash: string;
  dlpViolations: string[];
  outputSanitized: boolean;
  latencyMs: number;
  timestamp: string;
}

export async function createGovernanceAuditEvent(
  sessionId: string,
  modelIdentifier: string,
  rawPrompt: string,
  dlpViolations: string[],
  outputSanitized: boolean,
  latencyMs: number
): Promise<GovernanceAuditEvent> {
  // Compute SHA-256 hash of raw prompt to prevent PII log persistence
  const encoder = new TextEncoder();
  const data = encoder.encode(rawPrompt);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const promptHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");

  const auditEvent: GovernanceAuditEvent = {
    sessionId,
    modelIdentifier,
    promptHash,
    dlpViolations,
    outputSanitized,
    latencyMs,
    timestamp: new Date().toISOString()
  };

  // Dispatch to OpenTelemetry endpoint or internal log aggregator
  if (process.env.NODE_ENV === "production") {
    fetch("/api/telemetry/governance", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(auditEvent)
    }).catch((err) => console.error("Failed to ship governance audit log:", err));
  }

  return auditEvent;
}

Prestaties en Renderbudgetten

Het toevoegen van client-side DLP-scans, Zod-streamvalidatie en C2PA-metagegevensanalyse introduceert een runtime-overhead. Om een vloeiende 60fps UI-ervaring te behouden, zijn strikte prestatiebudgetten vereist:

1. Beperking van DLP-Overhead

Het scannen met reguliere expressies en het berekenen van de Shannon-entropie over een prompt van 2.000 woorden duurt ~1,8 ms op moderne V8-engines. Om invoervelden responsief te houden, voert u useSanitizedPrompt uit in een debounced callback of verplaatst u zware entropiescans naar een Web Worker.

2. Budgetten voor Bundelgrootte

  • DOMPurify (isomorfe versie): ~9.2kB gzipped
  • Zod: ~12.4kB gzipped
  • @contentauth/c2pa Node SDK: Wordt strikt op de server gehouden (Server Actions / API Routes) om te voorkomen dat zware WASM-cryptografische bibliotheken naar clientbundels worden gestuurd. De clientcomponent (ContentProvenanceBadge) weegt < 2kB.

3. Overzicht van Latentiestatistieken

Governance Tier Runtime Location Average Latency Impact Bundle Impact (Gzipped)
Tier 1: Client DLP React Hook / Client 1.2ms - 2.5ms ~1.5kB
Tier 2: Output Sanitizer Edge Middleware / Server 3.0ms - 8.0ms ~21.6kB (Server/Shared)
Tier 3: C2PA Signing Server Action 15.0ms - 45.0ms 0kB (Server Only)
Tier 4: Audit Telemetry Async Fetch / Background 0ms (Non-blocking) ~0.8kB

Samenvatting

Frontend AI-governance is een essentiële engineeringdiscipline voor moderne webapplicaties. Door client-side DLP-scanning in te bedden, Zod-schema-beveiligingsrails toe te passen, uitvoerbestanden te ondertekenen met C2PA-metagegevens voor de naleving van de EU AI Act en geanonimiseerde auditlogs uit te sturen, kunnen frontendteams veilige, veerkrachtige en volledig conforme door AI aangedreven gebruikersinterfaces bouwen.

Share this article