capture

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Nov 10, 2026 License: MIT Imports: 42 Imported by: 0

Documentation

Overview

Package capture stores bounded, best-effort diagnostics for requests under declared webhook prefixes and serves guarded viewer routes. Prefixes and full-body mode are Go declarations (application.Builder.CaptureWebhooks); deployment settings can only reduce capture, never extend it.

Index

Constants

View Source
const (
	MigrationOrigin migrate.Origin  = "foundry.webhook_capture"
	CreateTable     migrate.ID      = "000001_create_webhook_captures"
	Introduced      migrate.Version = "v0.17.0"
)

Variables

This section is empty.

Functions

func CaptureBodyOmissionCodec

func CaptureBodyOmissionCodec() codec.Codec[CaptureBodyOmission]

CaptureBodyOmissionCodec applies the generated membership rule at both SQL boundaries. Example: CaptureBodyOmissionCodec()

func CaptureBodyStateCodec

func CaptureBodyStateCodec() codec.Codec[CaptureBodyState]

CaptureBodyStateCodec applies the generated membership rule at both SQL boundaries. Example: CaptureBodyStateCodec()

func CaptureModeCodec

func CaptureModeCodec() codec.Codec[CaptureMode]

CaptureModeCodec applies the generated membership rule at both SQL boundaries. Example: CaptureModeCodec()

func CaptureOmissionCodec

func CaptureOmissionCodec() codec.Codec[CaptureOmission]

CaptureOmissionCodec applies the generated membership rule at both SQL boundaries. Example: CaptureOmissionCodec()

func CaptureResponseStateCodec

func CaptureResponseStateCodec() codec.Codec[CaptureResponseState]

CaptureResponseStateCodec applies the generated membership rule at both SQL boundaries. Example: CaptureResponseStateCodec()

func CapturedFieldJSON

func CapturedFieldJSON() foundrycontract.JSON[CapturedField]

CapturedFieldJSON describes the concrete CapturedField JSON boundary. Reuse this immutable descriptor for decoding and client contract export. Fields are discovered from Go declarations; persistence models are not response DTOs. Example: CapturedFieldJSON()

func CapturedRequestDetailJSON

func CapturedRequestDetailJSON() foundrycontract.JSON[CapturedRequestDetail]

CapturedRequestDetailJSON describes the concrete CapturedRequestDetail JSON boundary. Reuse this immutable descriptor for decoding and client contract export. Fields are discovered from Go declarations; persistence models are not response DTOs. Example: CapturedRequestDetailJSON()

func CapturedRequestFiltersDescriptor

func CapturedRequestFiltersDescriptor() foundryhttp.Query[CapturedRequestFilters]

CapturedRequestFiltersDescriptor binds query parameters to concrete CapturedRequestFilters fields. Reuse this descriptor for request decoding and query URL encoding. Optional fields preserve omission; repeated fields retain slice order.

Example: CapturedRequestFiltersDescriptor()

func CapturedRequestJSON

func CapturedRequestJSON() foundrycontract.JSON[CapturedRequest]

CapturedRequestJSON describes the concrete CapturedRequest JSON boundary. Reuse this immutable descriptor for decoding and client contract export. Fields are discovered from Go declarations; persistence models are not response DTOs. Example: CapturedRequestJSON()

func CapturedRequestPathDescriptor

func CapturedRequestPathDescriptor() foundryhttp.Path[CapturedRequestPath]

CapturedRequestPathDescriptor binds the declared pattern to concrete CapturedRequestPath fields. Reuse this descriptor for route registration and named URL generation.

func Command

func Command(construct func(foundation.Resolver) (*Store, error)) (cli.Declaration, error)

Command declares `webhook-captures prune` for the ordinary CLI registry. construct resolves a store after boot; it works after capture is disabled or removed, as long as the table's database and schema are configured.

Example: capture.Command(func(r foundation.Resolver) (*capture.Store, error) { return foundation.Resolve(r, key) })

func Migrations

func Migrations() []migrate.Definition

