AI Threat Intelligence Pipeline
The Enterprise-only intelligence worker: what it analyses, the assessment it writes, the hallucination cross-check, clustering and human review.
Overview
The intelligence pipeline adds a model-generated assessment on top of the deterministic pipeline. Two functions do the work.
- `shield-intelligence-worker` analyses one event after the fact. The ingest pipeline dispatches it fire and forget once the event row exists, so nothing in the decision path waits for it.
- `shield-intelligence-review` is the human review endpoint: list, review, cluster and explain.
Who can use it. Enterprise only. The worker is dispatched only when tenant.plan is enterprise, and every review action calls the Enterprise plan gate and returns 403 plan_required otherwise.
How each is reached. The worker is internal: it requires the service-role key and answers 401 Unauthorized to anything else, saying plainly that it needs the service-role key, so no API key can call it. The review endpoint is console-only. It is not in the public function allowlist, so it is not routed at api.passkeybridge.io; the dashboard calls it directly with your session, from Dashboard > Intelligence.
Live traffic only. A test-mode or sandbox event is refused by the worker with {"status": "skipped", "reason": "test_mode"}, and the ingest pipeline does not dispatch one in the first place. The context window, the baseline and the stored assessments are all filtered to live rows. That filter exists because synthetic events had come to dominate both: measured on production before it, 279 of 295 assessments came from test-mode events, and their mean risk score inverted the cross-check below.
Intelligence worker
Given an event_id and a tenant_id, in order:
- Fetch the event. 404
Event not foundwhen it does not belong to that organization. - Refuse test traffic.
test_mode = truereturns 200 withstatus: "skipped". - Build context. The 25 most recent live events for the organization, oldest fields only: type, result, response time, risk score, timestamps and hashes.
- Ask the model. One call to Claude Haiku through the shared AI gateway, temperature 0.1, a 1,024-token ceiling and a 10-second timeout. There is no tool calling: the prompt demands a single raw JSON object and the reply is parsed after any stray code fence is stripped. Unparseable output is a 502
AI returned invalid JSON; a gateway 429 or 529 is returned as 429 so the caller can retry; anything else is 502AI analysis failed. - Clamp.
risk_scoreis forced into 0.000 to 1.000. - Cross-check. The assessment is compared against the deterministic signal and the organization's baseline, and corrected where it contradicts them.
- Cluster. When the context window holds at least three events, the clustering engine runs and the top cluster's label is attached.
- Store. One row in
shield_intelligencewithtest_mode: false. - Escalate above 0.95. Shadow identities whose
user_hashequals the event's identifier hash are deactivated and stampedrevoked_at, and an audit row recordsintelligence.level2_triggered. This is the only write outside the intelligence table that the worker performs. - Nudge agent trust. When the model returns an
agent_trust_deltaof more than 0.05 either way,evaluate_tenantis dispatched to the trust engine with that delta. - Audit.
intelligence.analyzed, with the score, the type, the model and the delta.
The response is { "status": "ok", "intelligence_id", "risk_score", "intelligence_type", "threat_actor_label", "is_hallucination_check" }.
Output schema
| Field | Type | Meaning |
|---|---|---|
risk_score | 0.000 to 1.000 | The model's probability of a genuine threat, clamped on arrival and possibly corrected by the cross-check |
intelligence_type | enum | behavioral, synthetic, sybil_cluster, velocity_anomaly, credential_stuffing or clean. Anything else is a critical cross-check violation |
explanation_json | object | Feature names to attribution weights, expected to sum to about 1.0 |
threat_actor_label | string or null | One of fourteen known labels, such as automated_bot, sim_farm, credential_stuffer, state_actor_pattern. An unknown label is a warning rather than a rejection |
is_hallucination_check | boolean | True when the model reported low confidence or thin data, or when the cross-check flagged the assessment |
agent_trust_delta | -1.0 to +1.0 | How this event should move delegate trust. Above 0.05 either way it dispatches a trust evaluation |
Two keys are added by the platform rather than the model. _cluster_label carries the label of the top behavioural cluster when one was found, and _hallucination_violations carries the number of cross-check violations when the assessment was flagged. The Intelligence tab hides keys beginning with an underscore when it renders contributing factors, and shows the top six by weight.
model_version on the row records which model produced the assessment, and rows carry an expires_at the tab displays.
Hallucination cross-check
Every assessment goes through five deterministic layers before it is stored. The checker is a pure function with no I/O.
| Layer | What it tests | Severity |
|---|---|---|
| Rule consistency | A hard signal assessed below 0.7, or called clean | Critical, and the score is raised to 0.7 or the type corrected to behavioral |
| Rule consistency | The assessment more than 0.5 away from the rule-based risk_score on the event | Warning |
| Historical baseline | More than 2.5 standard deviations from the organization's mean, once at least 10 prior live assessments exist | Warning |
| Attribution integrity | Weights that sum more than 0.3 away from 1.0, or an empty explanation on an unflagged assessment | Warning |
| Attribution integrity | Any negative weight | Critical |
| Temporal coherence | More than 3 standard deviations from the mean of recent assessments of the same type | Warning |
| Actor plausibility | A threat_actor_label outside the known taxonomy | Warning |
| Actor plausibility | An intelligence_type outside the valid set | Critical |
The verdict is hallucinated when there is at least one critical violation or at least three warnings. The corrected values replace the model's, is_hallucination_check is set, and the violation count is written into explanation_json._hallucination_violations.
Two limits worth knowing. The temporal coherence layer needs a set of recent similar assessments, which the worker does not supply; it is exercised only by the hallucination_report action, which fetches up to ten. And the worker's own hard-signal list for layer 1 carries seven types, missing number_porting and scope_poisoning, so an assessment that understates one of those two is not corrected. The deterministic pipeline still revoked on them; only this correction is skipped.
Behavioral clustering
The clustering engine groups recent events by similarity to surface coordinated activity that a single event cannot show. It is a pure function: the calling function does the reads.
Features per event: event type, the first 8 characters of the identifier hash, the first 8 of the signal hash, a 4-hour band of the UTC hour, the day of week, a risk bucket (low at or below 0.2, medium to 0.5, high to 0.8, critical above), a latency bucket, the first 8 characters of an IP hash, a user-agent class, and whether a playbook matched.
One of those features is still always empty. The three infrastructure features are read from the event's metadata. Since 2026-09-17 the ingest pipeline writes ip_prefix, the first 8 characters of the keyed IP digest, and playbook_matched alongside risk_score, correlation_id and the HIBP fields, so both of those now contribute to similarity. Nothing writes a user-agent class, so that one contributes nothing. Before that date all three were empty, and the address half of the sim_farm_pattern test ("many identifiers, few addresses") was satisfied by an empty set, so the label was applied to any cluster holding more than three distinct identifier prefixes. It now requires at least one address to have been observed.
Similarity is 70% Jaccard overlap of the feature set and 30% temporal proximity inside a 30-minute window. Single-linkage merging at a threshold of 0.55, minimum cluster size 3, complexity O(n squared), which is fine for the windows used.
Labels: sim_farm_pattern, credential_stuffing_campaign (dominant type credential_stuffing or failed_verification), targeted_attack (one identifier prefix, at least five events), high_risk_<type>_cluster, or <type>_cluster.
Threat probability is the high-risk ratio times 0.5, plus cohesion times 0.3, plus a size bonus capped at 0.2. Clusters come back sorted by it.
Where it runs. The worker clusters its 25-event live context window and keeps the top label. get_clusters clusters up to 500 events from a window of up to 168 hours, and that query does not filter test traffic, so replayed test events appear in the Clusters view.
Human-in-the-loop review
shield-intelligence-review needs an Authorization: Bearer <session JWT>, a tenant_id in the body, tenant-admin membership and the Enterprise plan. It is not on the public host; the dashboard invokes it directly from Dashboard > Intelligence.
| Action | Parameters | Returns |
|---|---|---|
list_pending | filter one of all, pending, hallucinated, reviewed; limit default 50, maximum 200 | Assessments joined to their events, plus counts of total, pending, hallucinated and reviewed |
review | intelligence_id, verdict, optional corrected_risk_score, corrected_type, corrected_actor_label, review_notes | { "status": "reviewed", "intelligence_id", "verdict", "human_verified": true } |
get_clusters | hours default 24, maximum 168 | Clusters, noise event ids, totals and the window |
hallucination_report | intelligence_id | The assessment, the deterministic signal, baseline statistics and the full violation list |
Verdicts. approve records human_verified and nothing else. correct stores the corrected score, type or label in separate columns, leaving the model's original values intact. reject does the same and sets is_hallucination_check. An unknown verdict is refused with 400.
Each review writes an audit row with action intelligence.reviewed_approve, intelligence.reviewed_correct or intelligence.reviewed_reject, carrying the reviewer, the original score and any correction.
Corrections are a record rather than a training loop. They are stored beside the original assessment and shown in the tab as "Human correction". Nothing reads them back into the model or adjusts later assessments, so treat them as ground-truth labelling for your own analysis.
Errors: 401 Authentication required or Invalid authentication, 400 Missing tenant_id, 403 Tenant admin required, 403 plan_required, 404 Intelligence record not found, 400 Unknown action.