Events System
Runix includes a pub/sub event bus with persistent storage for process lifecycle events.
Implementation
Located at internal/events/:
| File | Responsibility |
|---|---|
event.go | Event struct and event type constants |
bus.go | Bus — pub/sub with wildcard subscriptions and history |
store.go | Store — persistent JSON-per-line event log |
Event Types
| Type | Description |
|---|---|
process.started | Process successfully started |
process.stopped | Process stopped cleanly |
process.crashed | Process exited with non-zero code |
process.healthy | Health check passed (recovered) |
process.unhealthy | Health check failed consecutively |
process.reloaded | Process was reloaded |
Event Structure
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "process.started",
"timestamp": "2025-01-15T10:30:00Z",
"process_id": "abc123-def456",
"process_name": "api",
"payload": {}
}
| Field | Type | Description |
|---|---|---|
id | UUID | Unique event identifier |
type | string | Event type |
timestamp | RFC3339 | UTC timestamp |
process_id | string | Internal process UUID |
process_name | string | Process name |
payload | map | Optional additional data |
Event Bus
Publishing
bus.Emit(events.ProcessStarted, processID, processName, payload)
The bus:
- Creates an
Eventwith UUID and UTC timestamp - Appends to the persistent store
- Sends to all subscribers for this event type
- Sends to wildcard subscribers (subscribed to all types)
Subscribing
// Subscribe to specific event types
ch := bus.Subscribe(events.ProcessCrashed, events.ProcessUnhealthy)
// Subscribe to all events
ch := bus.Subscribe()
Channels are buffered (capacity 256). If a subscriber's channel is full, events are dropped to prevent blocking the publisher.
Unsubscribing
bus.Unsubscribe(ch)
Removes the channel from all event type subscriptions.
Persistent Store
Events are appended to ~/.runix/events.log in JSON-per-line format:
{"id":"...","type":"process.started","timestamp":"...","process_id":"...","process_name":"api","payload":{}}
{"id":"...","type":"process.crashed","timestamp":"...","process_id":"...","process_name":"worker","payload":{"exit_code":1}}
Querying
// Get events from the last hour
history := bus.History(time.Now().Add(-1 * time.Hour))
// Filter by type
history := bus.History(time.Now().Add(-24*time.Hour), events.ProcessCrashed)
The store reads the file line-by-line and unmarshals each JSON object. Results are filtered by timestamp and optional event types.
CLI Usage
# View events from last hour
runix events
# Stream live events
runix events --follow
# Filter by type
runix events --type process.crashed
# Custom time range
runix events --since 24h
What's Next
- Events Command — CLI usage
- Architecture Overview — Where events fit in the system