Migrations returns the framework-owned capture table. Application assembly registers it for the configured database and schema; apply it with the ordinary migration command before capture can store rows. Rolling back the framework keeps the table.

func Module

func Module(name foundation.ProviderID, key foundation.Key[*Store], requires []foundation.ProviderID, construct func(foundation.Resolver) (*Store, error)) foundation.Module

Module registers the store factory and, at boot, its cleanup before any work starts. The writer and retention loop run beside the HTTP kernel, where captures originate, or after Start without a selected kernel; CLI and worker processes resolve the store for reads and the prune command. Cleanup runs after the HTTP kernel has drained and waits for Close to finish. Requires must name every borrowed resource owner.

Example: capture.Module("app.webhook-captures", key, requires, construct)

func NewRecorder

func NewRecorder(config Config) (*httpcapture.Recorder, error)

NewRecorder validates config and builds the static in-memory handoff that the HTTP kernel fills and the Store drains. It performs no I/O and starts no goroutine. Application assembly passes it to http.WithWebhookCapture and New.

func Routes

func Routes[M any](group http.Scope, captures *Store, guard http.GuardBinding[M], access ...http.GuardOption[M]) ([]http.RouteRegistration, error)

Routes declares the read-only viewer within group under /webhook-captures:

GET ""           ID <group>.webhook-captures.list  newest-first cursor page of CapturedRequest
GET "/{request}" ID <group>.webhook-captures.show  CapturedRequestDetail, 404 when unknown or expired

Both routes authenticate through guard, require every access option before decoding the query or path, and send Cache-Control: no-store. access must not be empty: mounting the viewer always states who may read captured payloads. List requires a from time and a window of at most 30 days; pages hold 30 items by default and at most 100. Declarations are validated before return.

Example: capture.Routes(group, captures, admins, http.Scopes(developerRead), http.Permissions(developerPermission))

Types

type CaptureBodyOmission

type CaptureBodyOmission string

CaptureBodyOmission explains why a body was not stored. Completeness comes only from an observed end of stream, never from a successful parse.

const (
	// OmittedUnread: the request finished before capture read its body, as
	// with maintenance, capacity or declared-length rejections.
	OmittedUnread CaptureBodyOmission = "unread"
	// OmittedOverCaptureLimit: the body exceeded the configured capture limit.
	OmittedOverCaptureLimit CaptureBodyOmission = "over_capture_limit"
	// OmittedBodyLimit: the server body limit stopped reading.
	OmittedBodyLimit CaptureBodyOmission = "body_limit"
	// OmittedClientDisconnect: the client closed the stream early.
	OmittedClientDisconnect CaptureBodyOmission = "client_disconnect"
	// OmittedReadTimeout: the read deadline expired.
	OmittedReadTimeout CaptureBodyOmission = "read_timeout"
	// OmittedReadFailed: another transport read failure.
	OmittedReadFailed CaptureBodyOmission = "read_failed"
	// OmittedContentEncoded: redacted mode cannot inspect compressed bodies.
	OmittedContentEncoded CaptureBodyOmission = "content_encoded"
	// OmittedUnsupportedMediaType: redacted mode stores only JSON, URL-encoded
	// and multipart form bodies.
	OmittedUnsupportedMediaType CaptureBodyOmission = "unsupported_media_type"
	// OmittedMalformed: the body did not parse as its declared media type and
	// is never stored raw.
	OmittedMalformed CaptureBodyOmission = "malformed"
	// OmittedRedactionLimit: nesting, token, part or output bounds were exceeded.
	OmittedRedactionLimit CaptureBodyOmission = "redaction_limit"
)

func CaptureBodyOmissionValues

func CaptureBodyOmissionValues() []CaptureBodyOmission

CaptureBodyOmissionValues returns declared values in source order, with independent slice ownership. Example: CaptureBodyOmissionValues()

func ParseCaptureBodyOmission

func ParseCaptureBodyOmission(text string) (CaptureBodyOmission, error)

ParseCaptureBodyOmission decodes and validates an enum's text representation. Example: ParseCaptureBodyOmission(text)

func (CaptureBodyOmission) EnumDescriptor

