Passa al contenuto principale

Inference Hooks: DLP inline per Claude Enterprise

Avanzato
What you'll learn
  • Cosa sono davvero gli Inference Hooks — un POST HTTPS da Anthropic verso un server che gestisci tu, non un WebSocket e non un agente on-device
  • Lo schema del frame del prompt — cosa vede esattamente il tuo AI security server (e cosa non vede mai: system prompt, ragionamento nascosto, byte grezzi)
  • Il JSON del verdetto: allow, deny con deny_reason, e perché deliberatamente oggi non esiste un'azione redact
  • Il modello di firma — Standard Webhooks HMAC-SHA256, i due bug di verifica che colpiscono ogni prima integrazione, e il formato del segreto whsec_
  • Le tre leve operative che decidono se gli utenti vengono bloccati o se il modello riceve traffico non ispezionato: timeout del verdetto, gestione dei fallimenti e circuit breaker
  • Un playbook di rollout che non esplode il primo giorno — shadow mode → rollout percentuale → esclusioni per ruolo → enforce, in quest'ordine

Annunciati il 5 agosto 2026, gli Inference Hooks sono la prima risposta first-party di Anthropic alla domanda che ogni security team si pone dopo aver attivato una seat Claude Enterprise: come impedisco a un prompt con dati regolamentati di raggiungere mai il modello? La risposta, finora, era un proxy aziendale che intercettava il traffico TLS verso claude.ai — fragile, incompleto e cieco alla CLI Claude Code. Gli Inference Hooks spostano il punto di enforcement dentro il perimetro di Anthropic: per ogni prompt governato, Anthropic mette in pausa l'inference, invia in POST la transcript a un server gestito dalla tua organizzazione, e attende un allow o deny prima che il modello veda alcunché.

La versione in un paragrafo

La tua organizzazione espone un endpoint HTTPS. Anthropic gli invia ogni prompt governato come POST firmato (Standard Webhooks HMAC-SHA256). Il tuo server risponde {"action": "allow"} e l'inference procede, oppure {"action": "deny", "deny_reason": "..."} e l'utente vede il motivo senza mai arrivare al modello. L'endpoint copre chat Claude Enterprise, Claude Code e Cowork con una sola configurazione. Se il tuo server va in timeout o restituisce 500, la tua impostazione di failure handling decide se la richiesta blocca o procede non ispezionata. Fai rollout graduale con shadow mode + rollout percentuale + esclusioni per ruolo prima di attivare Enforce verdicts.

Inference Hooks vs Compliance API

Entrambe esistono per la stessa audience — team security, legal e compliance di Claude Enterprise — ma operano ai due estremi opposti del ciclo di vita della richiesta.

Inference HooksCompliance API
QuandoInline, prima che l'inference partaA posteriori
Cosa faAutorizza o nega ogni richiesta governata in tempo realeRecupera attività, chat, file, progetti, utenti per audit ed export
DirezioneAnthropic → il tuo serverTu → Anthropic
Usalo perFermare una fugaProvare cosa è successo

La maggior parte delle enterprise userà entrambi. Gli hook sono il filo teso; la Compliance API è il log di audit.

Come funziona il round trip del verdetto

Guided walkthrough1 of 5
  1. Cioè chat claude.ai, Claude Code (web, desktop, CLI) o Claude Cowork. Le richieste accessorie come la generazione del titolo della conversazione NON vengono inviate. La modalità vocale è fuori scope per la beta.

Il senso di far girare tutto sui server di Anthropic, non sui device degli utenti, è l'uniformità: una config, un server, e ogni richiesta governata su ogni superficie viene ispezionata allo stesso modo. Non c'è nulla da installare sui laptop dei dipendenti, e non c'è alcuna integrazione per-app da tenere sincronizzata.

Il frame del prompt

Ogni richiesta è un body JSON con questi campi top-level:

CampoTipoDescrizione
typestringOggi sempre "prompt". Appariranno nuovi tipi di evento — restituisci allow su valori non riconosciuti così non attivi il circuit breaker.
request_idstringIdentificatore opaco per chiamata di inference. Coincide con l'header webhook-id — usalo come chiave di idempotenza.
tenant_idstring | nullIdentificatore opaco per l'organizzazione.
actorobjectDiscriminato su type ("user" è l'unico valore oggi). Porta un id taggato stabile tra le richieste di un utente e email_address quando disponibile. Entrambi i campi possono essere null.
sourceobject{"application": "..."}. Valori noti: claude-ai, claude-code, config-test (usato dal pulsante admin "Test connection"). Enum aperto — appariranno nuovi valori.
session_idstring | nullIdentificatore opaco della conversazione. Non parsarlo. Best-effort per Claude Code.
modelstring | nullIdentificatore pubblico del modello per questa richiesta, quando disponibile.
messagesarrayLa transcript della conversazione fino al punto di inference — vedi Blocchi di contenuto.
metadataobjectMappa di estensione riservata. Oggi vuota. Tollera chiavi che non conosci.

Una richiesta live minima ha questo aspetto:

{
"type": "prompt",
"request_id": "req_abc123",
"tenant_id": "11111111-1111-1111-1111-111111111111",
"actor": {
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrStUv",
"email_address": "alice@example.com"
},
"source": { "application": "claude-ai" },
"session_id": "22222222-2222-2222-2222-222222222222",
"model": "claude-sonnet-5",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Summarize the attached report." },
{
"type": "attachment",
"file_name": "q2-report.pdf",
"media_type": "application/pdf",
"size_bytes": 48213,
"text": "Q2 revenue grew 14% quarter over quarter..."
}
]
}
],
"metadata": {}
}

Blocchi di contenuto

Ogni entry messages[].content[] ha un type e segue il modello dei content block dell'API Messages pubblica. I risultati dei tool appaiono sotto il ruolo user.

type del bloccoCampi
texttext
tool_useid, tool_name, input
tool_resultcontent (testo, unito con newline; le parti binarie sono marker segnaposto), is_error, tool_name, tool_use_id
attachmentfile_name, media_type, size_bytes, text (testo estratto, transcript o metadati del link)

Cosa non contiene mai la transcript

È la parte che fa inciampare i privacy review.

  • Nessun system prompt. Né quelli di Anthropic, né i tuoi (via projects/skills), né la costituzione del modello — niente di tutto ciò viene inviato.
  • Nessun ragionamento nascosto. La catena di extended-thinking di Claude non fa parte della transcript che il tuo server vede.
  • Nessuna definizione di tool. Solo le chiamate e i loro risultati.
  • Nessun byte grezzo. File e immagini sono rappresentati da metadati e testo estratto. Il contenuto solo-immagine (uno screenshot di un documento) non verrà ispezionato.
  • Nessun contesto interno di Anthropic o trust boundary.

La transcript è la conversazione come la vede l'utente finale, più le tracce dei tool. Un blocco o turno i cui contenuti sono tutti esclusi viene rimosso interamente, quindi non dare per scontata la stretta alternanza user/assistant quando parsi.

Un gotcha sulla size

Le transcript vengono inviate non troncate fino a un tetto di 10 MB. I default comuni sono molto più piccoli — nginx client_max_body_size è 1 MB, Express express.json() è 100 kB, la maggior parte dei reverse proxy PaaS si ferma a pochi MB. Un body che il tuo server rifiuta è un webhook failure, che sotto la failure handling Allow the request significa che il prompt oversize raggiunge il modello non ispezionato. Alza i limiti del body prima di fare enforce.

Lo schema del verdetto

Rispondi con HTTP 200 per entrambi gli esiti. Il campo action fa da discriminante.

Allow:

{ "action": "allow" }

Deny:

{
"action": "deny",
"deny_reason": "This prompt appears to contain customer payment card data, which your organization's policy does not allow.",
"reference_id": "scan_01HXPT4R9V"
}
CampoTipo & limiteSemantica
action"allow" o "deny"; obbligatorioallow lascia procedere l'inference. deny rifiuta la richiesta.
deny_reasonstring o null; al massimo 500 caratteri, valori più lunghi vengono troncatiMostrato all'utente finale, in coda al messaggio permanente configurato dal tuo admin. Scrivilo per l'utente — digli cosa cambiare, non come si chiamava la regola del tuo scanner.
reference_idstring o null; al massimo 50 caratteri da [A-Za-z0-9._:/-]Il tuo identificatore per questa valutazione. Registrato sull'entry inference_hooks_request_denied della negazione nell'Activity Feed, mai mostrato all'utente finale. Tienilo opaco — niente contenuto della richiesta, niente dati personali.

