Ravel acknowledges a write when S3 has it.
Ravel is a database for OpenTelemetry metrics, logs, and traces that keeps all of its state in object storage: any S3-compatible store, or Google Cloud Storage. There is no write-ahead log, no replicated ingest quorum, and no StatefulSet to run. Kill any Ravel process at any moment, and every strictly acknowledged write is still in the bucket.
- Query with what you have. A Prometheus-compatible API for Grafana, and SQL over metrics, logs, and traces.
- Read your own write. Each acknowledgement returns a commit token. A query that carries it is guaranteed to see that write.
- Serve many tenants. Per-tenant encryption keys (SSE-KMS), legal hold, and admission limits.
You need git and Docker with Compose. Nothing is compiled.
git clone https://github.com/NOFireAI/ravel.git
cd ravel
docker compose -f deploy/docker-compose/ravel.yml up -dThis pulls published images and starts RustFS for object storage,
ravel-server 0.31.1, an OpenTelemetry Collector that sends your host's
metrics and its own logs, and Grafana. Wait for the server to report ready:
for i in $(seq 30); do curl -fs http://127.0.0.1:4318/readyz && echo ready && break; sleep 2; doneIt prints ready within a few seconds; if not, check
docker compose -f deploy/docker-compose/ravel.yml logs ravel-server. Host
metrics arrive about 20 seconds later. Query CPU load with PromQL, using the
demo bearer token:
curl -s -H "Authorization: Bearer demo-token" \
'http://127.0.0.1:4318/api/v1/query?query=system_cpu_load_average_1m'Success is "status":"success" with one series under data.result. An empty
result in the first 20 seconds is expected.
Count the Collector's log lines over the last hour. Logs answer PromQL through
the reserved ravel_log_lines metric name:
curl -s -H "Authorization: Bearer demo-token" \
--data-urlencode 'query=sum by (job) (count_over_time(ravel_log_lines[1h]))' \
'http://127.0.0.1:4318/api/v1/query'Read raw samples with SQL:
curl -s -X POST http://127.0.0.1:4318/api/v1/sql \
-H "Authorization: Bearer demo-token" \
-H "Content-Type: application/json" \
-d '{"query":"SELECT * FROM samples LIMIT 5"}'Open Grafana at http://127.0.0.1:3000 (admin / admin). The Ravel
datasource and a host-metrics dashboard are already set up.
Warning
Every credential in this stack is a fixed development value, and every port
binds 127.0.0.1 only. Do not expose it to a network.
Stop the stack:
docker compose -f deploy/docker-compose/ravel.yml downThe stored data stays in rustfs-data/, and the next up reuses it. To
delete it, run rm -rf rustfs-data after down (with sudo on Linux). That
cannot be undone.
The getting started guide explains each response and when an empty result is correct.
With the stack up, run demo/kill-and-recover.sh.
It writes one sample, kills ravel-server with SIGKILL, starts a fresh
container, and reads the sample back by its commit token. It exits non-zero if
the sample is missing. The GIF above is a recording of it.
Strict acknowledgement is the default. Ravel answers a write only after its data object and commit record are in the bucket, and returns one commit token per shard the write went through. A query without the token sees the write once the catalog's listing picks up its commit record.
Buffered acknowledgement is opt-in per request. It answers before the flush, for lower latency, and returns no commit token. A crash before the flush loses the buffered window, and a flush that runs past its time budget is abandoned and loses its acknowledged rows without any crash.
The consistency model is the normative statement of both modes, visibility, and crash behavior. The protocols behind it are model-checked in TLA+ under stated bounds (formal verification) and exercised by a deterministic fault-injection simulation.
Ravel fits when you want metrics, logs, and traces on a bucket you already run, with no stateful ingest layer and no local disk in the durability path.
Wait if:
- Your dashboards span months of high-cardinality metrics. Ravel keeps no downsampled rollups, so a wide query reads every raw hour it covers.
- You depend on LogQL, TraceQL, or the Jaeger UI. Ravel serves PromQL and SQL, and it has no Jaeger or Tempo API.
- You need write acknowledgement in single-digit milliseconds. Strict acknowledgement pays an object-store round trip.
Know these limits before you send data:
- Metrics must be cumulative. Ravel rejects delta
SumandHistogrampoints. Convert them in the Collector (how). - A structured log body (an array or a map) is stored as JSON text.
- Exemplars are kept from OTLP only. Profiles are not ingested.
Important
Ravel is pre-1.0. A data-format version change is one-way: once a new build writes the new version, an older build cannot read it. See rolling a format bump before you upgrade a deployment that holds data you keep.
The published image is built with --features sql,flight-sql,otap,agents,mcp.
A source build has no default features: pass the ones you need to cargo. Some
surfaces also need a runtime flag.
| Surface | Signals | Feature gate | In published image | Runtime switch |
|---|---|---|---|---|
| OTLP ingest, HTTP and gRPC | metrics, logs, traces | none |
yes | none |
| Prometheus Remote Write 1.0 and 2.0 | metrics | none |
yes | none |
| OTAP ingest, gRPC | metrics | otap |
yes | --otap |
| PromQL HTTP API | metrics, logs | none |
yes | none |
SQL, POST /api/v1/sql |
metrics, logs, traces, alerts, audit | sql |
yes | none |
Trace lookup, GET /api/traces/<trace_id> |
traces | sql |
yes | none |
| Flight SQL, gRPC | metrics, logs, traces, alerts, audit | flight-sql |
yes | none |
| Parquet tables over SQL | Parquet files in a granted location | sql |
yes | --parquet-profiles |
ES|QL, POST /api/v1/esql |
logs | esql |
no | none |
Ravel Agents, /api/v1/agents/ |
durable agent state | agents |
yes | --agents |
MCP server, POST /mcp |
metrics, logs, traces | mcp |
yes | --mcp |
"In published image" describes the image built from main; every such entry
is also in the 0.31.1 image the quickstart pins. Query surfaces serve in
--mode all and --mode query.
- SQL tables:
samples(metrics, without native histograms, which PromQL serves),logs,spans,alerts, andaudit. - PromQL reads logs through the reserved
ravel_log_linesandravel_log_bytesnames (PromQL over logs). - Parquet tables read files in place from a location an operator grants the tenant (Parquet table DDL).
The query guide covers each surface, and the HTTP API reference lists every route.
An ingest shard builds an immutable columnar segment in memory, writes it to the bucket, writes a commit record, and then answers the sender. Queries resolve commit records into a snapshot through the catalog. Background maintenance compacts segments and applies retention. Every process is disposable: a replacement reads its state from the bucket. The architecture overview explains the parts.
Ravel Agents runs durable agents on the same bucket as your telemetry. An agent is a named unit of state, such as one order or one conversation. Your worker runs a handler for each message, and Ravel commits the new state, the result, and the events in one conditional write. Any server or worker can stop, and the next one continues from the bucket.
Start with the Ravel Agents guide, then the TypeScript SDK guide. The operator guide lists the limits to know before production.
The MCP server lets an AI client, such as a coding assistant, query your
telemetry. The MCP server ships behind the mcp cargo feature and the
runtime --mcp flag, both off by default. The published image has the
feature, so there you pass only the flag, plus --mcp-allowed-origins when the
listener is reachable from outside localhost. All nine tools are served,
ravel_capabilities, ravel_describe_data and seven more, and every tool is
read-only. The MCP server guide shows how to
connect a client.
- Getting started, then the ingest and query guides.
- Operations to run Ravel on your own bucket, including container images.
- The documentation index routes every other task, from references to contributing (CONTRIBUTING.md).
- The changelog and the AI policy.
Apache 2.0. See LICENSE.