EnumDescriptor describes this enum type independently of the receiver's value.

func (CaptureBodyOmission) IsValid

func (v CaptureBodyOmission) IsValid() bool

IsValid checks membership; a Go cast alone does not validate external values.

func (CaptureBodyOmission) MarshalJSON

func (v CaptureBodyOmission) MarshalJSON() ([]byte, error)

MarshalJSON encodes a validated enum as JSON.

func (CaptureBodyOmission) MarshalText

func (v CaptureBodyOmission) MarshalText() ([]byte, error)

MarshalText encodes a validated enum as text.

func (*CaptureBodyOmission) Scan

func (v *CaptureBodyOmission) Scan(source any) error

Scan changes the receiver only after successful decoding. SQL NULL is rejected.

func (*CaptureBodyOmission) UnmarshalJSON

func (v *CaptureBodyOmission) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes validated JSON and rejects null.

func (*CaptureBodyOmission) UnmarshalText

func (v *CaptureBodyOmission) UnmarshalText(data []byte) error

UnmarshalText decodes validated enum text and changes the receiver only on success.

func (CaptureBodyOmission) Validate

func (v CaptureBodyOmission) Validate() error

Validate reports invalid values without formatting the input.

func (CaptureBodyOmission) Value

func (v CaptureBodyOmission) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer through the shared typed codec.

type CaptureBodyState

type CaptureBodyState string

CaptureBodyState says what a capture's stored body holds.

const (
	// BodyEmpty is a complete request without body bytes.
	BodyEmpty CaptureBodyState = "empty"
	// BodyExact holds the complete body bytes exactly as received (full mode).
	BodyExact CaptureBodyState = "exact"
	// BodyJSON holds the redacted JSON document.
	BodyJSON CaptureBodyState = "json"
	// BodyForm holds redacted URL-encoded fields as a JSON array of
	// {"name","values"} objects.
	BodyForm CaptureBodyState = "form"
	// BodyMultipart holds a JSON array describing each part; file contents
	// are never stored.
	BodyMultipart CaptureBodyState = "multipart"
	// BodyOmitted stores nothing; CaptureBodyOmission says why.
	BodyOmitted CaptureBodyState = "omitted"
)

func CaptureBodyStateValues

func CaptureBodyStateValues() []CaptureBodyState

CaptureBodyStateValues returns declared values in source order, with independent slice ownership. Example: CaptureBodyStateValues()

func ParseCaptureBodyState

func ParseCaptureBodyState(text string) (CaptureBodyState, error)

ParseCaptureBodyState decodes and validates an enum's text representation. Example: ParseCaptureBodyState(text)

func (CaptureBodyState) EnumDescriptor

EnumDescriptor describes this enum type independently of the receiver's value.

func (CaptureBodyState) IsValid

func (v CaptureBodyState) IsValid() bool

IsValid checks membership; a Go cast alone does not validate external values.

func (CaptureBodyState) MarshalJSON

func (v CaptureBodyState) MarshalJSON() ([]byte, error)

MarshalJSON encodes a validated enum as JSON.

func (CaptureBodyState) MarshalText

func (v CaptureBodyState) MarshalText() ([]byte, error)

MarshalText encodes a validated enum as text.

func (*CaptureBodyState) Scan

func (v *CaptureBodyState) Scan(source any) error

Scan changes the receiver only after successful decoding. SQL NULL is rejected.

func (*CaptureBodyState) UnmarshalJSON

func (v *CaptureBodyState) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes validated JSON and rejects null.

func (*CaptureBodyState) UnmarshalText

func (v *CaptureBodyState) UnmarshalText(data []byte) error

UnmarshalText decodes validated enum text and changes the receiver only on success.

func (CaptureBodyState) Validate

func (v CaptureBodyState) Validate() error

Validate reports invalid values without formatting the input.

func (CaptureBodyState) Value

func (v CaptureBodyState) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer through the shared typed codec.

type CaptureMode

type CaptureMode string

CaptureMode is the body disclosure policy a capture was stored under.