Perché non c'è un'azione redact

Il verdetto è deliberatamente binario. Anthropic avrebbe potuto aggiungere {"action": "redact", "rewritten_prompt": "..."} e lasciare che il tuo server DLP sanitizzasse la transcript in transito — ma questo significherebbe che Anthropic invia al modello qualunque cosa restituisca la tua scatola, sotto l'autorità della tua organizzazione. Il design mantiene netto quel trust boundary: il tuo server valuta i contenuti, non li scrive. Se hai bisogno di redazione, falla nel client prima che l'utente prema invio.

Un deny non viene mai scartato per problemi di formato

Un deny_reason oversize viene troncato; un reference_id malformato viene silenziosamente scartato; l'action resta comunque onorata. Il contrario non vale: qualunque cosa diversa da HTTP 200 con un verdetto parsabile è un webhook failure, non un deny. Se segnali i blocchi con HTTP 403 i tuoi deny si trasformano silenziosamente in allow fail-open (o blocchi, a seconda della failure handling) e ognuno di essi conta contro il circuit breaker.

Il server funzionante più piccolo

AI security server allow-all in 12 righe di Python

# Run with: python server.py — expose on an https:// URL your admin configures.
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

class VerdictHandler(BaseHTTPRequestHandler):
  protocol_version = "HTTP/1.1"  # keep the connection open between verdicts
  def do_POST(self):
      self.rfile.read(int(self.headers.get("Content-Length", 0)))
      verdict = b'{"action": "allow"}'
      self.send_response(200)
      self.send_header("Content-Type", "application/json")
      self.send_header("Content-Length", str(len(verdict)))
      self.end_headers()
      self.wfile.write(verdict)

ThreadingHTTPServer(("", 8000), VerdictHandler).serve_forever()

Mettilo dietro un reverse proxy TLS-terminating sulla porta 443, configuralo come tuo endpoint, premi Test connection nella console admin — vedrai il verdetto allow. Questa è esattamente la forma di un'integrazione archival-only: restituisci allow incondizionatamente e persisti il frame dopo aver risposto, come alternativa push al polling della Compliance API. Non è una forma con cui dovresti fare enforcement — accetta ogni richiesta, incluse quelle non firmate. Aggiungi la verifica della firma prima di attivare Enforce verdicts.

Verifica della firma

La firma segue la spec Standard Webhooks. Tre header, lowercase come li invia Anthropic ma case-insensitive in lookup (i proxy ricambiano il case).

HeaderContenuto
webhook-idUnivoco per delivery. Coincide con il request_id del body. Usalo come chiave di idempotenza.
webhook-timestampUnix time in secondi, come stringa decimale. Rifiuta se più di 5 minuti dal tuo orologio in una qualsiasi direzione — quella è la replay window.
webhook-signatureValori v1,<base64> separati da spazio. Ognuno è un HMAC-SHA256 sulla stringa di byte {webhook-id}.{webhook-timestamp}.{byte grezzi del body}. Accetta la richiesta se un qualunque valore matcha il tuo — usa confronto a tempo costante.

I due bug che colpiscono ogni prima integrazione

Watch out
  • Verifica i byte grezzi, NON JSON ri-serializzato. Calcola l'HMAC sul body esattamente come ricevuto, prima di qualunque parsing o riserializzazione. Un round trip json.loads() → json.dumps() cambia i whitespace e muore qui.
  • Decodifica il segreto con un decoder base64 STANDARD, non URL-safe. Il signing secret è il valore dopo il prefisso whsec_, codificato con l'alfabeto standard (+ e /). Un decoder URL-safe deriva i byte di chiave sbagliati ogni volta che il segreto contiene + o /, ovvero quasi sempre — e il fallimento è un mismatch silenzioso a tempo costante.

Implementazione Python di riferimento (compressa dai docs Anthropic):

import base64, hashlib, hmac, time

TOLERANCE_SECONDS = 300

def verify(secret: str, headers: dict[str, str], body: bytes) -> bool:
h = {k.lower(): v for k, v in headers.items()}
try:
msg_id, ts, sigs = h["webhook-id"], h["webhook-timestamp"], h["webhook-signature"]
except KeyError:
return False # unsigned, not from Anthropic
try:
signed_at = int(ts)
except ValueError:
return False
if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
return False # replayed, or clocks disagree
try:
key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
except ValueError:
return False # misconfigured secret
payload = f"{msg_id}.{ts}.".encode() + body
expected = b"v1," + base64.b64encode(hmac.new(key, payload, hashlib.sha256).digest())
return any(hmac.compare_digest(expected, s.encode()) for s in sigs.split())

Rotazione del segreto

La rotazione è un cutover immediato lato admin, ma le richieste firmate con il segreto precedente possono ancora arrivare per circa un minuto dopo, oltre a tutto ciò che è già in transito. Fa' in modo che il tuo server accetti firme sia dal vecchio che dal nuovo segreto durante la finestra di rotazione così quegli stragglers non vengano rifiutati come non firmati.

Eccezione una tantum

Un test di connessione inviato prima del primo salvataggio della tua organizzazione arriva non firmato, perché il signing secret ancora non esiste. Accetta le richieste non firmate finché il tuo admin non conferma che il segreto esiste, poi rifiutale.

Semantica operativa

Timeout

Il tuo admin imposta un timeout del verdetto tra 1 e 10.000 ms, con default a 5.000 ms. Quel budget copre l'intero round trip: connessione, handshake TLS, upload del body della richiesta, download del body della risposta.

Retry

Anthropic ritenta esattamente una volta, dopo un ritardo di 100 ms, e solo quando il tentativo di connessione fallisce. Non su 500. Non su timeout. Non su errori di parse. Una volta che il tuo server ha risposto — con qualunque cosa — lo scambio è chiuso. Il retry condivide lo stesso budget di timeout e porta lo stesso webhook-id e la stessa firma, quindi è sicuro chiavare la deduplica su webhook-id.

Gestione dei fallimenti

Tutto il resto che non è un 200 pulito con verdetto è un webhook failure: timeout, status non-200 (redirect inclusi), body di risposta non parsabili o oversize, endpoint irraggiungibili. Al fallimento, decide l'impostazione della tua organizzazione:

  • Block the request. Default sicuro per ambienti ad alta regolazione. Se il tuo server DLP è giù, gli utenti sono bloccati. La disponibilità di Claude diventa la disponibilità del tuo scanner.
  • Allow the request. Gli utenti continuano a lavorare mentre il tuo server si riprende. I prompt fluiscono non ispezionati durante il disservizio — un tradeoff accettato da molte org, ma pianifica come riconciliare il gap nel tuo audit trail.

Circuit breaker

Fallimenti di webhook sostenuti attribuibili al tuo AI security server fanno scattare un circuit breaker che ferma l'enforcement: Anthropic smette di chiamare il tuo server, e la failure handling si applica a ogni richiesta. Il recovery non è automatico — sistema il server, poi fa' che il tuo admin riattivi Enforce verdicts. In pratica questo significa: un type top-level sconosciuto dovrebbe restituire {"action": "allow"}, non un HTTP 500 — altrimenti un futuro nuovo tipo di evento ti farebbe finire in territorio circuit-breaker il giorno del rollout.

Latenza

Ogni richiesta governata nella tua organizzazione paga il round-trip del tuo AI security server in latenza aggiunta. Fai load-test prima di rilasciare su un'org grande; uno scanner da 4 secondi è invisibile su un prompt di chat ma un incubo sui loop di tool di Claude Code che fanno molte richieste di fila.

Allowlist degli IP sorgente

Le richieste originano da 160.79.106.0/24, parte degli outbound IP range pubblicati da Anthropic. Metti in allowlist quel blocco, non i range inbound sulla stessa pagina — sono liste diverse. E l'allowlisting non sostituisce la verifica della firma: il blocco trasporta traffico egress di Anthropic oltre agli Inference Hooks.

Il playbook di rollout

Guided walkthrough1 of 4
  1. La prima cosa che attivi. Il tuo server valuta ogni richiesta ma nessun deny viene applicato. Puoi tarare le tue regole contro una settimana di traffico reale prima che un singolo utente venga bloccato.

I docs Anthropic lo dicono chiaramente: bloccare i dipendenti il primo giorno è il modo in cui i programmi DLP muoiono. Lo shadow mode esiste per un motivo.

Progetta la tua integrazione

