Antares
An NGSI-LD Context Broker in Rust. ETSI CIM 009 V1.9.1, one native binary, about 35 MiB of memory under the full conformance suite, and a WebAssembly build that runs the same broker inside a web page.
📖 Docs: https://antaresbroker.joinedcontext.com/
🚀 Demo: https://antaresbroker.joinedcontext.com/demo/ — the context broker running in your browser, yes in the browser 😅
Why Antares
- Small. About 35 MiB average RSS while the complete ETSI suite runs, about 9 MiB idle. The whole store ladder fits where a JVM heap alone would not.
- Conformant. 1833/1833 ETSI CIM 009 V1.9.1 test cases green in every
native store mode, and 1821/1821 in the browser build, where the MQTT
cases have no socket to run against
(per-store report with Robot drill-down).
The ledger covers the whole spec text, one file per clause, in
docs/spec/. - Runs anywhere. Zero infrastructure by default, PostgreSQL for
production, NATS JetStream for scale-out, and a 4 MB wasm artifact that
serves
/ngsi-ld/v1/*from a Service Worker with nothing installed. - Tenants without a ceiling. A tenant is a row in one shared schema,
isolated by
tenant_idand Row-Level Security, and exists from the first request that names it. Nothing in the broker caps the count, so one deployment can hand every employee, department and use case a tenant of its own, 100,000 of them on one Postgres cluster.
ETSI conformance results
The latest full run of the ETSI NGSI-LD test suite (CIM 009 V1.9.1), one cell per store mode. The badges read the live report, so they always show the newest run.
The suite in ngsi-ld-test-suite/ started from the
ETSI NGSI-LD test suite
and was corrected and extended; no test was weakened.
Quickstart
docker run --rm -p 9090:9090 ghcr.io/joinedcontext/antares-broker:latest
curl -i -X POST localhost:9090/ngsi-ld/v1/entities \
-H 'Content-Type: application/ld+json' \
-d '{
"id": "urn:ngsi-ld:TemperatureSensor:001",
"type": "TemperatureSensor",
"temperature": {"type": "Property", "value": 21.5, "unitCode": "CEL"},
"@context": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.9.jsonld"
}'
# HTTP/1.1 201 Created
curl -s 'localhost:9090/ngsi-ld/v1/entities?type=TemperatureSensor'
Without Docker, cargo run -p antares-broker serves the same API on port
9090 (Rust 1.97 or newer). Continue with the
Getting started chapter.
Store modes
One binary, one setting (ANTARES_STORE), the same API in every mode.
| Mode | Backend | Durability | Config |
|---|---|---|---|
memory (default) |
in-memory maps | none | |
file |
memory + redb write-through shadow, fsync before every ack | process restarts | ANTARES_DATA_DIR on a mounted volume |
postgres |
PostgreSQL + PostGIS | WAL, ordinary Postgres backup and PITR | ANTARES_DATABASE_URL |
timescale |
PostgreSQL + TimescaleDB for history | WAL | ANTARES_DATABASE_URL |
memory for tests and demos, file for a durable single node without a
database, postgres for production, timescale when temporal queries
dominate. ANTARES_BUS=nats adds JetStream for multi-pod scale-out and
rolling updates; MQTT notifications are built in and chosen per
subscription endpoint. Details, measured costs and backup procedures:
Storage drivers and Operations.
# durable single node, no Postgres
docker run --rm -p 9090:9090 -e ANTARES_STORE=file -e ANTARES_DATA_DIR=/data \
-v antares-data:/data ghcr.io/joinedcontext/antares-broker:latest
# postgres / timescale
docker run --rm -p 9090:9090 -e ANTARES_STORE=postgres \
-e ANTARES_DATABASE_URL=postgresql://antares:antares@db:5432/antares \
ghcr.io/joinedcontext/antares-broker:latest
# local stacks: broker + PostGIS + NATS + mosquitto
docker compose -f compose-files/docker-compose.yml up # one broker
docker compose -f compose-files/docker-compose-ha.yml up # two replicas + haproxy + NATS
Image tags: :dev is the latest green dev, :dev-<run> one CI run,
:0.1.4 one release, :0.1 the newest patch of a minor, :latest the
latest release. Images are multi-arch (amd64, arm64). The
role-split fleet (--roles api,matcher,notifier,temporal,registry) and
the Kubernetes manifests are in Deployment.
Browser build
The broker compiles to wasm32-unknown-unknown and answers /ngsi-ld/v1/*
from a Service Worker; www/index.html is the playground.
./dev/install-wasm-tools.sh # wasm-bindgen + wasm-opt
./dev/wasm-build.sh # → www/pkg
node www/node-shim.mjs 9090 # the same .wasm behind a TCP port
./dev/wasm-test.sh # Node smoke + headless-Chromium page test
Scope: memory store and local bus only. See Browser & WebAssembly.
Observability
curl -s localhost:9090/q/health # liveness, store mode, bus state
curl -s localhost:9090/q/ready # readiness: store ping + bus connected
curl -s localhost:9090/q/metrics # Prometheus, antares_ prefixed
Traces and logs export over OTLP/HTTP when ANTARES_OTLP_ENDPOINT is set.
Every route under /q/ is in the Admin API chapter.
Conformance
The ETSI NGSI-LD test suite
is vendored in ngsi-ld-test-suite/ and runs in CI against every store
mode, including a ten-container role-split fleet that rolls under the
suite and the browser build behind a Node shim. The vendored copy carries a
few test-side fixes, each proven from the clause text and listed in
docs/upstream/etsi-raises.md; no test is
weakened to fit the broker.
cargo test --workspace -j 2 # unit and integration tests
dev/etsi-local.sh # workspace tests + the suite, memory store
STORE=postgres dev/etsi-local.sh # the store mode you are touching
How to read the matrix and the ledger: Conformance.
Documentation
The book is published at https://antaresbroker.joinedcontext.com/
and built from docs/src. Maintainers start at
ARCHITECTURE.md.
| Chapter | Covers |
|---|---|
| Getting started | install, first entity, first subscription, first federation pair |
| Configuration | every ANTARES_* variable, with defaults |
| Deployment | Docker, compose stacks, Kubernetes, the role split |
| Subscriptions | HTTP and MQTT delivery, retries, dead letters |
| Temporal API | recording, querying, aggregation, retention |
| Federation | registrations, forwarding, loop protection |
| Operations | health, tenants, backup and restore, bulk load, rolling updates |
| Storage drivers | the store ladder, the temporal driver, migrations |
| Performance | the weekly shape and scale runs and how to reproduce them |
| Shared crates | the parser, query engine and matcher as libraries |
| Extending Antares | features, the driver registry, hooks, adding a backend |
| Decisions | the architecture decision records |
Design targets
| Dimension | Target |
|---|---|
| Entities | 100,000,000 current-state, one PostgreSQL cluster |
| Tenants | 10,000, one shared schema, tenant_id + Row-Level Security |
| Subscriptions | 100,000 per broker, HTTP + MQTT |
| CSource registrations | 100,000+ per broker, index-shaped matching, bounded fan-out |
| HA | stateless broker pods, NATS JetStream, Postgres primary/replica |
The weekly scale run measures a fixed fraction of these rows on rented hardware and publishes the broker's and Postgres's memory and CPU per phase; the Performance chapter says how each number is produced.
Repository layout
crates/antares-model NGSI-LD types
crates/antares-ql q= / scopeQ / geoQ parsers and evaluator
crates/antares-jsonld @context cache and core-context fast path
crates/antares-sql storage drivers, SQL compiler, migrations
crates/antares-bus change-event bus: in-process or NATS JetStream
crates/antares-api HTTP binding (axum) and the operations
crates/antares-broker composition root, the `antares` binary
crates/antares-wasm the browser build
docs/ conformance ledger, ADRs, user book source
dev/ run and test scripts
compose-files/ local stacks
Related brokers
Antares is a compliant peer of Orion-LD, Scorpio and Stellio, not a fork of any of them: it shares no code with them and federates with them over the standard distributed-operations API. Where it fits and how it compares: Ecosystem.
Contributing
Changes are accepted into dev only. Fork, branch from dev, open the
pull request into dev; one aimed at main fails its first check and
gets retargeted. main holds released code and moves only when the
maintainers promote dev into it, and releases are the v* tags cut
there. Commits carry a DCO sign-off (git commit -s), one change each.
The whole process, from the first commit to a release, is in CONTRIBUTING.md. Report a vulnerability the way SECURITY.md describes, not in a public issue.
License
EUPL-1.2, the European Union Public Licence. Commercial licensing: contact@marek-mraz.com.