const (
	// Redacted masks credential-named fields, shortens long values and
	// summarizes multipart files. It is the default for every prefix.
	Redacted CaptureMode = "redacted"
	// Full stores complete bodies within the capture limit exactly as received.
	// Only a Go declaration with http.FullCaptureBody selects it.
	Full CaptureMode = "full"
)

func CaptureModeValues

func CaptureModeValues() []CaptureMode

CaptureModeValues returns declared values in source order, with independent slice ownership. Example: CaptureModeValues()

func ParseCaptureMode

func ParseCaptureMode(text string) (CaptureMode, error)

ParseCaptureMode decodes and validates an enum's text representation. Example: ParseCaptureMode(text)

func (CaptureMode) EnumDescriptor

func (CaptureMode) EnumDescriptor() enum.Descriptor[CaptureMode]

EnumDescriptor describes this enum type independently of the receiver's value.

func (CaptureMode) IsValid

func (v CaptureMode) IsValid() bool

IsValid checks membership; a Go cast alone does not validate external values.

func (CaptureMode) MarshalJSON

func (v CaptureMode) MarshalJSON() ([]byte, error)

MarshalJSON encodes a validated enum as JSON.

func (CaptureMode) MarshalText

func (v CaptureMode) MarshalText() ([]byte, error)

MarshalText encodes a validated enum as text.

func (*CaptureMode) Scan

func (v *CaptureMode) Scan(source any) error

Scan changes the receiver only after successful decoding. SQL NULL is rejected.

func (*CaptureMode) UnmarshalJSON

func (v *CaptureMode) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes validated JSON and rejects null.

func (*CaptureMode) UnmarshalText

func (v *CaptureMode) UnmarshalText(data []byte) error

UnmarshalText decodes validated enum text and changes the receiver only on success.

func (CaptureMode) Validate

func (v CaptureMode) Validate() error

Validate reports invalid values without formatting the input.

func (CaptureMode) Value

func (v CaptureMode) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer through the shared typed codec.

type CaptureOmission

type CaptureOmission string

CaptureOmission marks request or response metadata that was cut to its bound or left out.

const (
	MethodTruncated           CaptureOmission = "method_truncated"
	PathTruncated             CaptureOmission = "path_truncated"
	QueryTruncated            CaptureOmission = "query_truncated"
	QueryMalformed            CaptureOmission = "query_malformed"
	HeadersTruncated          CaptureOmission = "headers_truncated"
	ContentTypeTruncated      CaptureOmission = "content_type_truncated"
	ContentEncodingTruncated  CaptureOmission = "content_encoding_truncated"
	ResponseTruncated         CaptureOmission = "response_truncated"
	ResponseMetadataTruncated CaptureOmission = "response_metadata_truncated"
)

func CaptureOmissionValues

func CaptureOmissionValues() []CaptureOmission

CaptureOmissionValues returns declared values in source order, with independent slice ownership. Example: CaptureOmissionValues()

func ParseCaptureOmission

func ParseCaptureOmission(text string) (CaptureOmission, error)

ParseCaptureOmission decodes and validates an enum's text representation. Example: ParseCaptureOmission(text)

func (CaptureOmission) EnumDescriptor

func (CaptureOmission) EnumDescriptor() enum.Descriptor[CaptureOmission]

EnumDescriptor describes this enum type independently of the receiver's value.

func (CaptureOmission) IsValid

func (v CaptureOmission) IsValid() bool

IsValid checks membership; a Go cast alone does not validate external values.

func (CaptureOmission) MarshalJSON

func (v CaptureOmission) MarshalJSON() ([]byte, error)

MarshalJSON encodes a validated enum as JSON.

func (CaptureOmission) MarshalText

func (v CaptureOmission) MarshalText() ([]byte, error)

MarshalText encodes a validated enum as text.

func (*CaptureOmission) Scan

func (v *CaptureOmission) Scan(source any) error

Scan changes the receiver only after successful decoding. SQL NULL is rejected.

func (*CaptureOmission) UnmarshalJSON

func (v *CaptureOmission) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes validated JSON and rejects null.

