Back to Blog
Sessions 2026-02-09

Exec Session Recording

How iddio captures every byte of kubectl exec and attach sessions. Connection hijacking, bidirectional stream recording, and forensic-quality replay — transparent to the agent.

Why Record Exec Sessions

When an AI agent runs kubectl exec -it pod -- /bin/sh, it gets an interactive shell inside a running container. This is a T4 (break-glass) operation for good reason: the agent has direct, unrestricted access to the container’s filesystem, processes, and network.

A one-line audit log entry — “claude-code executed exec on pod api-7d4b8f6c9” — tells you the exec happened, but not what the agent did inside the session. Session recording captures every byte: every command typed, every output returned, every control character sent.

How Exec Works in Kubernetes

kubectl exec uses HTTP protocol upgrade. The initial request is a normal HTTPS request to the Kubernetes API server:

GET /api/v1/namespaces/payments/pods/api-7d4b8f6c9/exec?command=/bin/sh&stdin=true&stdout=true&tty=true
Connection: Upgrade
Upgrade: SPDY/3.1

The API server responds with 101 Switching Protocols, and the connection upgrades to a bidirectional stream. From this point, raw bytes flow in both directions: stdin from the client, stdout/stderr from the container.

Connection Hijacking

Iddio intercepts the upgrade by hijacking the HTTP connection:

func (p *Proxy) handleExec(w http.ResponseWriter, r *http.Request) {
    // Classify and enforce — this is T4 break-glass
    // ...

    // Hijack the client connection
    hijacker, _ := w.(http.Hijacker)
    clientConn, clientBuf, _ := hijacker.Hijack()
    defer clientConn.Close()

    // Connect to the real API server
    upstreamConn, _ := tls.Dial("tcp", p.clusterURL, p.tlsConfig)
    defer upstreamConn.Close()

    // Forward the upgrade request
    r.Write(upstreamConn)

    // Bidirectional stream copy with recording
    p.relayWithRecording(clientConn, upstreamConn, clientBuf, session)
}

After hijacking, iddio holds both ends of the connection: the agent’s TCP socket and the upstream cluster’s TCP socket. It relays bytes between them while capturing a copy.

Bidirectional Stream Recording

The recording captures both directions simultaneously:

func (p *Proxy) relayWithRecording(
    client, upstream net.Conn,
    clientBuf *bufio.ReadWriter,
    session *ExecSession,
) {
    done := make(chan struct{}, 2)

    // Client → Upstream (stdin)
    go func() {
        defer func() { done <- struct{}{} }()
        buf := make([]byte, 32*1024)
        for {
            n, err := clientBuf.Read(buf)
            if n > 0 {
                session.RecordStdin(buf[:n])
                upstream.Write(buf[:n])
            }
            if err != nil { return }
        }
    }()

    // Upstream → Client (stdout/stderr)
    go func() {
        defer func() { done <- struct{}{} }()
        buf := make([]byte, 32*1024)
        for {
            n, err := upstream.Read(buf)
            if n > 0 {
                session.RecordStdout(buf[:n])
                client.Write(buf[:n])
            }
            if err != nil { return }
        }
    }()

    <-done
}

The recording tees every byte without modifying the stream. The agent and the container communicate normally — the recording is completely transparent.

Session File Format

Each exec session is saved as a JSONL file with timestamped entries:

{"t": "2026-02-09T10:15:30.001Z", "d": "stdin",  "b": "bHMgLWxhCg=="}
{"t": "2026-02-09T10:15:30.050Z", "d": "stdout", "b": "dG90YWwgMjQK..."}
{"t": "2026-02-09T10:15:31.200Z", "d": "stdin",  "b": "Y2F0IC9ldGMvcGFzc3dk"}
{"t": "2026-02-09T10:15:31.250Z", "d": "stdout", "b": "cm9vdDp4OjA6..."}
  • t — timestamp with millisecond precision
  • d — direction: stdin or stdout
  • b — base64-encoded raw bytes

Base64 encoding preserves control characters, terminal escape sequences, and binary data without JSON escaping issues.

Forensic Replay

The iddio session replay command replays a recorded session in real-time (or at configurable speed):

iddio session replay --id exec_a1b2c3 --speed 2x

This reconstructs the terminal output exactly as it appeared during the original session, including colors, cursor movement, and timing. It’s forensic-quality: an auditor can see exactly what the agent did and what the container returned.

Audit Log Integration

Each exec session generates an audit event that links to the session recording:

{
  "timestamp": "2026-02-09T10:15:30Z",
  "agent": "claude-code",
  "method": "GET",
  "path": "/api/v1/namespaces/payments/pods/api-7d4b8f6c9/exec",
  "tier": 4,
  "decision": "allow",
  "session_type": "exec",
  "session_id": "exec_a1b2c3",
  "session_file": "~/.iddio/sessions/exec_a1b2c3.jsonl",
  "hash": "a3f8c2...",
  "prev_hash": "9e8d7c..."
}

The session file reference lets you jump from the audit log entry directly to the full recording.

Try It Yourself

Iddio is open source. Deploy a zero-trust command proxy for your AI agents in minutes.