Reversim 2026·eBPF + MCP
01 / 17
nav·F full·T theme
TalkReversim Summit 2026

eBPF + MCP.
Giving your agent a wiretap on itself.

Your agent just spent forty minutes doing something. What did it actually send over HTTPs? Not what the logs say — what left the machine.

01The question

Your agent ran for 40 minutes. What did it actually send?

×
Model trace is not network truth.
×
App logs are not network truth — especially for third-party tools, browsers, CLIs.
×
Browser screenshots are not network truth — they show the final UI state.
×
A proxy would work — if you can safely put a proxy in the path.

The distinction between what the agent said it did and what actually left the machine is the whole talk.

02Why the usual answers aren't enough

Every existing approach has a fatal caveat.

App logs

Helps when you own the app.

Fails for third-party tools, browsers, CLIs. Instrumentation is uneven. Bodies get redacted.

Browser instrumentation

Helps for browser-only flows.

Fails per-browser: extension limits, misses non-browser calls, breaks when the agent shells out.

MITM proxy

Helps when you can install a CA on the client.

Fails on cert pinning, TLS fingerprint changes, env-var CA that some SDKs ignore. Terminates TLS and opens a new connection.

03The weird layer

There is a moment when the plaintext exists in memory. Read it there.

Before SSL_write encrypts outbound bytes, the plaintext exists in memory.

After SSL_read decrypts inbound bytes, the plaintext exists in memory.

This does not break TLS. It observes the program at the point where the program itself already has plaintext.

// libssl.so.3
int SSL_write(SSL *ssl, const void *buf, int num);
//                    ^^^^^^^^^^
//                    plaintext, pre-encryption

int SSL_read(SSL *ssl, void *buf, int num);
//                     ^^^^^^^^^^
//                     plaintext, post-decryption
04eBPF uprobes in one diagram

Attach to SSL_write and SSL_read without touching the app.

   agent / browser / CLI
              ↓
       plaintext request
              ↓
   OpenSSL SSL_write  ←←← eBPF uprobe (captures outbound plaintext)
              ↓
       encrypted TLS
              ↓
          network
              ↓
       encrypted TLS
              ↓
   OpenSSL SSL_read   ←←← eBPF uprobe (captures inbound plaintext)
              ↓
       plaintext response
              ↓
        application

No CA. No proxy. Cert pinning irrelevant. Cross-process, cross-language, host-wide.

05Architecture

Agent → MCP → capture daemon → ecapture → OpenSSL uprobes.

   agent
    |  MCP tool calls: list_domains, get_pairs, search
    ↓
   MCP server  (my Python layer)
    |  domain filter, request/response pairing, full-text index
    ↓
   capture daemon  (subscribes to interesting events)
    |  reads flat stream
    ↓
   gojue/ecapture  v0.8.12  (the eBPF harness)
    |  attaches uprobes
    ↓
   OpenSSL · GnuTLS · BoringSSL · rustls

ecapture does the eBPF part. My layer does the useful abstraction — and MCP-wraps it so the agent can call it.

06Raw bytes are not observability

Capture gives you bytes. The abstraction layer gives you answers.

Group bytes by process / connection / domain
Reconstruct request/response pairs where possible
Persist only subscribed domains or relevant events
Full-text index the plaintext
Expose a small tool surface to the agent

Raw ecapture output: chunks, streams, timestamps. Not useful to a human. Not useful to an agent.

My layer is ~600 lines of Python. That's the useful part.

07The MCP tool surface

Three questions an operator asks — three tools.

list_domains
What domains did we hit? Returns {domain, request_count, last_seen}.
get_pairs
Show me the actual traffic to X. Returns {request, response, latency_ms}.
search
Did we send or receive Y? Returns {payload_snippet, request_meta}.
capture_start / capture_stop / capture_status
Control the capture daemon.
watch_domain
Subscribe to persist a specific domain going forward.
get_request
Fetch a specific request by id.

The whole MCP server is ~40 lines of Python once the capture layer exists.

08Live demo · setup

Two agents. One target. One observer.

