Skip to content

TLS capture

When tyrd --tls-capture is enabled, outbound TLS handshakes get tagged with the Server Name Indication (SNI) hostname. This is how Tyr distinguishes api.openai.com from random egress.

What it does

flowchart LR
Proc["process"] --> SSL["SSL_write()"]
SSL --> Kernel["kernel"]
SSL -. "uprobe in tyrd" .-> Capture["extract ClientHello<br/>read SNI<br/>emit TLS event<br/>{ sni = 'api.openai.com' }"]

The SNI is visible because it’s sent in the clear during the TLS handshake. No decryption is performed. No certificate is MITM’d. No secrets are captured.

Supported TLS stacks

LibraryHookStatus
OpenSSLuprobe on SSL_write
BoringSSLuprobe on SSL_write
rustlskprobe on socket layer + user-mode helpers
Go stdlib crypto/tlsGo’s TLS is static, not a shared library — hooked via socket-layer inspectionPartial
NSS (Firefox)uprobe on PR_WritePlanned

The vast majority of AI-agent TLS traffic goes through OpenSSL or rustls (Python requests, Node https, Rust reqwest, system curl) — so coverage is high in practice.

What you get

A TLS event for every outbound HTTPS handshake:

{
"kind": "TLS",
"resource": "api.openai.com",
"verdict": "ALLOW",
"metadata": {
"sni": "api.openai.com",
"remote_ip": "104.18.6.192",
"remote_port": "443",
"provider": "openai"
}
}

Policy implications

You can now write rules like:

- name: only-approved-llm-providers
action: tls_connect
sni_pattern: "*.openai.com|*.anthropic.com|api.mistral.ai"
verdict: allow
severity: low
- name: block-unknown-llm-destinations
action: tls_connect
verdict: deny
severity: high

The second rule, being a catch-all, fires if SNI doesn’t match the first.

Enforcement vs observation

For now, combine TLS SNI rules with network-layer CIDR rules for true enforcement:

- name: allow-openai-ips
action: net_connect
cidr_allow: [ "104.16.0.0/13", "104.18.0.0/15" ]
sni_pattern: "*.openai.com"
verdict: allow
- name: deny-other-external
action: net_connect
cidr_deny: [ "0.0.0.0/0" ]
verdict: deny

CIDR deny is in-kernel, so connect() returns ECONNREFUSED.

Privacy

  • Only the SNI hostname is captured, not the request body.
  • Response bodies are never touched.
  • If you need stricter privacy (no hostname logging), set --tls-capture=false and rely on CIDR rules only.

Known limitations

  • TLS 1.3 Encrypted ClientHello (ECH) — when ECH is widely deployed, SNI will no longer be visible on the wire. Tyr will need userspace hooks in the TLS library directly (the uprobe path already works for OpenSSL today).
  • QUIC / HTTP/3 — covered via UDP socket hooks; same SNI visibility in the QUIC handshake.
  • Self-hosted LLMs — obviously, no SNI tagging helps if your agent is hitting an internal IP. Use net_connect CIDR rules.

→ Next: Writing policies · AI detection