func (*CaptureOmission) UnmarshalText

func (v *CaptureOmission) UnmarshalText(data []byte) error

UnmarshalText decodes validated enum text and changes the receiver only on success.

func (CaptureOmission) Validate

func (v CaptureOmission) Validate() error

Validate reports invalid values without formatting the input.

func (CaptureOmission) Value

func (v CaptureOmission) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer through the shared typed codec.

type CaptureResponseState

type CaptureResponseState string

CaptureResponseState records how the handler's response ended. It describes writes the server accepted, not delivery to the caller.

const (
	// ResponseComplete: the handler returned and every write was accepted.
	ResponseComplete CaptureResponseState = "complete"
	// ResponseWriteFailed: a response write failed.
	ResponseWriteFailed CaptureResponseState = "write_failed"
	// ResponseAborted: a raw panic aborted the connection.
	ResponseAborted CaptureResponseState = "aborted"
	// ResponseHijacked: the handler took over the connection.
	ResponseHijacked CaptureResponseState = "hijacked"
)

func CaptureResponseStateValues

func CaptureResponseStateValues() []CaptureResponseState

CaptureResponseStateValues returns declared values in source order, with independent slice ownership. Example: CaptureResponseStateValues()

func ParseCaptureResponseState

func ParseCaptureResponseState(text string) (CaptureResponseState, error)

ParseCaptureResponseState decodes and validates an enum's text representation. Example: ParseCaptureResponseState(text)

func (CaptureResponseState) EnumDescriptor

EnumDescriptor describes this enum type independently of the receiver's value.

func (CaptureResponseState) IsValid

func (v CaptureResponseState) IsValid() bool

IsValid checks membership; a Go cast alone does not validate external values.

func (CaptureResponseState) MarshalJSON

func (v CaptureResponseState) MarshalJSON() ([]byte, error)

MarshalJSON encodes a validated enum as JSON.

func (CaptureResponseState) MarshalText

func (v CaptureResponseState) MarshalText() ([]byte, error)

MarshalText encodes a validated enum as text.

func (*CaptureResponseState) Scan

func (v *CaptureResponseState) Scan(source any) error

Scan changes the receiver only after successful decoding. SQL NULL is rejected.

func (*CaptureResponseState) UnmarshalJSON

func (v *CaptureResponseState) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes validated JSON and rejects null.

func (*CaptureResponseState) UnmarshalText

func (v *CaptureResponseState) UnmarshalText(data []byte) error

UnmarshalText decodes validated enum text and changes the receiver only on success.

func (CaptureResponseState) Validate

func (v CaptureResponseState) Validate() error

Validate reports invalid values without formatting the input.

func (CaptureResponseState) Value

func (v CaptureResponseState) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer through the shared typed codec.

type CapturedField

type CapturedField struct {
	Name   string   `json:"name"`
	Values []string `json:"values"`
}

CapturedField is one header, query or form name with its values. Masked credential values read "[masked]".

type CapturedFieldValidationFieldSet

type CapturedFieldValidationFieldSet struct {
	// Name validates the declared JSON property "name".
	Name foundryvalidation.Field[CapturedField, string]
	// Values validates the declared JSON property "values".
	Values foundryvalidation.Field[CapturedField, []string]
}

CapturedFieldValidationFieldSet retains concrete DTO and value types for validation. Example: CapturedFieldValidationFields()

func CapturedFieldValidationFields

func CapturedFieldValidationFields() CapturedFieldValidationFieldSet

CapturedFieldValidationFields returns typed field descriptors. Example: CapturedFieldValidationFields() Compose rules with these fields; JSON names and selectors are generated.

type CapturedRequest

