# "Event Bus"
_Path: en/lua/core/events_
> "Publish and subscribe to events for observability — monitoring runtime and application activity and reacting to it."
## Table of Contents
- Event Bus
## Content
# Event Bus
The event bus publishes runtime and application activity for monitoring, logging, metrics, and reactive side effects. This page is an API reference; the snippets assume an executable Lua entry with the listed module and permissions.
The event bus is a best-effort publish/subscribe channel, not a reliable transport. Do not depend on it for business-critical delivery. Use process messaging (`process.send`), channels, or the [message queue](lua/storage/queue.md) when delivery is part of application correctness.
## Loading
```lua
local events = require("events")
```
## Subscribing to Events
Subscribe to one system or a system pattern, with an optional event-kind filter:
```lua
-- Subscribe to all order events
local sub, err = events.subscribe("orders.*")
if err then
return nil, err
end
-- Process events
local ch = sub:channel()
while true do
local evt, ok = ch:receive()
if not ok then break end
print(evt.system, evt.kind, evt.path)
-- Process evt.data when the publisher supplied a payload.
end
```
Pass a second argument to restrict delivery to one kind, for example
`events.subscribe("users", "user.created")`. An omitted kind accepts every
kind from the matching system.
| Parameter | Type | Description |
|-----------|------|-------------|
| `system` | string | System pattern (supports wildcards like "test.*") |
| `kind` | string | Event kind filter (optional) |
**Returns:** `Subscription, error`
## Publishing Events
Publish an event to the event bus:
```lua
-- Send order created event
local ok, err = events.send("orders", "order.created", "/orders/123", {
order_id = "123",
customer_id = "456",
total = 99.99
})
if err then
return nil, err
end
-- Send without data
local heartbeat_sent, heartbeat_err = events.send("system", "heartbeat", "/health")
if heartbeat_err then
return nil, heartbeat_err
end
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `system` | string | System identifier |
| `kind` | string | Event kind/type |
| `path` | string | Event path for routing |
| `data` | any | Event payload (optional) |
**Returns:** `boolean, error`
A successful return confirms that the runtime accepted the send. It does not
confirm that any subscriber received or processed the event.
### Receive Channel
Use the subscription channel to receive events:
```lua
local json = require("json")
local ch = sub:channel()
local evt, ok = ch:receive()
if ok then
print("System:", evt.system)
print("Kind:", evt.kind)
print("Path:", evt.path)
local encoded, encode_err = json.encode(evt.data)
if encode_err then return nil, encode_err end
print("Data:", encoded)
end
```
Each event contains `system`, `kind`, and `path`. The `data` field is present
only when the publisher supplied a non-nil payload.
### Close a Subscription
Close the subscription to unsubscribe and close its channel:
```lua
local closed = sub:close() -- true
```
Closing is idempotent. After the channel is closed, `receive()` returns
`nil, false` once buffered events are drained.
## Permissions
| Action | Resource | Description |
|--------|----------|-------------|
| `events.subscribe` | system | Subscribe to events from a system |
| `events.send` | system | Send events to a system |
## Errors
| Condition | Kind | Retryable |
|-----------|------|-----------|
| Empty system | `errors.INVALID` | no |
| Empty send kind | `errors.INVALID` | no |
| Empty path | `errors.INVALID` | no |
| Policy denied | `errors.INVALID` | no |
| Missing execution or process context | `errors.INTERNAL` | no |
See [Error Handling](lua/core/errors.md) for working with errors.
## Navigation
Previous: "Request Context" (lua/core/context)
Next: "Entry Registry" (lua/core/registry)