---
question: Does a subscriber replay the event log?
kind: overview
---

# Events

The event service is an append-only log plus a live broadcast. `publish` writes the event to the log, then broadcasts it on the bus. A subscriber gets what is published while it listens; the history is a query on the log. The kernel services publish here as they work: keys set, objects written, mail sent, secrets changed.

Super admins can follow the log in Nexus, under **Events** in the sidebar, and browse it by type under System › Data › Events. Nexus never deletes or edits an event.

<!-- matrix -->

## Backends

`services.event.event_adapter` picks where the log lives:

| Adapter | Storage | Keys |
|---|---|---|
| `sqlite` | A SQLite file | `db_path` (default `storage/events/events.sqlite`) |
| `postgresql` | A PostgreSQL schema, shared by every engine | `dsn` (required), `schema` (default `abi_event`), `archive_after_days` (default `7`), timeouts and pool size |
| `nats_rpc` | Another engine's event log, over NATS | `nats_url`, `jwt_secret`, `service_identity` |

When `config.yaml` has no `event:` block, as in a new project, the engine uses `sqlite` at the default path. With `postgresql` in NATS mode, events older than `archive_after_days` move each hour to the `events_archive` dataset and are deleted from the log, so `query` no longer sees them; `null` keeps them all.

```yaml title="config.yaml"
services:
  event:
    event_adapter:
      adapter: postgresql
      config:
        dsn: "{{ secret.EVENT_POSTGRES_DSN }}"
        schema: "abi_event"
```

## Key concepts

- **Typed events.** An event is a class that inherits `LogProcess`, from `naas_abi_core.services.event.ontologies.modules.EventOntology`. You declare it in an ontology and generate the class. `publish` raises `InvalidEventError` for anything else.
- **Durable first.** The log row is written before the broadcast. If the bus fails, the event is still in the log.
- **Live only.** `subscribe` delivers what is published while it listens. Every subscriber gets its own copy. Nothing is replayed.
- **Catch up.** `query` reads the log. `query_for_consumer` returns what one consumer has not read yet and moves its cursor as it reads, so a worker that restarts picks up after the last batch it read. That is at most once: a crash mid-batch loses the rest of the batch, so keep batches small and the handling safe to repeat. The cursor is per consumer id and event type; events a filter skipped stay skipped. With `postgresql` in NATS mode, a consumer idle longer than `archive_after_days` is left behind.
- **Filters.** `query` and `subscribe` take the same filter dict: a value for equality, a list for one of, or an operator (`eq`, `ne`, `prefix`, `suffix`, `contains`, `in`, `gt`, `gte`, `lt`, `lte`, `exists`). Keys are dotted paths into the event.
- **Who and where.** `publish` fills `created_at`, and `actor_user_id`, `actor_workspace_id` and `triggered_via` from the request when you leave them empty.
- **Dependency.** A module lists `EventService` in `ModuleDependencies.services`. The attribute is `self.engine.services.events`. The event service loads the bus too.