type CapturedRequest struct {
	RequestID  attribution.RequestID `json:"request_id"`
	ReceivedAt temporal.DateTime     `json:"received_at"`
	Method     string                `json:"method"`
	// Path is the escaped request path as sent, at most 4 KiB.
	Path string `json:"path"`
	// Route is the matched route ID; null when no route matched.
	Route    value.Nullable[string] `json:"route"`
	ClientIP value.Nullable[string] `json:"client_ip"`
	// Status is the committed response status; null when none was sent.
	Status        value.Nullable[int32] `json:"status"`
	Outcome       observability.Outcome `json:"outcome"`
	ResponseState CaptureResponseState  `json:"response_state"`
	DurationMS    int64                 `json:"duration_ms"`
	Mode          CaptureMode           `json:"mode"`
	BodyState     CaptureBodyState      `json:"body_state"`
	// BodyOmission is set exactly when BodyState is omitted.
	BodyOmission value.Nullable[CaptureBodyOmission] `json:"body_omission"`
	// DeclaredBytes is the request Content-Length; null when absent.
	DeclaredBytes value.Nullable[int64] `json:"declared_bytes"`
	// MeasuredBytes is the body size, known only after the end of the stream.
	MeasuredBytes value.Nullable[int64] `json:"measured_bytes"`
	// ObservedBytes counts body bytes read from the client.
	ObservedBytes int64 `json:"observed_bytes"`
	// DroppedBefore counts captures the writing process lost after its
	// previous stored capture and before this one.
	DroppedBefore int64 `json:"dropped_before"`
}

CapturedRequest is one list item and the head of a detail. It never carries headers, query values or bodies.

type CapturedRequestDetail

type CapturedRequestDetail struct {
	Request             CapturedRequest   `json:"request"`
	Headers             []CapturedField   `json:"headers"`
	Query               []CapturedField   `json:"query"`
	ContentType         string            `json:"content_type"`
	ContentEncoding     string            `json:"content_encoding"`
	Body                []byte            `json:"body"`
	ResponseContentType string            `json:"response_content_type"`
	ResponseEncoding    string            `json:"response_encoding"`
	ResponseBytes       int64             `json:"response_bytes"`
	ResponseHead        []byte            `json:"response_head"`
	Omissions           []CaptureOmission `json:"omissions"`
}

CapturedRequestDetail is one capture with its stored metadata and bodies. Body and ResponseHead are base64 in JSON. The response head is the first 4 KiB the server accepted for writing; it is not redacted.

type CapturedRequestDetailValidationFieldSet

type CapturedRequestDetailValidationFieldSet struct {
	// Body validates the declared JSON property "body".
	Body foundryvalidation.Field[CapturedRequestDetail, []byte]
	// ContentEncoding validates the declared JSON property "content_encoding".
	ContentEncoding foundryvalidation.Field[CapturedRequestDetail, string]
	// ContentType validates the declared JSON property "content_type".
	ContentType foundryvalidation.Field[CapturedRequestDetail, string]
	// Headers validates the declared JSON property "headers".
	Headers foundryvalidation.Field[CapturedRequestDetail, []CapturedField]
	// Omissions validates the declared JSON property "omissions".
	Omissions foundryvalidation.Field[CapturedRequestDetail, []CaptureOmission]
	// Query validates the declared JSON property "query".
	Query foundryvalidation.Field[CapturedRequestDetail, []CapturedField]
	// Request validates the declared JSON property "request".
	Request foundryvalidation.Field[CapturedRequestDetail, CapturedRequest]
	// ResponseBytes validates the declared JSON property "response_bytes".
	ResponseBytes foundryvalidation.Field[CapturedRequestDetail, int64]
	// ResponseContentType validates the declared JSON property "response_content_type".
	ResponseContentType foundryvalidation.Field[CapturedRequestDetail, string]
	// ResponseEncoding validates the declared JSON property "response_encoding".
	ResponseEncoding foundryvalidation.Field[CapturedRequestDetail, string]
	// ResponseHead validates the declared JSON property "response_head".
	ResponseHead foundryvalidation.Field[CapturedRequestDetail, []byte]
}

CapturedRequestDetailValidationFieldSet retains concrete DTO and value types for validation. Example: CapturedRequestDetailValidationFields()

func CapturedRequestDetailValidationFields

func CapturedRequestDetailValidationFields() CapturedRequestDetailValidationFieldSet

