AgentSonar: Detecting Shadow AI Through Network Traffic Heuristics
Hook
Your employees are querying Claude, ChatGPT, and a dozen other LLMs you've never approved—and traditional network monitoring tools have no idea it's happening.
Context
The explosion of AI assistants has created a peculiar security blindspot. Unlike traditional SaaS sprawl where unauthorized tools are relatively static, AI services proliferate faster than security teams can maintain blocklists. An employee might use ChatGPT in their browser, Claude via a VSCode extension, Copilot through GitHub, and Perplexity on their phone—all in a single afternoon. Traditional approaches fail here: domain allowlists become maintenance nightmares as providers spin up new endpoints, DPI solutions can't inspect TLS 1.3 traffic, and endpoint agents only catch known applications.
AgentSonar takes a different approach: instead of asking "is this domain on my AI blocklist?", it asks "does this traffic pattern look like an LLM API call?" By combining packet capture with process association and heuristic scoring, it discovers AI usage by analyzing traffic characteristics—asymmetric request/response ratios, streaming patterns, programmatic TLS handshakes—rather than maintaining an ever-growing list of AI vendor domains. It's designed for security teams who need visibility into shadow AI: the unauthorized tools employees install because they're productive, even when they violate data governance policies.
Technical Insight
AgentSonar's architecture has three layers: packet capture with process correlation, pattern-based classification for known agents, and heuristic scoring for unknown AI traffic. The foundation is libpcap-based packet capture combined with socket ownership lookup. When it sees a TCP connection, it queries /proc/net/tcp (Linux) or uses lsof (macOS) to find which process owns that socket, creating a process-to-domain association that's critical for security investigation.
The first classification layer uses exact pattern matching. AgentSonar ships with a rules file mapping process names and domain patterns to known AI agents:
// Example from the classification engine
type AgentPattern struct {
ProcessName string
DomainRegex *regexp.Regexp
AgentName string
Category string // "llm", "search", "code-assistant"
}
// When both process and domain match, it's a confirmed detection
if pattern.ProcessName == proc.Name &&
pattern.DomainRegex.MatchString(domain) {
return Classification{
Agent: pattern.AgentName,
Confidence: 1.0,
Method: "exact-match"
}
}
This catches obvious cases: cursor.exe connecting to api.cursor.sh, or Code.exe hitting api.githubcopilot.com. But the interesting work happens in the heuristic layer for unknown traffic.
The heuristic classifier analyzes traffic shape without knowing anything about the destination. It looks for characteristics typical of LLM API calls: requests are small (a few kilobytes of prompt text), responses are large (streaming completions), connections often use HTTP/2 or Server-Sent Events, and TLS handshakes look programmatic rather than browser-initiated. Here's the core scoring logic:
func (h *HeuristicClassifier) Score(conn *Connection) float64 {
score := 0.0
// LLM APIs show extreme byte asymmetry
if conn.BytesReceived > conn.BytesSent * 10 {
ratio := float64(conn.BytesReceived) / float64(conn.BytesSent)
score += min(ratio / 100.0, 0.3) // Cap at 0.3
}
// Packet counts are more balanced (streaming chunks)
if conn.PacketsReceived > 5 && conn.PacketsSent > 5 {
packetRatio := float64(conn.PacketsSent) / float64(conn.PacketsReceived)
if packetRatio > 0.3 && packetRatio < 3.0 {
score += 0.2
}
}
// Connection duration suggests interactive use
if conn.Duration > 2*time.Second && conn.Duration < 60*time.Second {
score += 0.15
}
// TLS without browser-like SNI patterns
if conn.TLSHandshake != nil && !h.looksLikeBrowser(conn.TLSHandshake) {
score += 0.2
}
// Domain not in common CDN/cloud provider list
if !h.isInfrastructureDomain(conn.Domain) {
score += 0.15
}
return score
}
A score above 0.6 flags the connection as probable AI traffic. The byte asymmetry is the strongest signal—LLM completions can be 50-100x larger than prompts—but packet count symmetry prevents false positives from simple downloads (which have asymmetric bytes but also asymmetric packet counts).
AgentSonar stores all scored connections in a SQLite database, allowing historical queries and triage workflows. When you run it in interactive mode, high-scoring unknowns are presented for classification:
$ agentsonar triage --min-score 0.7
Unknown high-score detection:
Process: python3.11
Domain: api.anthropic.com
Score: 0.85 (byte_ratio: 0.3, packet_balance: 0.2, tls: 0.2, duration: 0.15)
Bytes sent: 4.2 KB, received: 387 KB
Classify as: [a]gent, [n]oise, [s]kip?
> a
Agent name: claude-api
Category: llm
✓ Added to agent patterns
This triage workflow builds institutional knowledge. After classifying an unknown pattern once, subsequent connections are instantly recognized via exact matching, and the pattern can be exported to share across your organization's deployment.
The tool also supports "classify-only" mode for analyzing packet captures without live monitoring. This is crucial for investigating containerized applications where process association fails (containers use network namespaces, breaking socket ownership lookup). You can run tcpdump inside a container, export the PCAP, and run AgentSonar's heuristic classifier offline—you lose process names, but still get domain-based AI detection.
Gotcha
AgentSonar's biggest limitation is process association in containerized environments. The socket ownership lookup queries the host's process table, so traffic from containers appears as PID 0 (kernel) or the container runtime process. You can run with --enable-pid0 to still classify the traffic, but you lose the critical "which application is doing this" context. If your infrastructure is heavily Kubernetes-based, you'd need to deploy AgentSonar as a sidecar or DaemonSet and correlate by pod labels rather than process names—which the current version doesn't support.
The heuristic scoring is also inherently probabilistic. Non-AI services with similar traffic patterns—streaming video APIs, websocket services, GraphQL subscriptions—can score high enough to trigger false positives. Conversely, AI tools with atypical traffic signatures (heavily cached responses, image generation with symmetric byte flows, on-device models with no network traffic) won't be detected. You're trading precision for discovery capability: AgentSonar will find AI tools you didn't know existed, but requires human triage to separate signal from noise. In high-traffic environments, even a 5% false positive rate generates overwhelming alert volume.
Finally, platform setup complexity can be a barrier. macOS requires granting terminal apps Full Disk Access to read BPF devices, Linux needs CAP_NET_RAW and CAP_NET_ADMIN capabilities, and all platforms need libpcap installed. In locked-down corporate environments, getting these permissions approved can take longer than deploying the actual tool.
Verdict
Use AgentSonar if you're a security team at a company where shadow AI is a data governance concern and you need discovery capability beyond static blocklists—particularly if you're auditing developer workstations with diverse, rapidly-changing AI tooling. It's ideal for establishing baseline AI usage before implementing formal policies, or for investigating suspected data exfiltration through LLM APIs. The heuristic scoring genuinely finds tools you didn't know to look for. Skip it if your environment is primarily containerized without process-level visibility requirements, if you already have comprehensive CASB coverage with AI app detection, or if you can't tolerate false positives that require manual triage. Also skip if you only need to monitor a known, approved set of AI services—a simple firewall rule or DNS filter is lower overhead for that use case. For personal use or small teams, the operational complexity outweighs the benefit unless you're specifically researching AI traffic patterns.