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
- type CaptureIntegrity
- type CaptureLoss
- type Chunk
- type ChunkPosition
- type Message
- type Reassembler
- func (r *Reassembler) Add(c Chunk) (messages []Message)
- func (r *Reassembler) EndProcess(pid uint32, start uint64, exec bool) []Message
- func (r *Reassembler) Flush(pid uint32, conn uint64) []Message
- func (r *Reassembler) FlushPID(pid uint32) []Message
- func (r *Reassembler) Release(c Chunk) []Message
- func (r *Reassembler) Reset(c Chunk)
- func (r *Reassembler) TakeLoss() CaptureLoss
- type Semantics
Constants ¶
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" )
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.
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.
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.
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
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 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 ¶
ParseSemantics extracts structured intent from a request or response body.