CapturedRequestDetailValidationFields returns typed field descriptors. Example: CapturedRequestDetailValidationFields() Compose rules with these fields; JSON names and selectors are generated.

type CapturedRequestFilters

type CapturedRequestFilters struct {
	From  temporal.DateTime                 `query:"from"`
	Until value.Optional[temporal.DateTime] `query:"until"`
	// Method matches the request method exactly.
	Method value.Optional[string] `query:"method"`
	// PathPrefix matches captures whose escaped path starts with this text.
	PathPrefix value.Optional[string]      `query:"path_prefix"`
	Status     value.Optional[int32]       `query:"status"`
	Mode       value.Optional[CaptureMode] `query:"mode"`
}

CapturedRequestFilters narrows the list. From is required and the window spans at most 30 days; Until defaults to no upper bound.

Example: capture.CapturedRequestFilters{From: since, Method: value.Set("POST")}

type CapturedRequestPath

type CapturedRequestPath struct {
	Request attribution.RequestID `path:"request"`
}

CapturedRequestPath binds the show route's request ID.

type CapturedRequestValidationFieldSet

type CapturedRequestValidationFieldSet struct {
	// BodyOmission validates the declared JSON property "body_omission".
	//
	// BodyOmission is set exactly when BodyState is omitted.
	//
	BodyOmission foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[CaptureBodyOmission]]
	// BodyState validates the declared JSON property "body_state".
	BodyState foundryvalidation.Field[CapturedRequest, CaptureBodyState]
	// ClientIP validates the declared JSON property "client_ip".
	ClientIP foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[string]]
	// DeclaredBytes validates the declared JSON property "declared_bytes".
	//
	// DeclaredBytes is the request Content-Length; null when absent.
	//
	DeclaredBytes foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[int64]]
	// DroppedBefore validates the declared JSON property "dropped_before".
	//
	// DroppedBefore counts captures the writing process lost after its
	// previous stored capture and before this one.
	//
	DroppedBefore foundryvalidation.Field[CapturedRequest, int64]
	// DurationMS validates the declared JSON property "duration_ms".
	DurationMS foundryvalidation.Field[CapturedRequest, int64]
	// MeasuredBytes validates the declared JSON property "measured_bytes".
	//
	// MeasuredBytes is the body size, known only after the end of the stream.
	//
	MeasuredBytes foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[int64]]
	// Method validates the declared JSON property "method".
	Method foundryvalidation.Field[CapturedRequest, string]
	// Mode validates the declared JSON property "mode".
	Mode foundryvalidation.Field[CapturedRequest, CaptureMode]
	// ObservedBytes validates the declared JSON property "observed_bytes".
	//
	// ObservedBytes counts body bytes read from the client.
	//
	ObservedBytes foundryvalidation.Field[CapturedRequest, int64]
	// Outcome validates the declared JSON property "outcome".
	Outcome foundryvalidation.Field[CapturedRequest, observability.Outcome]
	// Path validates the declared JSON property "path".
	//
	// Path is the escaped request path as sent, at most 4 KiB.
	//
	Path foundryvalidation.Field[CapturedRequest, string]
	// ReceivedAt validates the declared JSON property "received_at".
	ReceivedAt foundryvalidation.Field[CapturedRequest, temporal.DateTime]
	// RequestID validates the declared JSON property "request_id".
	RequestID foundryvalidation.Field[CapturedRequest, attribution.RequestID]
	// ResponseState validates the declared JSON property "response_state".
	ResponseState foundryvalidation.Field[CapturedRequest, CaptureResponseState]
	// Route validates the declared JSON property "route".
	//
	// Route is the matched route ID; null when no route matched.
	//
	Route foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[string]]
	// Status validates the declared JSON property "status".
	//
	// Status is the committed response status; null when none was sent.
	//
	Status foundryvalidation.Field[CapturedRequest, foundryvalue.Nullable[int32]]
}

CapturedRequestValidationFieldSet retains concrete DTO and value types for validation. Example: CapturedRequestValidationFields()

func CapturedRequestValidationFields

func CapturedRequestValidationFields() CapturedRequestValidationFieldSet