Pro tip
  • Fai deduplica su webhook-id. È univoco per delivery e matcha request_id nel body. Un retry da connection-failure lo riusa, quindi è una chiave di idempotenza pulita.
  • Salva ogni verdetto con il suo reference_id. Anthropic registra reference_id sull'entry dell'Activity Feed di ogni negazione, così puoi ricollegare le negazioni all'esatta decisione di scan nel tuo sistema.
  • Per integrazioni archival always-allow, RISPONDI prima, poi persisti. Rispondere prima di scrivere tiene il tuo round trip fuori dal critical path dell'utente — il tuo sistema di storage non è sul hot path.
  • Scrivi deny_reason per la persona, non per il SIEM. 'Rimuovi i numeri di carta di credito dal tuo prompt e invia di nuovo' batte 'PCI_REGEX_2A tripped, reference 4471'. Gli utenti agiranno sul primo.

Matrice di copertura

Superficie / accessoIspezionata dagli Inference Hooks?
claude.ai (web, desktop, mobile)✅ Sì
Claude Code (web, desktop, CLI)✅ Sì (session_id è best-effort, dichiarato dal client)
Claude Cowork✅ Sì
Modalità vocale❌ Non nella beta
Generazione titolo conversazione, altri accessori❌ Non inviati
System prompt, definizioni di tool❌ Mai inviati
Byte grezzi di file / immagini❌ Mai inviati (il testo estratto sì)
Contenuto solo-immagine (es. screenshot di doc)❌ Non ispezionato
API key di Claude Platform (accesso developer)❌ Fuori scope degli Inference Hooks (org Platform, non Enterprise)
Deploy su Amazon Bedrock / Google Cloud❌ Non disponibile su quei piani

Errori comuni

Watch out
  • Segnalare un blocco con HTTP 403. È un webhook failure, non un deny — il tuo verdetto di policy viene buttato via e la failure handling prende il sopravvento.
  • Restituire qualunque action diversa da 'allow' o 'deny'. Stessa storia: webhook failure. Se sei tentato di aggiungere un terzo stato, fallo nel tuo audit log, non nel verdetto.
  • Limiti default piccoli sul body (Express 100 kB, nginx 1 MB). Una transcript da 3 MB con il testo estratto di un PDF grosso farà 413 sul tuo reverse proxy. Alza i limiti per accogliere il tetto da 10 MB.
  • Decodifica base64 URL-safe del segreto whsec_. Mismatch silenzioso a tempo costante su ogni richiesta finché non ti accorgi che tutte le tue richieste sono 'non firmate'.
  • Ri-serializzare il body prima dell'HMAC. Verifica i byte grezzi esattamente come ricevuti. json.loads + json.dumps cambia i whitespace e rompe la firma.
  • Rifiutare `type` top-level sconosciuti con 500. Fa scattare il circuit breaker il giorno in cui Anthropic rilascia un nuovo tipo di evento. Restituisci `allow` sui type sconosciuti.
  • Rifiutare valori di `source.application` che non riconosci. È un enum aperto. Nuovi valori appariranno e le vecchie integrazioni non devono blocarsi su di essi.
  • Presupporre l'alternanza user/assistant. I turni i cui blocchi sono tutti esclusi vengono rimossi da `messages`. Parsa in modo difensivo.

Quando usare Inference Hooks vs un proxy client-side

Alcune org girano ancora proxy TLS-intercepting per coprire tutto ciò che i loro dipendenti fanno online. Gli Inference Hooks non sostituiscono un proxy — sono un punto di enforcement specifico per Claude che sta dentro il perimetro di Anthropic e vede una vista strutturata più ricca della conversazione di quanto veda un proxy che sulla wire osserva solo byte cifrati.

  • Usa Inference Hooks quando vuoi accesso strutturato a ciò che il modello vedrà davvero (tool call, allegati, transcript), copertura uniforme su chat + Code + Cowork, e nessun install per-device.
  • Tieni il tuo network DLP per tutto il resto sulla box: upload di file verso servizi non-Claude, traffico browser fuori da claude.ai, allegati email. I due non si sovrappongono.
  • Aggiungi la Compliance API per audit ed export a posteriori.

Quiz

Check yourself

0/3
  1. Anthropic contatta il tuo AI security server come?
  2. Il tuo scanner DLP rileva una violazione di policy. Quale risposta è corretta?
  3. Il tuo AI security server è giù per un rolling deploy e restituisce 500 per 90 secondi. Cosa succede ai prompt degli utenti in quella finestra?

Prossimo