Overview
Fracta has an internal in-process event bus ininternal/events.
Its job is to let subsystems emit structured lifecycle and infrastructure events without knowing where those events go. Emitters publish an events.Event; sinks decide whether to:
- write a structured log line
- persist a durable row in
agent_events - write a Kubernetes Event in K8s mode
What It Does
The event bus standardizes event emission across layers such as:orchestratorruntime.k8smcpclientgatewaymcpserver.GatewayServer
events.Bus instead of importing Postgres/SQLite event-writing helpers directly.
The current built-in sinks are:
LogSink: emits one structured JSON log line viafractalog.Component("events")StoreSink: persists the event toagent_eventsK8sEventSink: writes selected infra-facing events to Kubernetes Events when the runtime backend is Kubernetes
Architecture
ControlPlane constructs the core bus. In normal agent-serving mode it builds a FanoutBus with:
LogSinkalwaysStoreSinkwhen the active store can persist eventsK8sEventSinkwhen the runtime backend is Kubernetes and the backend can provide a Kubernetes event recorder
LogSinkStoreSink
K8sEventSink is attached.
Event Model
Events use a shared structured model:ID: random UUID for event trackingTime: timestampComponent: emitting subsystemCategory: event familyResource: concrete thing the event is aboutAction: event label such ascreate,connect_attempt,status_changeOutcome: normalized result such assuccess,failure,timeoutSeverity:debug,info,warn,errorTask,MissionID,ObjectiveID: fracta-native correlation keysDetail: short human-readable contextAttrs: metadata bag for extra structured fields
Relation To Logging
The event bus does not replace fracta’s logging system. Fracta still uses:- Go
log/slog internal/fractalogfractalog.Component("name")for structured JSON logs
- normal logs were emitted directly through
fractalog.Component(...) - some lifecycle events were written through the old
state.EventEmitter - store-backed event writes also logged as a side effect
- lower-level packages had no shared, store-independent event seam
- normal application logs still work the same way as before
- event-producing code emits
events.Eventthroughevents.Bus LogSinkis now the single built-in logging side effect for bus eventsStoreSinkis persistence-only and does not log- K8s-native event export is handled by
K8sEventSink, not by emitters
- general logging architecture: mostly unchanged
- event logging architecture: changed substantially
What Stayed The Same
- JSON logging still goes through
slogandfractalog - component-tagged logs are still the primary raw debug path
- stderr/file logging configuration is unchanged
- code that is not emitting bus events should still log directly with
fractalog.Component(...)
What Changed
For event-like signals, fracta now has one consistent flow:- Code emits a structured event.
- The bus fans it out to sinks.
- Logging, persistence, and Kubernetes export are handled centrally.
- less coupling between emitters and stores
- durable event rows without store imports in low-level packages
- one canonical event log format
- no duplicate log line from the store sink
- a K8s-native operator path in Kubernetes mode
Durable Storage
StoreSink writes structured fields to agent_events and also writes a derived legacy flat event alias for compatibility with older readers/tests.
That means:
- new code should think in structured fields
- old consumers of the flat
eventcolumn can continue to work during the migration window
When To Use The Bus vs Direct Logging
Use the event bus when the signal is:- a lifecycle or observability event that may need multiple sinks
- worth persisting durably
- useful as a Kubernetes Event in K8s mode
- something multiple layers may want to emit consistently
fractalog.Component(...) logging when the signal is:
- local debug detail
- step-by-step operational tracing
- not part of the shared event taxonomy
- not something that needs durable storage or K8s Event export
- bus = structured cross-sink event
- direct log = ordinary application log

