tlsintent

package
v0.10.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package tlsintent reassembles the fragmented TLS plaintext that the eBPF SSL_write/SSL_read probes capture into COMPLETE HTTP/1.1 request/response messages, and pulls minimal LLM semantics (model, endpoint) out of the body.

It is the userspace half of "capture the agent's actual LLM traffic without instrumenting the agent": the kernel probe can only read a bounded buffer per call, so it emits ordered Chunks keyed by the TLS connection; this package accumulates them and parses the message once whole. Clean-room implementation (technique only; our own code, Go stdlib HTTP framing).

Scope: HTTP/1.1 with Content-Length, chunked, and SSE (streaming) bodies, and HTTP/2 (h2 frames + HPACK, see http2.go) which dominates real LLM APIs. Platform-neutral (no eBPF/Linux deps) so it unit-tests anywhere.

Index

Constants

View Source
const (
	GapCaptureLimit  = "capture_limit"
	GapBufferLimit   = "reassembly_limit"
	GapHTTP2Frames   = "http2_frame_sequence"
	GapHTTP2Push     = "unsupported_header_frame"
	GapHPACK         = "hpack_decode_failed"
	GapStreamLimit   = "connection_state_evicted"
	GapCallSequence  = "tls_call_sequence"
	GapChunkSequence = "tls_chunk_sequence"
	GapSequenceReset = "tls_sequence_reset"
	GapChunkMetadata = "invalid_tls_chunk_metadata"
	DataHTTPBody     = "http_body"
	DataTLSFragment  = "tls_fragment"
	DataHPACK        = "hpack_fragment"
	DataHTTP2Body    = "http2_data_fragment"
)
View Source
const (
	Request  = "request"  // SSL_write: bytes the agent sent (the LLM request)
	Response = "response" // SSL_read: bytes the agent received (the LLM response)
)

Direction of a captured segment relative to the agent.

View Source
const (
	ProtoHTTP10 = "http/1.0"
	ProtoHTTP11 = "http/1.1"
	ProtoH2     = "h2" // parsed from h2 frames + HPACK (see http2.go)
)

Protocol values on a reassembled Message.

View Source
const CallCaptureLimit = 4096

CallCaptureLimit matches SSL_CHUNK * SSL_MAX_CHUNKS in the native producer. It bounds call assembly independently of the HTTP message retention budget.

View Source
const CaptureRule = "agentprov.tls_reassembly/v3"

Variables

This section is empty.

Functions

This section is empty.

Types

type CaptureIntegrity added in v0.10.1

type CaptureIntegrity struct {
	Rule     string         `json:"rule"`
	State    string         `json:"state"`
	Reason   string         `json:"reason"`
	DataKind string         `json:"data_kind"`
	Position *ChunkPosition `json:"position,omitempty"`
}

func (*CaptureIntegrity) Incomplete added in v0.10.1

func (c *CaptureIntegrity) Incomplete() bool

type CaptureLoss added in v0.8.0

type CaptureLoss struct {
	Streams uint64
	Bytes   uint64
}

CaptureLoss counts plaintext lost to parser limits, invalidated connection state or stale generations. It is separate from kernel ring-buffer drops.

type Chunk

type Chunk struct {
	PID            uint32
	Conn           uint64 // TLS object address; reusable across connection lifetimes
	ProcessStartNS uint64
	Epoch          uint64 // capture generation; zero for producers without lifecycle probes
	Direction      string // Request or Response
	Data           []byte
	Truncated      bool // the kernel dropped bytes past its capture cap for this call
	Position       *ChunkPosition
	Checkpoint     bool // producer's last call number at a lifecycle boundary; no data
}

Chunk is one captured segment of TLS plaintext. Native Position metadata checks call continuity and byte order within a process/connection lifetime. Producers without positions retain best-effort ordered-stream parsing.

type ChunkPosition added in v0.10.1

type ChunkPosition struct {
	StreamID     uint64 `json:"stream_id,string"`
	CallSequence uint64 `json:"call_sequence,string"`
	Offset       uint32 `json:"offset"`
	TotalBytes   uint32 `json:"total_bytes"`
	Checkpoint   bool   `json:"checkpoint,omitempty"`
}

type Message

