Documentation
¶
Overview ¶
Package component defines the Discoverable interface and related types
Package component defines SemStreams component factories, lifecycle interfaces, dependencies, configuration schemas, ports, and immutable registry declarations.
Applications register component factories explicitly during process composition. Factories are side-effect free: they construct a Discoverable, while I/O begins in Start and all owned work joins Stop.
Component creation is a framework-internal boot operation. ComponentManager constructs every enabled component from its constructor-captured configuration, captures the component's declared ports and resources, and admits an immutable declaration to Registry. ComponentManager retains the concrete handle as the sole lifecycle owner.
Registry read surfaces return defensive values and never expose a live component handle. Once boot admission is complete, Registry is sealed; later factory or component admission attempts fail until another process boot creates a fresh Registry.
Discoverable components describe metadata, health, flow metrics, schemas, and typed ports. Port declarations support core NATS subjects, JetStream, KV watches and writes, request/reply, network listeners, APIs, and related framework patterns. FlowGraph consumes declaration values for static diagram validation without owning or mutating runtime lifecycle.
Package component provides base types and utilities for SemStreams components.
Package component provides port configuration and management for component connections.
Package component provides schema validation and helper functions ¶
Package component provides schema tag parsing and generation for component configuration.
The schema tag system eliminates duplication between Config structs and ConfigSchema definitions by auto-generating schemas from struct tags. This provides a single source of truth for configuration metadata and follows Go stdlib patterns (similar to json tags).
Basic Usage ¶
Define configuration with schema tags:
type MyConfig struct {
Name string `json:"name" schema:"type:string,description:Component name,category:basic"`
Port int `json:"port" schema:"type:int,description:Port,min:1,max:65535,default:8080"`
}
Generate schema at init time:
var schema = component.GenerateConfigSchema(reflect.TypeOf(MyConfig{}))
Tag Syntax ¶
Tags use comma-separated directives with colon-separated key-value pairs:
- type:string - Field data type (required)
- description:text - Field description (recommended)
- category:basic - UI organization (basic or advanced)
- default:value - Default value
- min:N, max:N - Numeric constraints
- enum:a|b|c - Valid enum values (pipe-separated)
- readonly, editable - Boolean flags for PortDefinition fields
- required, hidden - Boolean flags for validation and UI
Performance ¶
Schema generation uses reflection but is designed for init-time execution:
- Call GenerateConfigSchema once at package init
- Cache result in package-level variable
- Zero reflection cost at runtime
Error Handling ¶
Invalid tags result in graceful degradation:
- Fields with invalid tags are skipped
- Errors are wrapped with context using pkg/errors
- Missing descriptions use field names as fallback
See docs/architecture/SCHEMA_TAG_SPEC.md for complete specification.
Index ¶
- Constants
- func BenchmarkLifecycleMethods(b *testing.B, factory LifecycleFactory)
- func GenerateCacheFieldSchema() map[string]CacheFieldInfo
- func GeneratePortFieldSchema() map[string]PortFieldInfo
- func GetBool(config map[string]any, key string, defaultValue bool) bool
- func GetFloat64(config map[string]any, key string, defaultValue float64) float64
- func GetInt(config map[string]any, key string, defaultValue int) int
- func GetProperties(schema ConfigSchema, category string) map[string]PropertySchema
- func GetPropertyValue(config map[string]any, key string) (any, bool)
- func GetString(config map[string]any, key string, defaultValue string) string
- func IsComplexType(propType string) bool
- func IsLifecycleComponent(comp Discoverable) bool
- func ResolveSubject(ports []PortDefinition, portName, suffix string) (string, error)
- func SafeUnmarshal(rawConfig json.RawMessage, target any) error
- func SortedPropertyNames(schema ConfigSchema) []string
- func StandardLifecycleTests(t *testing.T, factory LifecycleFactory)
- func TestErrorInjection(t *testing.T, factory LifecycleFactory)
- func ValidateAndPersistComponentConfig(ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, ...) error
- func ValidateComponentName(name string) error
- func ValidateConfigKey(key string) error
- func ValidateFactoryConfig(rawConfig json.RawMessage) error
- func ValidateJSONSize(data json.RawMessage) error
- func ValidateNetworkConfig(port int, bindAddr string) error
- func ValidatePortNumber(port int) error
- type CacheFieldInfo
- type ConfigComponentRegistry
- type ConfigPersister
- type ConfigSchema
- type ConfigValidator
- type ConsumerConfig
- type DebugStatusProvider
- type Dependencies
- type Direction
- type Discoverable
- type ErrorInjectingComponent
- func (e *ErrorInjectingComponent) Initialize() error
- func (e *ErrorInjectingComponent) InjectInitializeError(err error)
- func (e *ErrorInjectingComponent) InjectStartError(err error)
- func (e *ErrorInjectingComponent) InjectStopError(err error)
- func (e *ErrorInjectingComponent) Start(ctx context.Context) error
- func (e *ErrorInjectingComponent) Stop(ctx context.Context) error
- type Factory
- type FilePort
- type FlowMetrics
- type HTTPClientPort
- type HealthStatus
- type Info
- type InteractionPattern
- type InterfaceContract
- type JetStreamPort
- type KVReadPort
- type KVWatchPort
- type KVWritePort
- type LifecycleComponent
- type LifecycleFactory
- type LogEntry
- type LogLevel
- type ManagedComponent
- type Metadata
- type NATSPort
- type NATSRequestPort
- type NetworkFacts
- type NetworkPort
- type PlatformMeta
- type Port
- type PortConfig
- type PortDefinition
- type PortFacts
- func (f PortFacts) ConnectionIDs() []string
- func (f PortFacts) InteractionPattern() InteractionPattern
- func (f PortFacts) Interface() (InterfaceContract, bool)
- func (f PortFacts) IsExclusive() bool
- func (f PortFacts) KVReadBucket() (string, bool)
- func (f PortFacts) Kind() PortKind
- func (f PortFacts) NATSSubjects() []string
- func (f PortFacts) Network() (NetworkFacts, bool)
- func (f PortFacts) ResourceID() string
- func (f PortFacts) StoreReadBucket() (string, bool)
- func (f PortFacts) Stream() (StreamFacts, bool)
- type PortFieldInfo
- type PortKind
- type Portable
- type ProcessorMetrics
- type PropertySchema
- type Registerable
- type Registration
- type RegistrationConfig
- type Registry
- func (r *Registry) CreateComponent(_ componentadmission.Access, instanceName string, config types.ComponentConfig, ...) (Discoverable, error)
- func (r *Registry) GetComponentSchema(name string) (ConfigSchema, error)
- func (r *Registry) GetFactory(name string) (Factory, bool)
- func (r *Registry) ListAvailable() map[string]Info
- func (r *Registry) ListComponentTypes() []string
- func (r *Registry) ListFactories() map[string]*Registration
- func (r *Registry) RegisterFactory(name string, registration *Registration) error
- func (r *Registry) RegisterWithConfig(config RegistrationConfig) error
- func (r *Registry) SealComposition(_ componentadmission.Access)
- func (r *Registry) Snapshot(_ componentadmission.Access, instanceName string) (declarationSnapshot, bool)
- func (r *Registry) Snapshots(_ componentadmission.Access) []declarationSnapshot
- type SchemaDirectives
- type SimpleMockComponent
- func (m *SimpleMockComponent) ConfigSchema() ConfigSchema
- func (m *SimpleMockComponent) DataFlow() FlowMetrics
- func (m *SimpleMockComponent) Health() HealthStatus
- func (m *SimpleMockComponent) InputPorts() []Port
- func (m *SimpleMockComponent) Meta() Metadata
- func (m *SimpleMockComponent) OutputPorts() []Port
- type State
- type StoreProvidePort
- type StoreProvider
- type StoreReadPort
- type StreamFacts
- func (f StreamFacts) AckPolicy() string
- func (f StreamFacts) AckWait() string
- func (f StreamFacts) ConsumerName() string
- func (f StreamFacts) DeliverPolicy() string
- func (f StreamFacts) HeartbeatInterval() string
- func (f StreamFacts) MaxAckPending() int
- func (f StreamFacts) MaxDeliver() int
- func (f StreamFacts) MaxSizeGB() int
- func (f StreamFacts) Name() string
- func (f StreamFacts) Replicas() int
- func (f StreamFacts) RetentionDays() int
- func (f StreamFacts) RetentionPolicy() string
- func (f StreamFacts) Storage() string
- func (f StreamFacts) Subjects() []string
- type TimerPort
- type ToolRegistryReader
- type Validatable
- type ValidationError
- func ValidateComponentConfig(ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, ...) []ValidationError
- func ValidateConfig(config map[string]any, schema ConfigSchema) []ValidationError
- func ValidateWithSchema(ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, ...) []ValidationError
Examples ¶
Constants ¶
const ( MaxStringLength = 1024 // Maximum length for string values MaxJSONSize = 1024 * 1024 // Maximum JSON size (1MB) MinPort = 1 // Minimum valid port number MaxPort = 65535 // Maximum valid port number MaxInt = math.MaxInt32 // Maximum safe integer value MinInt = math.MinInt32 // Minimum safe integer value )
Config validation constants - security limits
const ( // DepModelRegistry signals that a component consumes // Dependencies.ModelRegistry at construction or run time. Components // that declare this consume the model registry selected at boot. // Later model_registry writes require process restart. DepModelRegistry = "model-registry" )
Dependency identifiers used by Registration.Dependencies. Components declare these at registration to opt into framework-driven behavior (e.g., restart when a named runtime dependency changes). Kept as typed constants so refactors are compiler-checked.
Variables ¶
This section is empty.
Functions ¶
func BenchmarkLifecycleMethods ¶
func BenchmarkLifecycleMethods(b *testing.B, factory LifecycleFactory)
BenchmarkLifecycleMethods provides benchmark tests for lifecycle operations
func GenerateCacheFieldSchema ¶
func GenerateCacheFieldSchema() map[string]CacheFieldInfo
GenerateCacheFieldSchema generates metadata for cache.Config fields. This describes which fields in cache.Config are editable and their constraints, enabling the UI to render appropriate controls for cache configuration.
The function examines cache.Config struct tags to determine:
- Field types (for appropriate UI controls)
- Editability (whether users can modify the field)
- Enum values (for strategy field)
- Numeric constraints (for size limits)
All cache.Config fields are marked as "editable" to allow runtime configuration.
This metadata is included in ConfigSchema for fields with type "cache", allowing the frontend to correctly render cache configuration forms.
Returns:
- Map of field names to CacheFieldInfo with type, editability, and constraint metadata
func GeneratePortFieldSchema ¶
func GeneratePortFieldSchema() map[string]PortFieldInfo
GeneratePortFieldSchema returns the canonical common-envelope metadata. Kind-specific data remains inside config and is validated by the closed port binding table; schema generation does not reflect retired flat fields.
This metadata is included in ConfigSchema for fields with type "ports", allowing the frontend to correctly render port configuration forms.
Returns:
- Map of field names to PortFieldInfo with type and editability metadata
func GetBool ¶
GetBool safely extracts a boolean value from config with a default fallback and validation
func GetFloat64 ¶
GetFloat64 safely extracts a float64 value from config with a default fallback and validation
func GetInt ¶
GetInt safely extracts an integer value from config with a default fallback and bounds checking
func GetProperties ¶
func GetProperties(schema ConfigSchema, category string) map[string]PropertySchema
GetProperties filters schema properties by category for UI organization.
Components can categorize their configuration properties as "basic" (shown by default) or "advanced" (hidden in collapsible section). This function extracts properties belonging to a specific category.
Parameters:
- schema: The component's configuration schema
- category: Filter by "basic" or "advanced", or empty string for all properties
Properties without an explicit Category field default to "advanced".
Returns a map of property names to PropertySchema definitions matching the category.
Example:
schema := component.ConfigSchema{
Properties: map[string]component.PropertySchema{
"port": {Type: "int", Category: "basic"},
"buffer_size": {Type: "int", Category: "advanced"},
"timeout": {Type: "int"}, // Defaults to "advanced"
},
}
basicProps := component.GetProperties(schema, "basic")
// Returns: map["port": {...}]
advancedProps := component.GetProperties(schema, "advanced")
// Returns: map["buffer_size": {...}, "timeout": {...}]
func GetPropertyValue ¶
GetPropertyValue safely extracts a property value from a configuration map.
Returns the value and true if the key exists, or nil and false if the key is not present in the map. This function is nil-safe - passing a nil config will return (nil, false).
Example:
config := map[string]any{"port": 8080, "host": "localhost"}
if port, exists := component.GetPropertyValue(config, "port"); exists {
fmt.Printf("Port: %v\n", port)
}
func GetString ¶
GetString safely extracts a string value from config with a default fallback and validation
func IsComplexType ¶
IsComplexType returns true if a property type requires complex rendering.
Complex types (object, array) cannot be rendered as simple form inputs and require specialized UI components like JSON editors or nested form builders.
Currently identifies "object" and "array" types as complex. In MVP, the UI falls back to a JSON editor for these types.
Example:
if component.IsComplexType(propSchema.Type) {
// Use JSON editor fallback
renderJSONEditor(propSchema)
} else {
// Render type-specific input field
renderInputField(propSchema)
}
func IsLifecycleComponent ¶
func IsLifecycleComponent(comp Discoverable) bool
IsLifecycleComponent checks if a component supports lifecycle management
func ResolveSubject ¶
func ResolveSubject(ports []PortDefinition, portName, suffix string) (string, error)
ResolveSubject returns the configured NATS subject for one uniquely named port with a trailing wildcard replaced by suffix.
func SafeUnmarshal ¶
func SafeUnmarshal(rawConfig json.RawMessage, target any) error
SafeUnmarshal performs validated unmarshaling into a target struct It validates the JSON first, then unmarshals with additional type checking
func SortedPropertyNames ¶
func SortedPropertyNames(schema ConfigSchema) []string
SortedPropertyNames returns property names in UI display order.
Properties are sorted by: 1. Category: "basic" properties first, then "advanced" properties 2. Alphabetically within each category
This ensures consistent, predictable ordering in configuration UIs. Properties without an explicit Category default to "advanced".
Example:
schema := component.ConfigSchema{
Properties: map[string]component.PropertySchema{
"port": {Category: "basic"},
"bind_address": {Category: "basic"},
"buffer_size": {Category: "advanced"},
"timeout": {}, // Defaults to "advanced"
},
}
names := component.SortedPropertyNames(schema)
// Returns: ["bind_address", "port", "buffer_size", "timeout"]
// ^-- basic (alpha) --^ ^---- advanced (alpha) ----^
func StandardLifecycleTests ¶
func StandardLifecycleTests(t *testing.T, factory LifecycleFactory)
StandardLifecycleTests verifies the portable LifecycleComponent floor. Resource-specific drain ordering, blocked joins, and partial-acquisition rollback remain the responsibility of focused owner tests.
func TestErrorInjection ¶
func TestErrorInjection(t *testing.T, factory LifecycleFactory)
TestErrorInjection tests components with injected errors
func ValidateAndPersistComponentConfig ¶
func ValidateAndPersistComponentConfig( ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, persister ConfigPersister, componentName, componentType string, configJSON json.RawMessage, ) error
ValidateAndPersistComponentConfig validates a component config and persists it to KV via the supplied persister. Returns validation errors as the first error if validation fails, or a wrapped persistence error if KV write fails.
Persister parameter (typically a *natsclient.KVStore from config.Manager) is taken explicitly so component doesn't import config — that import direction is what created the cycle this helper exists to avoid.
func ValidateComponentName ¶
ValidateComponentName validates component/instance names for security
func ValidateConfigKey ¶
ValidateConfigKey checks if a configuration key is valid
func ValidateFactoryConfig ¶
func ValidateFactoryConfig(rawConfig json.RawMessage) error
ValidateFactoryConfig performs validation before passing to factory This is the main security gate for all component configurations
func ValidateJSONSize ¶
func ValidateJSONSize(data json.RawMessage) error
ValidateJSONSize checks if JSON input is within safe limits
func ValidateNetworkConfig ¶
ValidateNetworkConfig validates network configuration including port and bind address
func ValidatePortNumber ¶
ValidatePortNumber validates port numbers are within valid range
Types ¶
type CacheFieldInfo ¶
type CacheFieldInfo struct {
Type string `json:"type"`
Editable bool `json:"editable"`
Enum []string `json:"enum,omitempty"` // For strategy field
Min *int `json:"min,omitempty"` // For numeric fields
}
CacheFieldInfo describes metadata for cache.Config fields
type ConfigComponentRegistry ¶
type ConfigComponentRegistry interface {
GetComponentSchema(componentType string) (ConfigSchema, error)
}
ConfigComponentRegistry defines the interface needed for schema validation. Local-package interface so the validator helpers don't depend on the broader Registry surface.
type ConfigPersister ¶
type ConfigPersister interface {
Put(ctx context.Context, key string, value []byte) (uint64, error)
}
ConfigPersister is the minimal kvStore-shaped surface ValidateAndPersistComponentConfig needs. *natsclient.KVStore satisfies it. Defined here so config.Manager can pass its kvStore through without component importing config (which would cycle: component → config → component).
type ConfigSchema ¶
type ConfigSchema struct {
Properties map[string]PropertySchema `json:"properties"`
Required []string `json:"required"`
}
ConfigSchema describes the configuration parameters for a component
func GenerateConfigSchema ¶
func GenerateConfigSchema(configType reflect.Type) ConfigSchema
GenerateConfigSchema generates a ConfigSchema from a struct type using reflection. This function performs one-time reflection at initialization to extract schema metadata from struct field tags, eliminating the need for manual schema definitions.
Usage Pattern:
type MyComponentConfig struct {
Name string `json:"name" schema:"type:string,description:Name,category:basic"`
Port int `json:"port" schema:"type:int,description:Port,min:1,max:65535"`
}
var schema = component.GenerateConfigSchema(reflect.TypeOf(MyComponentConfig{}))
Field Processing:
- Only exported fields with both 'json' and 'schema' tags are included
- json:"-" fields are skipped
- Fields without schema tags are skipped
- Invalid schema tags result in skipped fields (graceful degradation)
Special Handling:
- Fields with type "ports" automatically include PortFieldSchema metadata
- Default values are converted from strings to appropriate types
- Required fields are added to the schema's Required list
Performance:
- Call once at init() time - reflection cost is paid only once
- Generated schemas are cached in package-level variables
- Zero reflection overhead at runtime
Parameters:
- configType: The reflect.Type of the config struct (use reflect.TypeOf(ConfigStruct{})) Pointer types are automatically dereferenced
Returns:
- ConfigSchema with Properties map and Required list populated from struct tags
- Empty schema for non-struct types
Example ¶
ExampleGenerateConfigSchema demonstrates how to use schema tags to auto-generate configuration schemas from struct definitions
package main
import (
"encoding/json"
"fmt"
"reflect"
"github.com/c360studio/semstreams/component"
)
func main() {
// Define a configuration struct with schema tags
type ComponentConfig struct {
// Basic configuration
Name string `json:"name" schema:"type:string,description:Component name,category:basic"`
Port int `json:"port" schema:"type:int,description:Listen port,min:1,max:65535,default:8080,category:basic"`
Enabled bool `json:"enabled" schema:"type:bool,description:Enable component,default:true,category:basic"`
// Advanced configuration
Timeout string `json:"timeout" schema:"type:string,description:Request timeout,default:30s,category:advanced"`
LogLevel string `json:"log_level" schema:"type:enum,description:Logging level,enum:debug|info|warn|error,default:info,category:advanced"`
// Required field
APIKey string `json:"api_key" schema:"required,type:string,description:Authentication API key"`
}
// Generate the schema at init time (one-time reflection cost)
schema := component.GenerateConfigSchema(reflect.TypeOf(ComponentConfig{}))
// The generated schema can be used for validation, UI generation, etc.
schemaJSON, _ := json.MarshalIndent(schema, "", " ")
fmt.Println(string(schemaJSON))
// Output will show the generated schema with all properties
}
Output:
type ConfigValidator ¶
type ConfigValidator struct {
// contains filtered or unexported fields
}
ConfigValidator provides secure validation for component configurations
func NewConfigValidator ¶
func NewConfigValidator() *ConfigValidator
NewConfigValidator creates a validator with secure defaults
func (*ConfigValidator) ValidateConfig ¶
func (v *ConfigValidator) ValidateConfig(rawConfig json.RawMessage) error
ValidateConfig performs comprehensive validation on raw JSON config This prevents injection attacks, resource exhaustion, and malformed input
type ConsumerConfig ¶
type ConsumerConfig struct {
DeliverPolicy string
AckPolicy string
MaxDeliver int
AckWait time.Duration
HeartbeatInterval time.Duration
MaxAckPending int // 0 = inherited/default/capped server policy; -1 = unlimited outstanding acks (gh#480)
}
ConsumerConfig holds extracted JetStream consumer configuration.
Duration fields (AckWait, HeartbeatInterval) are zero-valued when the port-level string is empty; consumers may apply their component default. Invalid declarations are rejected before a ConsumerConfig is returned.
func GetConsumerConfig ¶
func GetConsumerConfig(port Port) (ConsumerConfig, error)
GetConsumerConfig validates a JetStream port through the canonical facts projection and extracts its consumer configuration. A non-JetStream or invalid port returns an error rather than silently receiving defaults. Unset fields receive these defaults: - DeliverPolicy: "new" (safe default - don't replay historical messages) - AckPolicy: "explicit" - MaxDeliver: 3 - AckWait, HeartbeatInterval: zero (caller applies per-component default)
type DebugStatusProvider ¶
type DebugStatusProvider interface {
// DebugStatus returns extended debug information for the component.
// The returned value should be JSON-serializable.
DebugStatus() any
}
DebugStatusProvider is an optional interface for components that can provide extended debug information beyond basic health status.
type Dependencies ¶
type Dependencies struct {
NATSClient *natsclient.Client // NATS client for messaging
MetricsRegistry *metric.MetricsRegistry // Metrics registry for Prometheus (can be nil)
Logger *slog.Logger // Structured logger (can be nil, defaults to slog.Default())
Platform PlatformMeta // Platform identity (organization and platform)
Security security.Config // Platform-wide security configuration
ModelRegistry model.RegistryReader // Boot-selected model registry (can be nil)
ToolRegistry ToolRegistryReader // Shared tool executor registry (can be nil; agentic-tools requires it)
PayloadRegistry *payloadregistry.Registry // Shared payload registry (can be nil; components unmarshaling BaseMessage require it)
// LifecycleManager is the shared pkg/lifecycle.Manager that
// owns workflow-shaped entity instances (ADR-047). Apps that
// declare Participant-implementing entities (drone missions,
// sensor lifecycles, manufacturing batches, scenario executions)
// build the Manager in main.go, call Manager.Register for each
// app workflow, and pass it through Dependencies. Both the rule
// processor (lifecycle_* actions + $entity.lifecycle.* condition
// fields) and the lifecycle-gateway (operator HTTP API) read
// from this field.
//
// Concrete type (not an interface) per the PayloadRegistry
// precedent — pkg/lifecycle is a framework-owned leaf package,
// not an external/pluggable surface, and consumers narrow to
// their own minimum-surface interfaces locally (see
// processor/rule.LifecycleManager) when they want to abstract
// for testing.
//
// Can be nil — apps without any lifecycle-managed entity types
// pay zero cost, and the consumers that DO read this field
// loud-fail with a wiring error rather than silently no-op'ing.
LifecycleManager *lifecycle.Manager
// StoreRegistry is the shared {StorageInstance → storage.StreamableStore}
// resolver (ADR-063). The ComponentManager populates it from the boot-composed
// storage components' store-provide ports at Start and clears entries at Stop;
// content-fetch consumers (graph-embedding, fusion) resolve a StorageRef's
// StorageInstance through it, lazily per-fetch. Concrete framework-leaf type
// per the PayloadRegistry / LifecycleManager precedent.
//
// Can be nil — deployments with no offloaded-content fetch pay zero cost. A
// consumer that receives a StorageReference without this exact-name authority
// reports content-unresolved and excludes the body; it never selects another
// store or degrades solely because the name is unresolved.
StoreRegistry *storeregistry.Registry
}
Dependencies provides all external dependencies needed by components.
PayloadRegistry uses the concrete *payloadregistry.Registry rather than an interface (the way ToolRegistry uses ToolRegistryReader) on purpose: payloadregistry is a leaf package this package already imports, so there's no cycle to dodge, and message.NewDecoder also requires the concrete type. An interface here would force callers to type-assert at every Decoder construction site — pure friction for no abstraction win.
func (*Dependencies) GetLogger ¶
func (d *Dependencies) GetLogger() *slog.Logger
GetLogger returns the configured logger or a default logger if none is provided
func (*Dependencies) GetLoggerWithComponent ¶
func (d *Dependencies) GetLoggerWithComponent(componentName string) *slog.Logger
GetLoggerWithComponent returns a logger configured with component context
type Direction ¶
type Direction string
Direction identifies whether data enters or leaves a component.
type Discoverable ¶
type Discoverable interface {
// Meta returns basic component information
Meta() Metadata
// InputPorts returns the ports this component accepts data on
InputPorts() []Port
// OutputPorts returns the ports this component produces data on
OutputPorts() []Port
// ConfigSchema returns the configuration schema for this component
ConfigSchema() ConfigSchema
// Health returns current health status
Health() HealthStatus
// DataFlow returns current data flow metrics
DataFlow() FlowMetrics
}
Discoverable defines the interface for components that can be discovered and inspected by the management layer. This interface enables dynamic discovery of component capabilities, configuration, and health status.
Components implementing this interface can be: - Input components: Accept external data (UDP, TCP, HTTP) - Processor components: Transform data (plugins) - Output components: Send data to external systems - Storage components: Store and retrieve data (ObjectStore, KV)
type ErrorInjectingComponent ¶
type ErrorInjectingComponent struct {
LifecycleComponent
// contains filtered or unexported fields
}
ErrorInjectingComponent wraps a component to inject errors for testing
func NewErrorInjectingComponent ¶
func NewErrorInjectingComponent(comp LifecycleComponent) *ErrorInjectingComponent
NewErrorInjectingComponent creates a component wrapper that can inject errors for testing
func (*ErrorInjectingComponent) Initialize ¶
func (e *ErrorInjectingComponent) Initialize() error
Initialize initializes the component, returning injected error if configured
func (*ErrorInjectingComponent) InjectInitializeError ¶
func (e *ErrorInjectingComponent) InjectInitializeError(err error)
InjectInitializeError configures the component to return an error on Initialize
func (*ErrorInjectingComponent) InjectStartError ¶
func (e *ErrorInjectingComponent) InjectStartError(err error)
InjectStartError configures the component to return an error on Start
func (*ErrorInjectingComponent) InjectStopError ¶
func (e *ErrorInjectingComponent) InjectStopError(err error)
InjectStopError configures the component to return an error on Stop
type Factory ¶
type Factory func(rawConfig json.RawMessage, deps Dependencies) (Discoverable, error)
Factory creates a component instance from configuration following service pattern The factory function receives raw JSON configuration and dependencies, parses its own config, and returns a properly initialized component that implements the Discoverable interface. All I/O operations should be performed in the component's Start() method, not in the factory. This pattern matches service constructors: func(rawConfig json.RawMessage, deps Dependencies) (Service, error)
type FilePort ¶
FilePort - File system access
func (FilePort) IsExclusive ¶
IsExclusive returns false as multiple components can read files
func (FilePort) ResourceID ¶
ResourceID returns unique identifier for file ports
type FlowMetrics ¶
type FlowMetrics struct {
MessagesPerSecond float64 `json:"messages_per_second"`
BytesPerSecond float64 `json:"bytes_per_second"`
ErrorRate float64 `json:"error_rate"`
LastActivity time.Time `json:"last_activity"`
}
FlowMetrics describes the current data flow through a component
type HTTPClientPort ¶
type HTTPClientPort struct {
Method string `json:"method,omitempty"` // Required HTTP method
URLPattern string `json:"url_pattern"` // target endpoint/resource pattern; NO inline creds, NO query-string secrets
TriggerPort string `json:"trigger_port,omitempty"` // NAME of a sibling TimerPort driving cadence (empty = event-driven/internal)
AuthRef string `json:"auth_ref,omitempty"` // credential key name resolved at runtime; NEVER a secret value; empty = unauthenticated
ContactPolicy string `json:"contact_policy,omitempty"` // public User-Agent/contact identity (feeds often require it); not a secret
Interface *InterfaceContract `json:"interface,omitempty"` // message-interface contract this port produces
}
HTTPClientPort describes an outbound HTTP-client / polling input dependency: a relationship where the component initiates connections to an external HTTP resource (REST API, alert feed, snapshot endpoint) rather than binding a listener (NetworkPort) or consuming a NATS subject (NATSPort).
It is a DESCRIPTOR, not a runtime — like TimerPort it declares the dependency shape so lifecycle/config/health/flowgraph/ownership/telemetry tooling can SEE it; the polling/HTTP execution stays in the component. Cadence is NOT owned here: when polling is timer-driven the component also declares a TimerPort and this port references it by name via TriggerPort (single source of truth for the interval).
Secrets never appear in this descriptor. AuthRef carries a credential REFERENCE (a key name resolved at runtime from the component's secret source), never a value. No field holds a secret.
func (HTTPClientPort) IsExclusive ¶
func (h HTTPClientPort) IsExclusive() bool
IsExclusive returns false: an outbound client relationship is shareable (multiple components may poll the same endpoint), unlike a NetworkPort listener.
func (HTTPClientPort) Kind ¶
func (h HTTPClientPort) Kind() PortKind
Kind returns the canonical port kind.
func (HTTPClientPort) ResourceID ¶
func (h HTTPClientPort) ResourceID() string
ResourceID returns a unique identifier for the HTTP client dependency. Two ports with the same method and URL pattern identify the same external resource.
type HealthStatus ¶
type HealthStatus struct {
Healthy bool `json:"healthy"`
LastCheck time.Time `json:"last_check"`
ErrorCount int `json:"error_count"`
LastError string `json:"last_error,omitempty"`
Uptime time.Duration `json:"uptime"`
Status string `json:"status"`
}
HealthStatus describes the current health state of a component
type Info ¶
type Info struct {
Type string `json:"type"` // "input", "processor", "output", "storage"
Protocol string `json:"protocol"` // Technical protocol (udp, tcp, mavlink, etc.)
Domain string `json:"domain"` // Business domain (robotics, semantic, network, storage)
Description string `json:"description"` // Human-readable description
Version string `json:"version"` // Component version
}
Info holds metadata about an available component type
type InteractionPattern ¶
type InteractionPattern string
InteractionPattern is the canonical communication behavior of a resolved port.
const ( PatternTimer InteractionPattern = "timer" PatternNetwork InteractionPattern = "network" PatternHTTPClient InteractionPattern = "http-client" PatternStream InteractionPattern = "stream" PatternRequest InteractionPattern = "request" PatternWatch InteractionPattern = "watch" PatternRead InteractionPattern = "read" PatternStore InteractionPattern = "store" )
Canonical interaction patterns projected from resolved ports.
type InterfaceContract ¶
type InterfaceContract struct {
Type string `json:"type"`
Version string `json:"version,omitempty"`
Compatible []string `json:"compatible,omitempty"`
}
InterfaceContract defines the payload contract carried across a port.
type JetStreamPort ¶
type JetStreamPort struct {
// Stream configuration (for outputs)
StreamName string `json:"stream_name"` // e.g., "ENTITY_EVENTS"
Subjects []string `json:"subjects"` // e.g., ["events.graph.entity.>"]
Storage string `json:"storage,omitempty"` // "file" or "memory" when declared
RetentionPolicy string `json:"retention,omitempty"` // "limits", "interest", or "work_queue" when declared
RetentionDays int `json:"retention_days,omitempty"` // Declared message retention in days
MaxSizeGB int `json:"max_size_gb,omitempty"` // Declared maximum stream size in GiB
Replicas int `json:"replicas,omitempty"` // Declared replica count
// Consumer configuration (for inputs)
ConsumerName string `json:"consumer_name,omitempty"` // Durable consumer name
DeliverPolicy string `json:"deliver_policy,omitempty"` // Declared delivery policy
AckPolicy string `json:"ack_policy,omitempty"` // Declared acknowledgement policy
MaxDeliver int `json:"max_deliver,omitempty"` // Declared maximum redelivery attempts
// AckWait is the duration the JetStream server waits for an ack before
// redelivering. Strings are parsed via time.ParseDuration ("90s",
// "2m", "5m"). Empty falls through to a per-component default. The
// component-level default is a starting point; this port-level field
// lets operators tune ack_wait for long-running consumers (LLM model
// calls, slow tool execution) without forking the component. Per
// docs/operations/14-timeout-chain.md: ack_wait must comfortably
// exceed the longest legitimate per-task wallclock budget so that
// healthy long-tail work isn't reaped before it can ack.
AckWait string `json:"ack_wait,omitempty"`
// HeartbeatInterval is the cadence at which the consumer goroutine
// fires msg.InProgress() to reset the ack clock. Strings are parsed
// via time.ParseDuration ("60s", "90s"). Empty falls through to a
// per-component default. Should be sized comfortably below ack_wait
// (typical 1.5x margin) so a single missed heartbeat doesn't trigger
// redelivery. Only honored on consumers that wrap their handler in
// natsclient.ConsumeWithHeartbeat — pure-ack consumers ignore it.
HeartbeatInterval string `json:"heartbeat_interval,omitempty"`
// MaxAckPending caps the number of delivered-but-unacked messages the
// server keeps in flight for this consumer — the consumer-side backpressure
// lever. Empty/0 leaves policy to NATS, which may inherit a stream limit,
// apply its default, or cap it under server/account policy; -1 is unlimited
// outstanding acknowledgements. gh#480: there was previously no
// config path to this at all, so operators could not tune ingest backpressure.
MaxAckPending int `json:"max_ack_pending,omitempty"`
// Interface contract
Interface *InterfaceContract `json:"interface,omitempty"`
}
JetStreamPort - NATS JetStream for durable, at-least-once messaging
func (JetStreamPort) IsExclusive ¶
func (j JetStreamPort) IsExclusive() bool
IsExclusive returns false as JetStream manages consumer coordination
func (JetStreamPort) Kind ¶
func (j JetStreamPort) Kind() PortKind
Kind returns the canonical port kind.
func (JetStreamPort) ResourceID ¶
func (j JetStreamPort) ResourceID() string
ResourceID returns unique identifier for JetStream ports
type KVReadPort ¶
type KVReadPort struct {
Bucket string `json:"bucket"`
Interface *InterfaceContract `json:"interface,omitempty"`
}
KVReadPort declares exact or list access to current values in one KV bucket. It is metadata only: acquisition and missing-value policy remain component-owned.
func (KVReadPort) IsExclusive ¶
func (k KVReadPort) IsExclusive() bool
IsExclusive reports that concurrent readers may share a bucket.
func (KVReadPort) ResourceID ¶
func (k KVReadPort) ResourceID() string
ResourceID returns the canonical KV read resource identity.
type KVWatchPort ¶
type KVWatchPort struct {
Bucket string `json:"bucket"` // e.g., "ENTITY_STATES"
Keys []string `json:"keys,omitempty"` // Keys to watch, empty = all
History bool `json:"history,omitempty"` // Include historical values
Interface *InterfaceContract `json:"interface,omitempty"`
}
KVWatchPort - NATS KV Watch for state observation
func (KVWatchPort) IsExclusive ¶
func (k KVWatchPort) IsExclusive() bool
IsExclusive returns false as multiple watchers are allowed
func (KVWatchPort) Kind ¶
func (k KVWatchPort) Kind() PortKind
Kind returns the canonical port kind.
func (KVWatchPort) ResourceID ¶
func (k KVWatchPort) ResourceID() string
ResourceID returns unique identifier for KV watch ports
type KVWritePort ¶
type KVWritePort struct {
Bucket string `json:"bucket"` // e.g., "ENTITY_STATES"
Interface *InterfaceContract `json:"interface,omitempty"` // Data type contract
}
KVWritePort - NATS KV Write for state persistence
func (KVWritePort) IsExclusive ¶
func (k KVWritePort) IsExclusive() bool
IsExclusive returns false as multiple writers are allowed (with CAS handling)
func (KVWritePort) Kind ¶
func (k KVWritePort) Kind() PortKind
Kind returns the canonical port kind.
func (KVWritePort) ResourceID ¶
func (k KVWritePort) ResourceID() string
ResourceID returns unique identifier for KV write ports
type LifecycleComponent ¶
type LifecycleComponent interface {
Discoverable
Initialize() error
Start(ctx context.Context) error
Stop(ctx context.Context) error
}
LifecycleComponent defines the portable lifecycle contract for components. Initialize performs setup without runtime authority. Start accepts the context that owns continuing work. Stop uses its exact caller context to bound the component's terminal admission fence, cancellation, join, and cleanup; the resource owner determines that sequence's protocol-specific ordering. Exact resource ordering preserves admitted callback authority where native drain requires it; no universal cancel-before-drain order is implied.
During controlled shutdown, callers keep the accepted Start context live until a separately bounded Stop drains, joins, and finalizes the owner and returns nil. Ending the Start context first is abort cancellation. Stop then makes bounded best-effort progress under its exact caller authority and may return accurate native cleanup or deadline errors. Abort cleanup does not invent replacement authority, detach cleanup, or promise a second rejoin; if the bound wins, the portable contract makes no complete-join or leak-freedom claim.
Nil Start and Stop contexts are rejected before action. A completed repeated Stop is a no-op. Concurrent lifecycle calls, result replay, later rejoin after a Stop bound wins, reinitialization, and same-instance restart are not portable guarantees.
func AsLifecycleComponent ¶
func AsLifecycleComponent(comp Discoverable) (LifecycleComponent, bool)
AsLifecycleComponent safely casts a component to LifecycleComponent
type LifecycleFactory ¶
type LifecycleFactory func() LifecycleComponent
LifecycleFactory creates a new instance of a LifecycleComponent for testing
type LogEntry ¶
type LogEntry struct {
Timestamp string `json:"timestamp"` // RFC3339 format
Level LogLevel `json:"level"`
Component string `json:"component"`
FlowID string `json:"flow_id"`
Message string `json:"message"`
Stack string `json:"stack,omitempty"` // Stack trace for errors
}
LogEntry represents a structured log entry that can be published to NATS and consumed by the Flow Builder SSE endpoint.
type LogLevel ¶
type LogLevel string
LogLevel represents the severity level of a log entry
const ( // LogLevelDebug represents debug-level logs LogLevelDebug LogLevel = "DEBUG" // LogLevelInfo represents informational logs LogLevelInfo LogLevel = "INFO" // LogLevelWarn represents warning logs LogLevelWarn LogLevel = "WARN" // LogLevelError represents error logs LogLevelError LogLevel = "ERROR" )
type ManagedComponent ¶
type ManagedComponent struct {
// Component is the actual component instance
Component Discoverable
// State tracks the current lifecycle state
State State
// Config is the effective component configuration this instance was
// created from. The ComponentManager retains it so a per-component runtime
// config update can restart the component only when the effective config
// actually changed — a no-op update is skipped instead of stop/start-cycling
// a healthy running component (gh#520).
Config types.ComponentConfig
// StartOrder tracks the order components were started for reverse shutdown
StartOrder int
// LastError tracks the last error that occurred during lifecycle operations
LastError error
}
ManagedComponent tracks a component and its lifecycle state This is used by ComponentManager to properly manage component lifecycle
type Metadata ¶
type Metadata struct {
Name string `json:"name"`
Type string `json:"type"` // "input", "processor", "output", "storage"
Description string `json:"description"`
Version string `json:"version"`
}
Metadata describes what a component is
type NATSPort ¶
type NATSPort struct {
Subject string `json:"subject"`
Queue string `json:"queue,omitempty"`
Interface *InterfaceContract `json:"interface,omitempty"`
}
NATSPort - NATS pub/sub
func (NATSPort) IsExclusive ¶
IsExclusive returns false as multiple components can subscribe
func (NATSPort) ResourceID ¶
ResourceID returns unique identifier for NATS ports
type NATSRequestPort ¶
type NATSRequestPort struct {
Subject string `json:"subject"`
Timeout string `json:"timeout,omitempty"` // Duration string e.g. "1s", "500ms"
Retries int `json:"retries,omitempty"`
Interface *InterfaceContract `json:"interface,omitempty"`
}
NATSRequestPort - NATS Request/Response pattern for synchronous operations
func (NATSRequestPort) IsExclusive ¶
func (n NATSRequestPort) IsExclusive() bool
IsExclusive returns false as multiple components can handle requests
func (NATSRequestPort) Kind ¶
func (n NATSRequestPort) Kind() PortKind
Kind returns the canonical port kind.
func (NATSRequestPort) ResourceID ¶
func (n NATSRequestPort) ResourceID() string
ResourceID returns unique identifier for NATS request ports
type NetworkFacts ¶
type NetworkFacts struct {
// contains filtered or unexported fields
}
NetworkFacts is the immutable listener binding portion of PortFacts.
func (NetworkFacts) Host ¶
func (f NetworkFacts) Host() string
Host returns the configured bind host.
func (NetworkFacts) Protocol ¶
func (f NetworkFacts) Protocol() string
Protocol returns the network protocol.
type NetworkPort ¶
type NetworkPort struct {
Protocol string `json:"protocol"` // "tcp", "udp"
Host string `json:"host"` // "0.0.0.0", "localhost"
Port int `json:"port"` // 14550, 8080
}
NetworkPort - TCP/UDP network bindings
func (NetworkPort) IsExclusive ¶
func (n NetworkPort) IsExclusive() bool
IsExclusive returns true as network ports are exclusive
func (NetworkPort) Kind ¶
func (n NetworkPort) Kind() PortKind
Kind returns the canonical port kind.
func (NetworkPort) ResourceID ¶
func (n NetworkPort) ResourceID() string
ResourceID returns unique identifier for network ports
type PlatformMeta ¶
type PlatformMeta = types.PlatformMeta
PlatformMeta provides platform identity to components. Type alias to avoid import cycles while maintaining compatibility.
type Port ¶
type Port struct {
Name string `json:"name"`
Direction Direction `json:"direction"`
Required bool `json:"required,omitempty"`
Description string `json:"description,omitempty"`
Config Portable `json:"config"`
}
Port is a resolved component I/O declaration.
func (Port) Facts ¶
Facts revalidates the current Port value and returns its immutable semantic projection.
func (Port) MarshalJSON ¶
MarshalJSON encodes Port with the same config.kind envelope as PortDefinition.
func (*Port) UnmarshalJSON ¶
UnmarshalJSON strictly decodes and resolves a runtime port.
type PortConfig ¶
type PortConfig struct {
Inputs []PortDefinition `json:"inputs,omitempty"`
Outputs []PortDefinition `json:"outputs,omitempty"`
}
PortConfig groups semantic declarations by data-flow direction.
func MergePortConfig ¶
func MergePortConfig(defaults, overrides PortConfig) (PortConfig, error)
MergePortConfig applies complete named replacements to default declarations. Inputs and outputs are independent, ordering is stable, and returned data is cloned.
func (*PortConfig) UnmarshalJSON ¶
func (p *PortConfig) UnmarshalJSON(data []byte) error
UnmarshalJSON rejects unknown top-level lanes such as the retired kv_write lane.
type PortDefinition ¶
type PortDefinition struct {
Name string `json:"name" schema:"readonly,type:string,description:Port identifier"`
Required bool `json:"required,omitempty" schema:"readonly,type:bool,description:Whether port connection is required"`
Description string `json:"description,omitempty" schema:"readonly,type:string,description:Human-readable port description"`
Config Portable `json:"config" schema:"editable,type:object,description:Typed semantic port configuration"`
}
PortDefinition is a configuration-shaped semantic port declaration.
func (PortDefinition) MarshalJSON ¶
func (p PortDefinition) MarshalJSON() ([]byte, error)
MarshalJSON writes the canonical common port envelope.
func (PortDefinition) Resolve ¶
func (d PortDefinition) Resolve(direction Direction) (Port, error)
Resolve validates and resolves a configuration declaration for one direction.
func (*PortDefinition) UnmarshalJSON ¶
func (p *PortDefinition) UnmarshalJSON(data []byte) error
UnmarshalJSON strictly decodes the canonical common port envelope.
type PortFacts ¶
type PortFacts struct {
// contains filtered or unexported fields
}
PortFacts is an immutable semantic projection of one resolved Port.
func (PortFacts) ConnectionIDs ¶
ConnectionIDs returns defensive copies of the identifiers used for flow matching.
func (PortFacts) InteractionPattern ¶
func (f PortFacts) InteractionPattern() InteractionPattern
InteractionPattern returns the canonical communication behavior.
func (PortFacts) Interface ¶
func (f PortFacts) Interface() (InterfaceContract, bool)
Interface returns a defensive copy of the semantic interface contract.
func (PortFacts) IsExclusive ¶
IsExclusive reports whether two ports may claim the same resource.
func (PortFacts) KVReadBucket ¶
KVReadBucket returns the declared KV bucket for a kv-read port, so a component OBSERVES the bucket it was bound to instead of predicting the name with a constant. Reports false for every other port kind: the second return is the whole answer to "is this a KV read port", and a caller must never treat an empty bucket as a default.
This is the projection route for the bucket; concrete port config types are only interpreted by the canonical projection owners (this file and port_codec.go).
func (PortFacts) NATSSubjects ¶
NATSSubjects returns defensive copies of declared NATS subject families.
func (PortFacts) Network ¶
func (f PortFacts) Network() (NetworkFacts, bool)
Network returns the immutable network binding when the port declares one.
func (PortFacts) ResourceID ¶
ResourceID returns the canonical resource identity.
func (PortFacts) StoreReadBucket ¶
StoreReadBucket returns the configured content-store bucket for a store-read port.
func (PortFacts) Stream ¶
func (f PortFacts) Stream() (StreamFacts, bool)
Stream returns the immutable JetStream projection when the port declares one.
type PortFieldInfo ¶
type PortFieldInfo struct {
Type string `json:"type"`
Editable bool `json:"editable"`
Minimum *int `json:"minimum,omitempty"`
Enum []string `json:"enum,omitempty"`
Required []string `json:"required,omitempty"`
AnyRequired [][]string `json:"anyRequired,omitempty"`
RequiredByDirection map[Direction][]string `json:"requiredByDirection,omitempty"`
Directions []Direction `json:"directions,omitempty"`
Properties map[string]PortFieldInfo `json:"properties,omitempty"`
Variants map[string]PortFieldInfo `json:"variants,omitempty"`
Items *PortFieldInfo `json:"items,omitempty"`
AdditionalProperties *bool `json:"additionalProperties,omitempty"`
// contains filtered or unexported fields
}
PortFieldInfo describes metadata for PortDefinition fields
func (PortFieldInfo) ZeroIsOmitted ¶
func (p PortFieldInfo) ZeroIsOmitted() bool
ZeroIsOmitted reports whether numeric zero has omission semantics for this field. It is intentionally absent from discovery JSON: editors use the canonical Directions metadata, while schema projection uses this predicate to preserve an explicitly supplied zero as semantic absence.
type PortKind ¶
type PortKind string
PortKind is the closed vocabulary of semantic component port kinds.
const ( PortKindTimer PortKind = "timer" PortKindNetwork PortKind = "network" PortKindFile PortKind = "file" PortKindHTTPClient PortKind = "http-client" PortKindNATS PortKind = "nats" PortKindNATSRequest PortKind = "nats-request" PortKindJetStream PortKind = "jetstream" PortKindKVWatch PortKind = "kv-watch" PortKindKVRead PortKind = "kv-read" PortKindKVWrite PortKind = "kv-write" PortKindStoreRead PortKind = "store-read" PortKindStoreProvide PortKind = "store-provide" )
Canonical port kinds. Alternate spellings and custom kinds are not accepted.
type ProcessorMetrics ¶
type ProcessorMetrics struct {
// EventsProcessed counts total events processed, labeled by operation type
EventsProcessed *prometheus.CounterVec
// EventsErrors counts total processing errors, labeled by error type
EventsErrors *prometheus.CounterVec
// KVOperations counts KV bucket operations, labeled by operation (get/put/delete/watch)
KVOperations *prometheus.CounterVec
// ProcessingDuration measures processing latency in seconds
ProcessingDuration prometheus.Histogram
// contains filtered or unexported fields
}
ProcessorMetrics provides standard Prometheus metrics for processor components. Each processor component should create its own instance with a unique subsystem name.
func NewProcessorMetrics ¶
func NewProcessorMetrics(registry *metric.MetricsRegistry, subsystem string) *ProcessorMetrics
NewProcessorMetrics creates and registers processor metrics with the given subsystem name. The subsystem name should be the component name with underscores (e.g., "graph_ingest"). If registry is nil, metrics are created but not registered (useful for testing).
func (*ProcessorMetrics) ObserveDuration ¶
func (m *ProcessorMetrics) ObserveDuration(seconds float64)
ObserveDuration records a processing duration
func (*ProcessorMetrics) RecordError ¶
func (m *ProcessorMetrics) RecordError(errorType string)
RecordError increments the error counter for the given error type
func (*ProcessorMetrics) RecordEvent ¶
func (m *ProcessorMetrics) RecordEvent(operation string)
RecordEvent increments the events processed counter for the given operation
func (*ProcessorMetrics) RecordKVOperation ¶
func (m *ProcessorMetrics) RecordKVOperation(operation string)
RecordKVOperation increments the KV operations counter
type PropertySchema ¶
type PropertySchema struct {
Type string `json:"type"` // "string", "int", "bool", "float", "enum", "array", "object", "ports", "cache"
Description string `json:"description"`
Default any `json:"default,omitempty"`
Enum []string `json:"enum,omitempty"` // Valid string values
Minimum *int `json:"minimum,omitempty"` // For numeric types
Maximum *int `json:"maximum,omitempty"` // For numeric types
MinLength *int `json:"minLength,omitempty"` // Minimum string length
MaxLength *int `json:"maxLength,omitempty"` // Maximum string length
Pattern string `json:"pattern,omitempty"` // Regular expression for string values
Category string `json:"category,omitempty"` // "basic" or "advanced" for UI organization
PortFields map[string]PortFieldInfo `json:"portFields,omitempty"` // Metadata for port fields (when type is "ports")
CacheFields map[string]CacheFieldInfo `json:"cacheFields,omitempty"` // Metadata for cache fields (when type is "cache")
Properties map[string]PropertySchema `json:"properties,omitempty"` // Nested properties for object types
// AdditionalProperties controls whether object fields outside Properties
// are accepted. nil preserves JSON Schema's permissive default.
AdditionalProperties *bool `json:"additionalProperties,omitempty"`
Required []string `json:"required,omitempty"` // Required nested fields for object types
Items *PropertySchema `json:"items,omitempty"` // Item schema for array types
}
PropertySchema describes a single configuration property
type Registerable ¶
type Registerable interface {
Registration() Registration
}
Registerable allows components to self-describe for registry registration
type Registration ¶
type Registration struct {
Name string `json:"name"` // Factory name (e.g., "udp-input")
Type string `json:"type"` // Component type (input/processor/output/storage)
Protocol string `json:"protocol"` // Technical protocol (udp, mavlink, websocket, etc.)
Domain string `json:"domain"` // Business domain (robotics, semantic, network, storage)
Description string `json:"description"` // Human-readable description
Version string `json:"version"` // Component version
Schema ConfigSchema `json:"schema"` // Schema as static metadata (Feature 011)
Factory Factory `json:"-"` // Factory function (not serializable)
Dependencies []string `json:"dependencies"` // Boot dependencies such as DepModelRegistry
}
Registration holds factory and metadata for a component type
type RegistrationConfig ¶
type RegistrationConfig struct {
Name string // Component name (e.g., "udp", "websocket", "graph-processor")
Factory Factory // Factory function to create component instances
Schema ConfigSchema // Configuration schema for validation and discovery
Type string // Component type: "input", "processor", "output", "storage"
Protocol string // Technical protocol (udp, tcp, websocket, file, etc.)
Domain string // Business domain (network, storage, processing, robotics, semantic)
Description string // Human-readable description of the component
Version string // Component version (semver recommended)
Dependencies []string // Runtime deps declared via constants like DepModelRegistry
}
RegistrationConfig provides a clean API for component registration. This config struct replaces the previous 7-8 parameter function signatures. It maps 1:1 to Registration struct fields for simplicity.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages component factories and immutable admitted declarations. Runtime component handles remain private to ComponentManager.
func (*Registry) CreateComponent ¶
func (r *Registry) CreateComponent( _ componentadmission.Access, instanceName string, config types.ComponentConfig, deps Dependencies, prepare func(Discoverable) error, ) (Discoverable, error)
CreateComponent is the sole framework-internal boot-admission seam. prepare must finish all fallible owner-local setup before the Registry publishes the immutable declaration. Downstream adopters cannot obtain the internal access token and configure components through ComponentManager.
func (*Registry) GetComponentSchema ¶
func (r *Registry) GetComponentSchema(name string) (ConfigSchema, error)
GetComponentSchema retrieves a component's schema directly from Registration metadata This method retrieves schemas without component instantiation (Feature 011 - Option 1) Schema is stored as static metadata during registration, avoiding dependency validation issues
func (*Registry) GetFactory ¶
GetFactory returns a specific factory by name Unlike ListFactories, this returns the actual Factory function for creating components
func (*Registry) ListAvailable ¶
ListAvailable returns information about all available component types This provides metadata about what types of components can be created.
func (*Registry) ListComponentTypes ¶
ListComponentTypes returns all registered component factory type names This returns factory names (e.g., "udp-input", "websocket-output") not instance names
func (*Registry) ListFactories ¶
func (r *Registry) ListFactories() map[string]*Registration
ListFactories returns all registered component factories This provides information about what types of components can be created.
func (*Registry) RegisterFactory ¶
func (r *Registry) RegisterFactory(name string, registration *Registration) error
RegisterFactory registers a component factory with the given name Returns an error if a factory with the same name is already registered.
func (*Registry) RegisterWithConfig ¶
func (r *Registry) RegisterWithConfig(config RegistrationConfig) error
RegisterWithConfig registers a component using a configuration struct. This is the recommended registration method that replaces the multi-parameter functions.
Example usage:
registry.RegisterWithConfig(component.RegistrationConfig{
Name: "udp",
Factory: CreateUDPInput,
Schema: udpSchema,
Type: "input",
Protocol: "udp",
Domain: "network",
Description: "UDP input component for receiving network data",
Version: "1.0.0",
})
func (*Registry) SealComposition ¶
func (r *Registry) SealComposition(_ componentadmission.Access)
SealComposition closes boot admission for the current process. The internal access token prevents downstream callers from treating the seal as a public runtime-composition control.
func (*Registry) Snapshot ¶
func (r *Registry) Snapshot( _ componentadmission.Access, instanceName string, ) ( declarationSnapshot, bool, )
Snapshot returns one defensive admitted-declaration view to a root-internal framework consumer.
func (*Registry) Snapshots ¶
func (r *Registry) Snapshots( _ componentadmission.Access, ) []declarationSnapshot
Snapshots returns a deterministic defensive complete admission set to a root-internal framework consumer.
type SchemaDirectives ¶
type SchemaDirectives struct {
// core (required)
Type string // REQUIRED - field type
Description string // REQUIRED (warning if missing)
// UI Organization
Category string // "basic" or "advanced"
ReadOnly bool // For PortDefinition fields
Editable bool // For PortDefinition fields
Hidden bool // Hide from UI
// Constraints
Default any // Type-specific default value (stored as string, converted during schema generation)
Required bool // Field must be provided
Min *int // Numeric minimum
Max *int // Numeric maximum
Enum []string // Valid enum values
// Future extensions (stored but not used yet)
Help string
Placeholder string
Pattern string
Format string
}
SchemaDirectives represents parsed schema tag directives
func ParseSchemaTag ¶
func ParseSchemaTag(tag string) (SchemaDirectives, error)
ParseSchemaTag parses a schema struct tag into directives.
Tag Syntax:
- Directives are comma-separated
- Key-value pairs use colon: "key:value"
- Boolean flags have no colon: "readonly", "required"
- Enum values are pipe-separated: "enum:val1|val2|val3"
- Whitespace is trimmed from all values
Required Directives:
- type: Field data type (string, int, bool, float, enum, array, object, ports)
Recommended Directives:
- description: Human-readable field description (used for UI and documentation)
Optional Directives:
- category: UI organization (basic, advanced)
- default: Default value (converted to appropriate type)
- min/max: Numeric constraints
- enum: Valid values for enum types (pipe-separated)
- readonly: Field is read-only (boolean flag)
- editable: Field is user-editable (boolean flag)
- hidden: Field is hidden from UI (boolean flag)
- required: Field must be provided (boolean flag)
Example Tags:
schema:"type:string,description:Component name,category:basic" schema:"type:int,description:Port,min:1,max:65535,default:8080" schema:"type:enum,description:Level,enum:debug|info|warn,default:info" schema:"required,type:string,description:API key" schema:"readonly,type:string,description:System ID"
Returns an error if:
- Tag is empty
- Type directive is missing
- Type value is invalid
- Directive syntax is malformed
- Numeric values cannot be parsed
See SCHEMA_TAG_SPEC.md for complete specification.
Example ¶
ExampleParseSchemaTag demonstrates parsing individual schema tags
package main
import (
"fmt"
"github.com/c360studio/semstreams/component"
)
func main() {
// Parse a simple field tag
tag := "type:int,description:Port number,min:1,max:65535,default:8080"
directives, err := component.ParseSchemaTag(tag)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Type: %s\n", directives.Type)
fmt.Printf("Description: %s\n", directives.Description)
fmt.Printf("Min: %d\n", *directives.Min)
fmt.Printf("Max: %d\n", *directives.Max)
fmt.Printf("Default: %s\n", directives.Default)
}
Output: Type: int Description: Port number Min: 1 Max: 65535 Default: 8080
Example (Enum) ¶
ExampleParseSchemaTag_enum demonstrates parsing enum tags
package main
import (
"fmt"
"github.com/c360studio/semstreams/component"
)
func main() {
tag := "type:enum,description:Log level,enum:debug|info|warn|error,default:info"
directives, _ := component.ParseSchemaTag(tag)
fmt.Printf("Type: %s\n", directives.Type)
fmt.Printf("Description: %s\n", directives.Description)
fmt.Printf("Enum values: %v\n", directives.Enum)
fmt.Printf("Default: %s\n", directives.Default)
}
Output: Type: enum Description: Log level Enum values: [debug info warn error] Default: info
Example (Flags) ¶
ExampleParseSchemaTag_flags demonstrates boolean flags
package main
import (
"fmt"
"github.com/c360studio/semstreams/component"
)
func main() {
tag := "required,readonly,type:string,description:System identifier"
directives, _ := component.ParseSchemaTag(tag)
fmt.Printf("Type: %s\n", directives.Type)
fmt.Printf("Required: %v\n", directives.Required)
fmt.Printf("ReadOnly: %v\n", directives.ReadOnly)
}
Output: Type: string Required: true ReadOnly: true
type SimpleMockComponent ¶
type SimpleMockComponent struct {
// contains filtered or unexported fields
}
SimpleMockComponent is a simple component implementation for testing
func (*SimpleMockComponent) ConfigSchema ¶
func (m *SimpleMockComponent) ConfigSchema() ConfigSchema
ConfigSchema returns the component's configuration schema
func (*SimpleMockComponent) DataFlow ¶
func (m *SimpleMockComponent) DataFlow() FlowMetrics
DataFlow returns the component's data flow metrics (zeros for mock)
func (*SimpleMockComponent) Health ¶
func (m *SimpleMockComponent) Health() HealthStatus
Health returns the component's health status (always healthy for mock)
func (*SimpleMockComponent) InputPorts ¶
func (m *SimpleMockComponent) InputPorts() []Port
InputPorts returns the component's input ports (none for mock)
func (*SimpleMockComponent) Meta ¶
func (m *SimpleMockComponent) Meta() Metadata
Meta returns the component metadata
func (*SimpleMockComponent) OutputPorts ¶
func (m *SimpleMockComponent) OutputPorts() []Port
OutputPorts returns the component's output ports (none for mock)
type State ¶
type State int
State represents the current lifecycle state of a component
const ( // StateCreated indicates component was created but not initialized StateCreated State = iota // StateInitialized indicates component was initialized but not started StateInitialized // StateStarted indicates component is running StateStarted // StateStopped indicates component was stopped StateStopped // StateFailed indicates component failed during lifecycle operation StateFailed )
type StoreProvidePort ¶
type StoreProvidePort struct {
Instance string `json:"instance"` // StorageInstance name this component owns
}
StoreProvidePort declares that a storage component OWNS (provides) a store instance, addressable by its StorageInstance name (ADR-063). It is the flowgraph marker for store ownership and complements the StoreProvider interface the ComponentManager reads to populate the shared StoreRegistry.
Non-exclusive by design. Duplicate live Store ownership is owner-local runtime state and remains enforced by storeregistry.Register; it is not a declaration-derived exclusive-resource claim.
func (StoreProvidePort) IsExclusive ¶
func (s StoreProvidePort) IsExclusive() bool
IsExclusive returns false — ownership conflicts are caught at registry-population time, not here (see the type doc).
func (StoreProvidePort) Kind ¶
func (s StoreProvidePort) Kind() PortKind
Kind returns the canonical port kind.
func (StoreProvidePort) ResourceID ¶
func (s StoreProvidePort) ResourceID() string
ResourceID returns a unique identifier for store provide ports.
type StoreProvider ¶
type StoreProvider interface {
ProvidedStores() map[string]storage.StreamableStore
}
StoreProvider is implemented by storage components that own one or more stores addressable by StorageInstance name (ADR-063). For the boot-composed component set, ComponentManager reads this AFTER a component Starts to populate the shared StoreRegistry and clears those entries when the component Stops. A desired-state write does not swap a handle in the running process.
The map is keyed by the StorageInstance name each store STAMPS into refs (store.InstanceName()), so the registry key, the store-provide port token, and the ref's StorageInstance are the same value by construction. Returns nil/empty before Start (no store yet) or for a component that provides no store.
type StoreReadPort ¶
type StoreReadPort struct {
Bucket string `json:"bucket"` // Storage bucket name (e.g., "MESSAGES")
Interface *InterfaceContract `json:"interface,omitempty"` // Optional content type contract
}
StoreReadPort declares streaming read access to a content storage bucket. Components use this to read large content (documents, images, video) from storage backends (NATS ObjectStore, filesystem, etc.) without coupling to a specific backend implementation.
func (StoreReadPort) IsExclusive ¶
func (s StoreReadPort) IsExclusive() bool
IsExclusive returns false as multiple readers are allowed
func (StoreReadPort) Kind ¶
func (s StoreReadPort) Kind() PortKind
Kind returns the canonical port kind.
func (StoreReadPort) ResourceID ¶
func (s StoreReadPort) ResourceID() string
ResourceID returns unique identifier for store read ports
type StreamFacts ¶
type StreamFacts struct {
// contains filtered or unexported fields
}
StreamFacts is the immutable JetStream-specific portion of PortFacts.
func (StreamFacts) AckPolicy ¶
func (f StreamFacts) AckPolicy() string
AckPolicy returns the declared consumer acknowledgement policy.
func (StreamFacts) AckWait ¶
func (f StreamFacts) AckWait() string
AckWait returns the declared acknowledgement timeout.
func (StreamFacts) ConsumerName ¶
func (f StreamFacts) ConsumerName() string
ConsumerName returns the declared durable consumer name.
func (StreamFacts) DeliverPolicy ¶
func (f StreamFacts) DeliverPolicy() string
DeliverPolicy returns the declared consumer delivery policy.
func (StreamFacts) HeartbeatInterval ¶
func (f StreamFacts) HeartbeatInterval() string
HeartbeatInterval returns the declared consumer heartbeat interval.
func (StreamFacts) MaxAckPending ¶
func (f StreamFacts) MaxAckPending() int
MaxAckPending returns the declared unacknowledged-message ceiling.
func (StreamFacts) MaxDeliver ¶
func (f StreamFacts) MaxDeliver() int
MaxDeliver returns the declared maximum delivery attempts.
func (StreamFacts) MaxSizeGB ¶
func (f StreamFacts) MaxSizeGB() int
MaxSizeGB returns the declared stream size limit in GiB.
func (StreamFacts) Replicas ¶
func (f StreamFacts) Replicas() int
Replicas returns the declared stream replica count.
func (StreamFacts) RetentionDays ¶
func (f StreamFacts) RetentionDays() int
RetentionDays returns the declared stream retention duration in days.
func (StreamFacts) RetentionPolicy ¶
func (f StreamFacts) RetentionPolicy() string
RetentionPolicy returns the declared JetStream retention policy.
func (StreamFacts) Storage ¶
func (f StreamFacts) Storage() string
Storage returns the declared JetStream storage mode.
func (StreamFacts) Subjects ¶
func (f StreamFacts) Subjects() []string
Subjects returns a defensive copy of the declared subjects.
type TimerPort ¶
type TimerPort struct {
Interval string `json:"interval"` // Duration string e.g. "30s", "1m"
Interface *InterfaceContract `json:"interface,omitempty"`
}
TimerPort represents a periodic timer trigger port
func (TimerPort) IsExclusive ¶
IsExclusive returns false as multiple timers can run independently
func (TimerPort) ResourceID ¶
ResourceID returns unique identifier for timer ports
type ToolRegistryReader ¶
type ToolRegistryReader interface {
// Execute implementations that may cause external effects MUST use
// ToolCall.ID as the downstream idempotency key. agentic-tools persists only
// COMPLETED outcomes, so a crash or transient Create failure after the
// external effect but before durable completion is inherently ambiguous and
// may redeliver the same call to Execute.
Execute(ctx context.Context, call agentic.ToolCall) (agentic.ToolResult, error)
ListTools() []agentic.ToolDefinition
}
ToolRegistryReader is the dependency-side surface of the agentic- tools executor registry. *agentictools.ExecutorRegistry satisfies it implicitly. Defined here in the component package so all component consumers can refer to it through Dependencies without importing agentic-tools — mirrors how model.RegistryReader is wired.
On a tool miss, Execute returns a wrapped agentic.ErrToolNotFound sentinel. Callers detect via errors.Is — the previous string-match fallback in agentic-tools/component.go was the source of repeated extension friction and is gone with this contract.
type Validatable ¶
type Validatable interface {
Validate() error
}
Validatable interface for configs that can self-validate
type ValidationError ¶
type ValidationError struct {
Field string `json:"field"` // Name of the field that failed validation
Message string `json:"message"` // Human-readable error message
Code string `json:"code"` // Machine-readable error code (see above)
}
ValidationError represents a validation error for a specific configuration field. It provides structured error information that can be displayed to users and mapped to specific form fields in the UI.
Error codes are standardized across frontend and backend:
- "required": Field is required but missing
- "min": Numeric value below minimum threshold
- "max": Numeric value above maximum threshold
- "enum": Value not in allowed enum values
- "type": Value doesn't match expected type (string, int, bool, etc.)
- "pattern": String doesn't match required pattern (future use)
func ValidateComponentConfig ¶
func ValidateComponentConfig( ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, componentType string, configJSON json.RawMessage, ) []ValidationError
ValidateComponentConfig validates a component configuration from KV format. Convenience wrapper that handles JSON unmarshaling.
func ValidateConfig ¶
func ValidateConfig(config map[string]any, schema ConfigSchema) []ValidationError
ValidateConfig validates a configuration map against a ConfigSchema. It checks required fields, type constraints, min/max bounds, and enum values.
The validation is lenient - unknown fields are allowed to support backward compatibility and future schema evolution. Only explicitly defined properties are validated against their schema constraints.
Returns a slice of ValidationError containing all validation failures found. An empty slice indicates the configuration is valid.
Example usage:
schema := component.ConfigSchema{
Properties: map[string]component.PropertySchema{
"port": {
Type: "int",
Minimum: ptrInt(1),
Maximum: ptrInt(65535),
Category: "basic",
},
},
Required: []string{"port"},
}
config := map[string]any{"port": 99999}
errors := component.ValidateConfig(config, schema)
if len(errors) > 0 {
// Handle validation errors
fmt.Printf("Validation failed: %s\n", errors[0].Message)
}
func ValidateWithSchema ¶
func ValidateWithSchema( ctx context.Context, logger *slog.Logger, registry ConfigComponentRegistry, componentType string, config map[string]any, ) []ValidationError
ValidateWithSchema validates component configuration against its schema. Free function — takes logger explicitly so it doesn't need to live on config.Manager and create the config→component→agentic→ message→config cycle.
Returns validation errors if the config doesn't meet schema requirements. Should be called before persisting configuration to KV.
Source Files
¶
- config_validator.go
- dependencies.go
- discovery.go
- doc.go
- lifecycle.go
- lifecycle_test_suite.go
- logging.go
- metrics.go
- port.go
- port_codec.go
- port_facts.go
- port_file.go
- port_httpclient.go
- port_jetstream.go
- port_kv.go
- port_nats.go
- port_network.go
- port_provide.go
- port_resolver.go
- port_store.go
- port_timer.go
- ports.go
- registerable.go
- registry.go
- schema.go
- schema_tags.go
- test_helpers.go
- validation.go