CapturedRequestValidationFields returns typed field descriptors. Example: CapturedRequestValidationFields() Compose rules with these fields; JSON names and selectors are generated.

type Config

type Config struct {
	// BodyBytes bounds the request body read before the handler (1 KiB to
	// 8 MiB). A larger body is omitted; the handler still receives every byte.
	BodyBytes int
	// ValueBytes bounds a redacted JSON string or form, query or multipart
	// field value (64 bytes to 1 MiB); longer values read "[omitted N bytes]".
	ValueBytes int
	// ForceRedacted stores every prefix redacted, including prefixes declared
	// with http.FullCaptureBody.
	ForceRedacted bool
	// Rate admits at most this many captures per second per process (1 to
	// 10,000). Refused requests are served normally and counted as lost.
	Rate int
	// Burst is the admission burst (1 to 10,000).
	Burst int
	// QueueRows bounds captures being read, queued or written (1 to 256).
	QueueRows int
	// RetainedBytes bounds memory held by those captures (enough for one
	// capture, up to 1 GiB).
	RetainedBytes int64
	// Retention deletes captures older than this (1 hour to 365 days).
	Retention time.Duration
	// MaxRows keeps about this many newest captures in the table (1,000 to
	// 10,000,000). Pruning catches up every minute while over the cap.
	MaxRows int
}

Config holds deployment limits for one capture store. Settings load it as features.webhook_capture.config.

Example: config := capture.DefaultConfig(); config.Retention = 7 * 24 * time.Hour

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a 2 MiB body limit, 4 KiB values, 50 captures per second with a burst of 50, 256 queued captures within 64 MiB, 30-day retention and 100,000 rows.

func (Config) Validate

func (c Config) Validate() error

Validate checks every limit against its hard cap.

type Dependencies

type Dependencies struct {
	DB *database.DB
	// Schema holds foundry_webhook_captures; it must be a valid identifier.
	Schema string
	// Clock supplies retention cutoffs. Receive times come from the kernel.
	Clock  clock.Clock
	Logger *slog.Logger
}

Dependencies are borrowed; the Store never closes them.

type PruneCommand

type PruneCommand struct {
	// contains filtered or unexported fields
}

PruneCommand is a validated prune operation. Without --before it applies the configured retention and row cap; with it, only the explicit cutoff, which must not be later than now. Without --apply it only counts.

func ParsePrune

func ParsePrune(args []string, help io.Writer) (PruneCommand, error)

ParsePrune parses prune arguments without database I/O. It writes help on request and then returns flag.ErrHelp.

func (PruneCommand) Run

func (c PruneCommand) Run(ctx context.Context, captures *Store, output io.Writer) error

Run counts the matching captures or, with --apply, removes them in bounded transactions and reports the committed count, also when a later batch fails. It never prints captured data.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store owns the capture table: the single writer that drains the recorder, retention, and the bounded reads behind Routes. Applications hold it to mount Routes; Module drives its lifecycle. Construct it with New.

func New

func New(recorder *httpcapture.Recorder, deps Dependencies, config Config) (*Store, error)

New validates its inputs without I/O. A nil recorder gives a store that reads and prunes but never writes, as when capture is disabled at runtime.

Example: capture.New(recorder, capture.Dependencies{DB: db, Schema: "public", Clock: clock.System{}, Logger: logger}, capture.DefaultConfig())

func (*Store) Close

func (s *Store) Close(ctx context.Context) error

Close stops admission, writes what remains within a short drain budget and counts the rest as lost. It runs after the HTTP kernel has drained.

func (*Store) Done

func (s *Store) Done() <-chan struct{}

Done closes once Close has finished.

func (*Store) Format

func (*Store) Format(s fmt.State, _ rune)

Format writes a fixed label without exposing dependencies or captures.

func (*Store) Run

func (s *Store) Run(ctx context.Context) error

Run writes captures and prunes the table until ctx ends. Database trouble never stops it: an unavailable table degrades capture with one logged reason, failed batches are retried twice and then counted as lost.

Jump to

Keyboard shortcuts

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