Follow one alert through Calseta. From raw webhook to closed investigation.
An Impossible Travel alert fires in Elastic Security. Here's what happens next — and why your agent gets a complete investigation starting point instead of a raw JSON dump.
Alert arrives via webhook.
202 Accepted in <200ms.
Elastic Security fires an Impossible Travel alert for nick.hathaway@calseta.com — a login from Frankfurt via a known Tor exit node, 148 minutes after a San Francisco login. The raw ECS payload hits POST /v1/ingest/elastic. Calseta validates, enqueues, and returns 202.
// Elastic Security ECS alert payload { "kibana.alert.rule": { "name": "Impossible Travel Detected", "severity": "high", "risk_score": 73 }, "source": { "ip": "185.220.101.47", "geo.city_name": "Frankfurt am Main", "geo.country_name": "Germany" }, "user": { "email": "nick.hathaway@calseta.com", "name": "nick.hathaway" }, "event": { "action": "user-login", "outcome": "success" }, "signal.original_event": { "distance_km": 9349, "time_delta_minutes": 148 }, "okta.security_context": { "as.name": "Tor Project, Inc.", "domain": "torproject.org", "is_proxy": true } // ... 47 more lines }
Vendor format in. Clean schema out.
Every indicator found.
Elastic's ECS format uses nested objects and numeric risk scores. Calseta normalizes to clean field names designed for AI consumption — same schema regardless of source. Then a 3-pass extraction pipeline pulls every actionable indicator.
Parallel enrichment. Cached.
Automatic verdicts.
Every indicator fans out to all configured providers in parallel. Threat intel, identity context, internal data — whatever your environment needs. Results cached per provider TTL. A failing provider never blocks others. Malice verdicts computed automatically from configurable threshold rules.
{
"indicator": "185.220.101.47",
"type": "ip",
"malice": "Malicious",
"enrichment_results": {
"virustotal": {
"malicious": 14,
"suspicious": 2,
"score": "47/94",
"as_owner": "Tor Project, Inc."
},
"abuseipdb": {
"confidence_score": 97,
"total_reports": 1842,
"is_tor": true,
"isp": "Tor Project"
}
}
}Org knowledge.
Attached automatically.
Calseta evaluates every context document's targeting rules against the alert. The Impossible Travel detection rule documentation, Account Compromise Runbook, and Company IR Plan all match. Past alerts involving nick.hathaway or 185.220.101.47 are surfaced too.
// Targeting rule evaluation { "alert": "Impossible Travel Detected", "severity": "High", "rule_name": "Impossible Travel", "matched_documents": [ { "title": "Impossible Travel", "type": "detection_rule", "rule": "rule_name = \'Impossible Travel\'", "matched": true }, { "title": "Account Compromise Runbook", "type": "runbook", "rule": "severity in [High, Critical]", "matched": true }, { "title": "Company IR Plan", "type": "ir_plan", "rule": "global", "matched": true } ], "past_alerts": [ { "uuid": "3c1d-a2f4-...", "title": "Impossible Travel Detected", "status": "closed", "classification": "true_positive_suspicious_activity", "closed_at": "2024-11-03T14:22:00.000Z", "indicators": ["91.108.56.130", "nick.hathaway@calseta.com"], "analyst_notes": "Confirmed malicious. Sessions revoked. Password reset. INC-2847." }, { "uuid": "7e8b-c9d2-...", "title": "Impossible Travel Detected", "status": "closed", "classification": "benign_positive_suspicious_but_expected", "closed_at": "2024-09-17T09:14:00.000Z", "indicators": ["185.220.101.47", "nick.hathaway@calseta.com"], "analyst_notes": "VPN exit node. User confirmed travel. No action taken. INC-2601." } ] }
Context-rich payload delivered.
The agent reasons — not guesses.
The enriched alert — normalized data, enrichment, detection rule docs, matched runbooks, past alerts, available workflows — is dispatched to every registered agent webhook. One POST. Everything the agent needs.
{
"event": "alert.enriched",
"alert": {
"uuid": "9f2a-b3c1-...", "title": "Impossible Travel Detected",
"severity": "High", "source": "elastic", "malice": "Malicious"
},
"indicators": // 3 indicators, fully enriched
"detection_rule": // name, MITRE, docs, blind spots
"past_alerts": // 2 previous (1 TP, 1 benign)
"context_documents": // IR plan + runbook
"workflows": // 6 available actions
}Agent triggers a response.
Sandboxed. Versioned. Audited.
The agent calls execute with the indicator, a reason, and confidence score. The workflow is an HTTP automation script — Python glue that calls external APIs via ctx.http. Calseta validates the code via AST checking, runs it in a sandbox, and logs every step.
{
"workflow_uuid": "wf-8a3c-...",
"indicator_type": "account",
"indicator_value": "nick.hathaway@calseta.com",
"trigger_source": "agent",
"reason": "Confirmed Tor exit node login. AbuseIPDB: 97. Previous TP for same user.",
"confidence": 0.92
}# Revoke all active sessions for the affected account async def run(ctx): user = ctx.indicator.value okta = ctx.secrets["OKTA_API_TOKEN"] # Find Okta user ID resp = await ctx.http.get( f"https://{ctx.secrets['OKTA_DOMAIN']}/api/v1/users/{user}", headers={"Authorization": f"SSWS {okta}"} ) user_id = resp.json()["id"] # Clear all sessions await ctx.http.delete( f"https://{ctx.secrets['OKTA_DOMAIN']}/api/v1/users/{user_id}/sessions", headers={"Authorization": f"SSWS {okta}"} ) ctx.log(f"Revoked all sessions for {user}") return WorkflowResult.ok( message=f"Sessions revoked for {user}", data={"user_id": user_id} )
High-risk action?
Human decides.
The workflow has approval_mode: "always" and risk_level: high. The approval gate fires — a Slack message appears in #security-approvals with the agent's reason, confidence, and approve/reject buttons.
Alert closed. Metrics updated.
Everything audited.
The analyst approves. Sessions revoked. The agent posts its finding, updates the status to Closed with classification "True Positive — Credential Compromise." MTTD, MTTA, and MTTC are updated. The immutable activity log records every action.
One alert. Zero tokens wasted.
That's what your agents deserve.
From raw webhook to closed investigation. Calseta handles the deterministic infrastructure. Your agents handle the reasoning.
Open source · Self-hostable · Apache 2.0