Chronix

Time-series database · Rust · MIT OR Apache-2.0

A time-series database you add with Cargo.

Chronix runs inside your binary — no server, no sidecar, no daemon. The same engine also runs as chronixd behind Grafana when you need one. Forecasting and anomaly detection are part of the query engine, not a Python service beside it.

cargo add chronix

Two ways to run it, one engine

Embedded

A library, not a deployment. File-locked data directory, crash-safe WAL, graceful close(), no background daemons required. Runs where a database server cannot — a 512 MB gateway, a desktop app, a test.

use chronix::prelude::*;

// Gateway preset: under 25 MiB peak heap measured for ingest and rollups,
// flash-friendly WAL.
let db = Chronix::open_small("/var/lib/chronix")?;

db.insert(&Point::new(
    SeriesKey::new("power", tags! { "meter" => "main" })?,
    fields! { "watts" => 231.45 },
    1_700_000_000_000_000_000,
)?)?;

// Forecasting is a method on the database, not another service —
// and the model is chosen by cross-validation, not by you.
let chosen = db.auto_forecast(
    "power", "watts", &[("meter", "main")],
    start, end, 24 * 60, None,
)?;
println!("{}", chosen.selection.label);   // e.g. SARIMA(1,0,0)(0,1,1)[24]

Server

chronixd speaks the protocols your tools already emit: InfluxDB line protocol, Prometheus remote write/read, full PromQL, OTLP metrics and Arrow Flight SQL. Point Grafana at it and it behaves like a Prometheus data source.

chronixd --data-dir /var/lib/chronix

# InfluxDB line protocol — Telegraf can write here unchanged
curl -X POST localhost:8086/write \
  --data-binary 'power,meter=main watts=231.45'

# PromQL — the same request Grafana sends a Prometheus data source
curl 'localhost:8086/api/v1/query?query=rate(power_watts[5m])'

Why Chronix

Analytics in the engine

Forecasting (SES, Holt, Holt-Winters, ARIMA/SARIMA) and anomaly detection (Z-score, MAD, IQR, CUSUM, forecast-residual) run against zero-copy Arrow buffers, callable as FORECAST() and ANOMALY() from SQL. No Python sidecar, no data leaving the process.

Analytics guide →

Compression that fits the data

Most metric values started life as decimals — a meter reporting 231.45 W. Pcodec recovers the integer instead of XOR-ing bit patterns, then entropy-codes the deltas: 5–29× on realistic meter and inverter data, where ALP manages 3–8× and the XOR codecs 1.0–2.5×. The winner is chosen per block by trial encoding. A column declared Decimal skips the recovery step — the mantissa is already the integer — and reaches 475× on noisy connection-point power.

How encoding works →

Built for small machines

Chronix::open_small() is a ~48 MB preset with a flash-friendly WAL fsync policy, for eMMC and SD-backed gateways. The primary target is a 64-bit ARM box with 512 MB of RAM and days of offline operation.

Tuning guide →

Wire-compatible

InfluxDB line protocol, Prometheus remote write/read and PromQL, OTLP metrics, Arrow Flight SQL. Drop it in behind Telegraf, Prometheus, Grafana or the OpenTelemetry Collector without changing what they send.

API reference →

Security that stays out of the way

Cedar policy authorization, AES-256-GCM at rest, mTLS, Argon2 API keys, JWT/OIDC and a tamper-evident HMAC-chained audit log — all feature-gated, and an embedded build compiles none of it.

Security guide →

Open about what it isn't

Pre-1.0, single-node, and no production deployments yet. The distributed tier is frozen on purpose. Everything measured names the test that pins it, and the gaps are written down rather than implied away.

Read the internals →

Start here