Event Log Store
Alopex Trail
Storage that fits event logs
Trail is storage for event logs: user activity, incoming external notifications, and the inputs and outputs of tools called by an agent. Event logs differ from record to record and are not rewritten afterward. Trail is designed for those properties, so there is no need to decide their shape before writing.
A storage engine designed for event logs: store each record as it arrives, even when every event has a different shape.
- What it stores
- Event logs
- Upfront schema definition
- None
Key points
- No schema to decide before writing
- Ingestion continues when fields are added or types change
- Choose the type when reading
Challenge
Writing freely and reading quickly do not coexist
Storage that accepts any shape becomes slow to search after the fact. Storage that reads quickly asks you to decide the shape before writing. Trail aims to accept any shape at write time and read at columnar speed.
- Products that accept any shape are slow to search after storage
- Products that read quickly demand a schema before writing
- When both are necessary, teams either give one up or maintain two systems
Event logs vary record by record and are not rewritten once they are written: user actions, incoming external notifications, and inputs and outputs of tools called by agents all work this way.
Relational storage asks for a shape before writing. Time-series storage assumes stable names and tags around numeric values. Document stores accept any shape but become slow to search after the fact.
Trail is designed around this nature of data. It accepts the arriving shape without requiring a schema at write time, and lets the reader choose a type at query time. It uses Skulk as its storage engine. Alopex OTel brings time-series and event logs together with dashboards.
Store data in the shape it arrives
Trail does not require a schema for writes. It accepts new fields and conflicting types as they arrive; interpretation happens when you read. That removes shaping code and the work of repairing it when it breaks.
- Ingestion does not stop for type changes, avoiding missing records
- Values do not need to be packed into JSON strings; they remain searchable
- Read the same field as a number or a string, depending on the query
- Transformation code
- None
- Schema changes
- None
Use Cases
Three places it fits
What to put in Trail and how to read it later.
Keep a record of application events
Activity and audit logs
- Store user actions, state transitions, errors, and external API results
- Line up the records around an incident to reconstruct what happened
- Keep writing when a new feature adds attributes, without changing definitions
Receive incoming event logs
Webhooks and integration notifications
- Store webhooks and notifications whose structure changes outside your control
- Keep them without conversion, then read the necessary attributes when they are known
- Ingestion continues when an integration upgrades its API version
Keep records from agents and devices
Execution logs and IoT event logs
- Store tool-call inputs and outputs, and device events with different attributes by model
- Reproduce an execution one event at a time or select the same attribute across devices
- Keep records intact even when their contents change every time
Features
Key features
What Trail provides to store and read event logs.
Schema-free writes
There is no need to decide the shape before writing. Continue writing when new fields appear without changing definitions.
Keep conflicting types
If the same field arrives as a number or a string, Trail retains both. Ingestion does not stop, so records are not lost.
Choose types when reading
Choose how to read a value at query time, supporting uses that need the same field treated as a number or as text.
No required timestamp
Accept records without a timestamp and event logs with an empty payload. No tag-versus-field distinction is required.
Parquet readable by external tools
Data stays in Parquet, so analysis needs no export step.
Tech Stack
Technology stack
Built directly on Skulk's storage engine.
Storage
Skulk engine (Apache Parquet)
Durability
WAL and atomic file publication
Implementation
Rust
Status
Development status
The design is ready for implementation. Versions v0.1 through v0.4 will add schema-free writes, storage, and Python bindings in order. Trail uses the storage engine already running in Skulk v0.3.0.
Public Resources
The latest documents, code, and community links in one place.
Get Involved
Do you have records that do not fit anywhere?
Records whose shape changes too much for relational storage and are not numeric enough for time-series storage—real examples help determine what to build first.