Agent A · target

A Claude Code session with a small task — e.g. “find the top 3 US tech news headlines and summarize each in 50 words.”

Uses its browser tool + search tool. Real HTTPs traffic to real sites.

Agent B · observer

A Claude Code session with the ebpf-mcp server connected.

Task: “audit what agent A just did — which sites did it hit, did it send anything unexpected?”

Every live moment has a canned counterpart. Screen recording ready if wifi or the MCP handshake dies.

08bLive demo · captured domains

Agent B calls list_domains. Here is what agent A actually hit.

ebpf-mcp · list_domains(since=«5m»)
domain                              requests    last_seen
api.openai.com                       17       12s ago
www.techcrunch.com                    4       48s ago
arstechnica.com                       3     1m 12s ago
news.ycombinator.com                  2     1m 44s ago
google.com                             2     2m 03s ago
stats.tracker.dev                       6     4m 22s ago  ← unexpected

The unexpected domain jumps out. Now agent B calls get_pairs(“stats.tracker.dev”).

08cLive demo · plaintext request/response

The plaintext body on stage. No proxy involved.

ebpf-mcp · get_pairs(“api.openai.com”)[0]
POST https://api.openai.com/v1/chat/completions
Authorization: Bearer sk-***REDACTED***
Content-Type: application/json

{
  “model”: “gpt-4o”,
  “messages”: [
    {“role”: “system”, “content”: “You are a helpful assistant.”},
    {“role”: “user”, “content”: “Summarize this article in 50 words: ...”}
  ]
}

200 OK · 1.4s · 823 bytes
{“choices”: [{“message”: {“content”: “OpenAI announced ...”}}]}

This is the request body the agent actually sent. Not what the trace said. Not what the model summarized. The bytes before encryption.

09Design decision

Capture host-wide. Persist selectively.

Capture

Host-wide — any process on the box, no client-side cooperation required.

The uprobes fire on every SSL_write / SSL_read. That's the correct default.

Persist

Selective — ring buffer in RAM by default, disk overflow only for watched domains or on explicit save.

The capture stream contains secrets. Treat the persistence layer like a secrets store: short retention, allowlists, no casual sharing.

10Where it breaks

Honest limits. This makes the talk credible.

×
Root required — the capture daemon needs privileged access. Not for shared machines.
×
OpenSSL coverage is good, not universal — Go crypto/tls is separate, BoringSSL / Chrome may be statically linked or stripped.
×
Plain HTTP doesn't go through SSL functions — different capture needed.
×
QUIC / HTTP/3 may need different probes.
×
Pairing is imperfect — high-throughput capture needs careful buffering, and some connections resist reconstruction.
11Privacy and safety

This is sensitive infrastructure. Treat it that way.

!
The capture contains plaintext secrets — auth headers, API keys, prompts, responses.
!
Default to short retention. Ring buffer, not archive.
!
Support allowlists / watched domains. Persist only what's explicitly requested.
!
Do NOT run this casually on shared machines.
!
Legal note: for host-local processes you control, this is fine. Not for prod services you don't operate. Not a covert wiretap.
12Future work

The eBPF+MCP combination is a category, not a one-off.

Function signatures for stripped binaries — cache sha256(binary) → offsets.
Support more TLS stacks — Go, rustls, BoringSSL variants.
Correlate network truth with agent tool traces — join by timestamp + pid.
Wrap the same technique for other syscalls: execve, open, socket — a full agent audit surface.
A redactions_check MCP tool — auto-flag outbound bodies that look like secrets.
13Why this matters in 2026

MCP tools usually give the agent capability.
This one inspects the agent's capability.

2026's agent-observability question is not «did the tool call succeed?»
It is «what did the tool call actually send, and did the response actually match the model's claim about it?»

eBPF gives us the ground-truth layer. MCP gives the agent a way to reason over that layer during its own session.

Together: the agent audits itself, in real time, without a human in the loop.

CloseOne line
The honest layer is not the prompt. Not the screenshot. Not the log. It is the bytes before encryption.