type Message struct {
	PID            uint32
	Conn           uint64
	ProcessStartNS uint64
	Epoch          uint64
	Direction      string // Request or Response
	Protocol       string // ProtoHTTP11 or ProtoH2
	StreamID       uint32 // HTTP/2 stream ID; zero for HTTP/1.x

	// HTTP surface.
	Method  string // request only
	Path    string // request only
	Host    string // request only
	Status  int    // response only
	Headers map[string]string
	Body    []byte // decoded body: dechunked and, for SSE, the joined data: payloads

	Truncated  bool           // some bytes were dropped, so Body may be incomplete
	CaptureGap string         // why framing or retained content is incomplete
	DataKind   string         // empty/http_body for decoded bodies; otherwise an undecoded fragment
	Position   *ChunkPosition // position of a contiguous raw TLS fragment, not a decoded HTTP body

	// Minimal LLM semantics (best-effort; empty when not an LLM call).
	Model    string
	Endpoint string // request: host+path
}

Message holds a decoded HTTP message or an explicitly incomplete fragment.

type Reassembler

type Reassembler struct {
	MaxBytes   int // per-direction retained byte cap, including HTTP/2
	MaxStreams int // active connection-directions; 0 -> 128
	// contains filtered or unexported fields
}

Reassembler accumulates Chunks per connection+direction and returns any messages that became complete. It handles HTTP keep-alive (several messages on one connection) by keeping the pipelined remainder after each complete message.

func NewReassembler

func NewReassembler() *Reassembler

NewReassembler returns an empty Reassembler.

func (*Reassembler) Add

func (r *Reassembler) Add(c Chunk) (messages []Message)

Add returns completed messages and any fragments released after a capture gap.

func (*Reassembler) EndProcess added in v0.10.0

func (r *Reassembler) EndProcess(pid uint32, start uint64, exec bool) []Message

EndProcess matches the kernel process lifetime, not just the reusable PID. Exec discards old address-space state; exit can emit remaining partial data.

func (*Reassembler) Flush

func (r *Reassembler) Flush(pid uint32, conn uint64) []Message

Flush emits whatever is buffered for a connection as a best-effort (possibly truncated) message and forgets its streams. Call it when a connection closes (e.g. on process_exit) so a close-framed response with no Content-Length isn't lost.

func (*Reassembler) FlushPID added in v0.8.0

func (r *Reassembler) FlushPID(pid uint32) []Message

FlushPID frees every TLS connection belonging to an exited process. It also emits close-framed or partial messages with their existing truncated marker.

func (*Reassembler) Release added in v0.10.0

func (r *Reassembler) Release(c Chunk) []Message

Release flushes only the matching generation. SSL_free can decrement a live object's reference count, so retained data is emitted as best-effort and a later use starts a fresh capture generation in the producer.

func (*Reassembler) Reset added in v0.10.0

func (r *Reassembler) Reset(c Chunk)

Reset marks a capture boundary, not a proven TCP close. A clear/rebind attempt may retain the SSL object while invalidating its HTTP and HPACK context.

func (*Reassembler) TakeLoss added in v0.8.0

func (r *Reassembler) TakeLoss() CaptureLoss

TakeLoss returns and resets loss counters since the previous call.

type Semantics

type Semantics struct {
	Model        string   `json:"model,omitempty"`
	MessageCount int      `json:"message_count,omitempty"` // request: number of messages
	HasSystem    bool     `json:"has_system,omitempty"`    // request: a system prompt is present
	ToolsOffered []string `json:"tools_offered,omitempty"` // request: tool names offered to the model
	ToolCalls    []string `json:"tool_calls,omitempty"`    // response: tool names the model decided to call
	ToolCommands []string `json:"tool_commands,omitempty"` // response: the shell command(s) the model decided to run
	StopReason   string   `json:"stop_reason,omitempty"`   // response: why generation stopped
}

Semantics is the minimal structured intent pulled from an LLM request/response body, tolerant across the common provider shapes (Anthropic Messages, OpenAI Chat Completions). Empty fields mean "not present / not an LLM call".

func ParseSemantics

func ParseSemantics(direction string, body []byte) Semantics

ParseSemantics extracts structured intent from a request or response body.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL