Eimdall Documentation v0.9

Eimdall is the edge-first product intelligence and predictive maintenance platform for robotics. It runs fully air-gapped — no cloud dependency, no data leaving your infrastructure.

Distribution

Edge Core and Central are proprietary binaries distributed by DATA AGILITY after a demo. The ROS 2 bridge is open source (MIT). Request access →

Quick read

Two dashboards, one platform: the manufacturer dashboard shows feature adoption, user journeys and friction signals; the operator dashboard shows fleet health, anomaly timelines and failure predictions.

What Eimdall gives you

Architecture

Eimdall is composed of two binaries that communicate via a structured report format. No raw sensor stream is ever transmitted.

Edge Core

Rust runtime on the robot. Ingests sensors, builds features, detects anomalies, stores reports in a local spool.

Central

Your server. Receives compact daily reports, stores them, serves the dashboards and runs LLM analysis.

Insights

Local Ollama instance generates natural-language analysis, predictions and recommendations on demand.

Data flow

  1. Your robot bridge pushes sensor readings to the Edge Core via a local HTTPS endpoint (127.0.0.1:8787)
  2. Edge Core aggregates readings into DailyReports — compact protobuf summaries (typically <50 KB)
  3. Reports are stored in an on-robot spool (up to 14 days of autonomy) and uploaded to Central when connectivity is available
  4. Central indexes reports and exposes them to the dashboards via a REST API
  5. Insights are generated on demand by triggering a local Ollama model against the indexed data
Air-gapped design

Neither the Edge Core nor Central require internet access at runtime. DNS, NTP and all external dependencies are optional.

Quickstart — first robot in 30 minutes

This guide connects a single robot to a running Central instance.

1

Download and install the Edge binary

You received a download link from DATA AGILITY after your demo. The archive contains the pre-compiled binary for your target platform (linux-x86_64 or linux-arm64).

# Extract the archive you received
tar -xzf eimdall-edge-core-v0.9.2-linux-x86_64.tar.gz

# Verify integrity (SHA256 provided in the email)
sha256sum eimdall-edge-core

# Install
sudo mkdir -p /opt/eimdall/bin
sudo install -m 755 eimdall-edge-core /opt/eimdall/bin/eimdall-edge

# Verify
/opt/eimdall/bin/eimdall-edge --version
No binary yet?

Request access via the demo form on eimdall.io. A download link is sent by email after a 15-minute demo call.

2

Create the configuration file

Save as /etc/eimdall/edge.runtime.yaml. Replace YOUR_CENTRAL_IP.

feature_set_version: "fs-2026-04"

sensors:
  - sensor_id: "body_imu"
    family: "imu"
    enabled: true
    sample_rate_hz: 10

spool_disk:
  path: "/var/lib/eimdall/spool"
  ttl_days: 14
  max_bytes: 1073741824   # 1 GB

upload:
  central_url: "https://YOUR_CENTRAL_IP:8080"
  retry_base_ms: 1000
  max_retries: 8
  tls_ca_cert_path: "/etc/eimdall/tls/central-ca.crt"

local_service:
  enabled: true
  bind: "127.0.0.1:8787"
  require_pairing: true
  token_file: "/etc/eimdall/eimdall-local-service.token"
sudo mkdir -p /etc/eimdall /var/lib/eimdall/spool /var/log/eimdall
echo "token-$(head -c 8 /dev/urandom | xxd -p)" | \
  sudo tee /etc/eimdall/eimdall-local-service.token
3

Start the Edge runtime

sudo /opt/eimdall/bin/eimdall-edge \
  --config /etc/eimdall/edge.runtime.yaml

# Verify
curl --cacert /etc/eimdall/tls/edge-local-ca.crt \
  https://127.0.0.1:8787/v1/local/ping
# → {"status":"ok","version":"0.9.2"}
4

Push your first sensor reading

Use the Python bridge below or any HTTP client. See The bridge for details.

TOKEN=$(cat /etc/eimdall/eimdall-local-service.token)

curl -sk https://127.0.0.1:8787/v1/local/bridge/ingest \
  -H "X-Eimdall-Bridge-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "robot_id":   "robot-01",
    "bridge_id":  "bridge-main",
    "source":     "constructor_sdk",
    "sensor_id":  "body_imu",
    "family":     "imu",
    "ts_unix_ms": '$(date +%s000)',
    "values": {
      "accel_x_g": 0.01, "accel_y_g": -0.02, "accel_z_g": 1.01,
      "gyro_x_dps": 0.5, "gyro_y_dps": -0.3, "gyro_z_dps": 0.1
    }
  }'
5

View data on the Central dashboard

Open https://YOUR_CENTRAL_IP:8080/app/auth/login, log in with your constructor account. Your robot appears in Fleet overview within minutes. To force an immediate upload:

SESSION=$(curl -sk -X POST https://127.0.0.1:8787/v1/local/pair/session \
  -H "X-Eimdall-Bridge-Token: $TOKEN" | jq -r .session_token)

curl -sk -X POST https://127.0.0.1:8787/v1/local/upload/trigger \
  -H "Authorization: Bearer $SESSION"

The bridge

The bridge is the only code you write. It reads your robot's native SDK and pushes readings to the Edge Core's local HTTPS endpoint. Eimdall provides no SDK dependency — any HTTP client works.

Python bridge — minimal example

import requests, time

EDGE_URL  = "https://127.0.0.1:8787/v1/local/bridge/ingest"
HB_URL    = "https://127.0.0.1:8787/v1/local/bridge/heartbeat"
TOKEN     = open("/etc/eimdall/eimdall-local-service.token").read().strip()
HEADERS   = {"X-Eimdall-Bridge-Token": TOKEN, "Content-Type": "application/json"}
CA        = "/etc/eimdall/tls/edge-local-ca.crt"

def push_imu(ax, ay, az, gx, gy, gz):
    requests.post(EDGE_URL, headers=HEADERS, verify=CA, timeout=2, json={
        "robot_id":   "robot-01",
        "bridge_id":  "bridge-main",
        "source":     "constructor_sdk",
        "sensor_id":  "body_imu",
        "family":     "imu",
        "ts_unix_ms": int(time.time() * 1000),
        "values": {
            "accel_x_g": ax, "accel_y_g": ay, "accel_z_g": az,
            "gyro_x_dps": gx, "gyro_y_dps": gy, "gyro_z_dps": gz,
        }
    })

def send_heartbeat():
    requests.post(HB_URL, headers=HEADERS, verify=CA, timeout=2, json={
        "robot_id": "robot-01", "bridge_id": "bridge-main",
        "ts_unix_ms": int(time.time() * 1000),
        "status": "ok", "uptime_s": int(time.monotonic()), "errors_5m": 0
    })

last_hb = 0
while True:
    # ← replace with your robot SDK call
    push_imu(0.01, -0.02, 1.01, 0.5, -0.3, 0.1)
    if time.time() - last_hb > 5:
        send_heartbeat()
        last_hb = time.time()
    time.sleep(0.1)

Ingest payload format

FieldTypeDescription
robot_idstringUnique robot identifier within your tenant
bridge_idstringBridge instance name (e.g. bridge-main)
sourcestringconstructor_sdk | ros2 | custom
sensor_idstringSensor name, matches config sensors[].sensor_id
familystringSensor family — see Sensor families
ts_unix_msintegerTimestamp in milliseconds (Unix epoch)
valuesobjectKey-value map of sensor readings (family-specific)

Sensor families

Each family defines a set of expected keys in the values payload. Unknown keys are accepted and stored as generic numerics.

FamilyKey fieldsTypical use
imuaccel_x/y/z_g, gyro_x/y/z_dpsBody motion, vibration, fall detection
motorcurrent_a, rpm, temp_c, torque_nmDrivetrain health, drift detection
batteryvoltage_v, current_a, soc_pct, temp_cCharge cycles, drain patterns
lidarscan_count, obstacle_count, retry_countNavigation friction, environment mapping
proximitydistance_m, trigger_countCollision risk, retry analysis
camerafps, detection_count, latency_msVision pipeline health
audiorms_db, keyword_count, retry_countVoice recognition adoption and friction
encoderposition_ticks, velocity_rps, error_countOdometry accuracy, wear detection
touchevent_count, duration_ms, retry_countUI interaction, fallback measurement
genericany numeric keysCustom sensors, process variables

ROS 2 integration

The eimdall_ros2_bridge package translates ROS 2 topic messages into Eimdall bridge payloads. It runs as a sidecar node alongside your ROS 2 stack with no dependency on Eimdall binaries in your ROS workspace.

Open source

The ROS 2 bridge is open source and MIT-licensed. No demo required — clone, build and adapt freely.

Install

# Clone into your ROS 2 workspace
cd ~/ros2_ws/src
git clone https://github.com/YvesData/eimdall-ros2-bridge.git

cd ~/ros2_ws
colcon build --packages-select eimdall_ros2_bridge
source install/setup.bash

Launch

ros2 launch eimdall_ros2_bridge bridge.launch.py \
  robot_id:=robot-01 \
  edge_url:=https://127.0.0.1:8787 \
  token_file:=/etc/eimdall/eimdall-local-service.token

The bridge automatically maps standard ROS 2 message types to Eimdall sensor families: sensor_msgs/Imuimu, sensor_msgs/BatteryStatebattery, nav_msgs/Odometryencoder, etc.

Custom topics

Any additional topic can be mapped to a generic sensor by editing the launch file topic map. No code change required.

Key concepts

DailyReport

The unit of data exchange between Edge and Central. A DailyReport is a compact protobuf summary built over a rolling 24-hour window. It contains aggregated feature vectors, anomaly counts, session summaries and usage patterns — no raw sensor values. Typical size: 10–50 KB per robot per day.

Anomaly scoring

The Edge Core maintains per-sensor baselines using an online learning pipeline. When a sensor reading deviates significantly from its baseline, an anomaly event is recorded with a severity level (low / medium / high / critical) and attached to the next DailyReport.

Insights

Insights are natural-language analysis results generated by a local Ollama LLM model against the indexed DailyReports. Available insight kinds:

KindDescription
fleet_healthOverall fleet status summary with risk breakdown
feature_adoption_reportWhich features are used, ignored or overridden across the fleet
friction_emerging_usageDetected retry patterns and unexpected usage behaviours
user_journey_analysisSession flow analysis and abandonment moments
predictive_maintenancePer-robot failure risk scores and maintenance recommendations
cross_environment_comparisonUsage differences across sites, firmware versions or clients
robot_anomalyAnomaly deep-dive for a specific robot
daily_digestDaily summary for a specific robot

Multi-tenancy

A single Central instance supports multiple manufacturers (constructor tenants) and their clients (client tenants). Each robot belongs to one tenant. Clients can access only the data shared by their manufacturer — the sharing policy (none / aggregated / full) is set per-tenant by the manufacturer.

Security

Transport

All communication between Edge, bridge, and Central uses HTTPS/TLS. The local Edge service (127.0.0.1:8787) uses a self-signed certificate. The Central API supports mutual TLS (mTLS) for robot authentication.

Authentication

Data privacy

No raw sensor data is ever transmitted over the network. Only aggregated DailyReports move between Edge and Central. The LLM never sees raw values — it receives pre-computed feature summaries.

Production deployment

For production, generate proper TLS certificates for Central using your PKI. Self-signed certs are sufficient for development and air-gapped demos. See the admin guide for mTLS setup.

API overview

The Central REST API uses JSON throughout. All routes except /v1/auth/login require Authorization: Bearer <token>.

Base URL: https://YOUR_CENTRAL:8080

Authentication

POST/v1/auth/loginGet a JWT tokenpublic
POST/v1/auth/refreshRenew a tokenJWT

Fleet & robots

GET/v1/robotsList robots in your tenantJWT
GET/v1/client/robots/:id/timelineActivity timeline for a robotJWT
GET/v1/client/fleet-healthFleet health scores and risk levelsJWT

Intelligence

GET/v1/insightsList generated insightsJWT
POST/v1/insights/analyzeTrigger an LLM analysisJWT
GET/v1/anomaliesList detected anomaliesJWT

Webhooks

POST/v1/webhooksCreate a webhook (insight.created, anomaly.critical, …)JWT
DELETE/v1/webhooks/:idDelete a webhookJWT
Full reference

The full API reference documents every endpoint with parameters, request/response examples and integration code for Python, JavaScript and curl.

Prometheus & Grafana integration

Eimdall exposes a Prometheus-compatible metrics endpoint on a dedicated port 9091, completely isolated from the main API. Your existing Grafana stack continues to work — Eimdall publishes what it knows, you visualise it the way you want.

Commercial positioning

Eimdall's value is product intelligence, not raw monitoring. Prometheus/Grafana gives you time-series dashboards. Eimdall gives you why — adoption patterns, predictive failure, usage friction, LLM insights. They complement each other; they do not replace each other.

Security model (4 independent layers)

The metrics endpoint is hardened by design. There is no configuration that allows a non-authenticated, non-encrypted scrape.

LayerMechanismPurpose
1 — Network isolationDedicated port 9091, firewallable independentlyInaccessible if port is not explicitly opened
2 — TLS 1.3 onlyServer certificate required (EIMDALL_METRICS_TLS_CERT/KEY)All traffic encrypted, no downgrade possible
3 — mTLS (recommended)Client CA required (EIMDALL_METRICS_CLIENT_CA)Only Prometheus with a signed cert is accepted
4 — Bearer tokenConstant-time comparison (EIMDALL_METRICS_TOKEN)Defense-in-depth, timing-attack resistant

Environment variables

EIMDALL_METRICS_TOKEN=<secret>          # Required — bearer token for Prometheus scrape
EIMDALL_METRICS_TLS_CERT=/path/cert.pem  # Required — server TLS certificate (PEM)
EIMDALL_METRICS_TLS_KEY=/path/key.pem   # Required — server TLS private key (PEM)
EIMDALL_METRICS_CLIENT_CA=/path/ca.pem  # Recommended — CA to verify Prometheus client cert (mTLS)
EIMDALL_METRICS_PORT=9091               # Optional — default 9091

If EIMDALL_METRICS_TOKEN or TLS variables are absent, the metrics server does not start — no fallback, no plaintext endpoint.

Prometheus scrape configuration

Add the following job to your prometheus.yml:

scrape_configs:
  - job_name: eimdall
    scrape_interval: 30s
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/eimdall-ca.pem       # CA that signed Eimdall's cert
      cert_file: /etc/prometheus/certs/prometheus.pem     # Prometheus client cert (mTLS)
      key_file: /etc/prometheus/certs/prometheus-key.pem  # Prometheus client key
    authorization:
      type: Bearer
      credentials_file: /etc/prometheus/eimdall-token     # File containing the bearer token
    static_configs:
      - targets: ["YOUR_CENTRAL_HOST:9091"]

Generating certificates (quick start)

# 1. Create CA
openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca.pem \
  -days 3650 -nodes -subj "/CN=eimdall-metrics-ca"

# 2. Server cert for Eimdall
openssl req -newkey rsa:4096 -keyout server-key.pem -out server.csr \
  -nodes -subj "/CN=YOUR_CENTRAL_HOST"
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server.pem -days 365

# 3. Client cert for Prometheus (mTLS)
openssl req -newkey rsa:4096 -keyout prometheus-key.pem -out prometheus.csr \
  -nodes -subj "/CN=prometheus-scraper"
openssl x509 -req -in prometheus.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out prometheus.pem -days 365

# 4. Generate bearer token
openssl rand -hex 32 > eimdall-token

Available metrics

MetricTypeLabelsDescription
eimdall_robot_last_seen_timestamp_secondsgaugerobot_id, tenant_idUnix timestamp of last ingest for this robot
eimdall_robot_onlinegaugerobot_id, tenant_id1 if robot was seen in the last 24 h, else 0
eimdall_fleet_robot_countgaugetenant_idTotal robots registered for the tenant
eimdall_fleet_online_countgaugetenant_idRobots seen in the last 24 h
eimdall_fleet_critical_countgaugetenant_idRobots with at least one severity ≥ 3 anomaly in 24 h
eimdall_anomaly_events_totalcounterrobot_id, tenant_idCumulative anomaly events for this robot
eimdall_anomaly_severity_lastgaugerobot_id, tenant_idSeverity of the most recent anomaly (1–4)
eimdall_daily_reports_totalcountertenant_idDailyReports ingested for this tenant
eimdall_insights_totalcountertenant_idLLM insights generated for this tenant

Grafana dashboard

An importable Grafana dashboard is available at monitoring/grafana-dashboard.json in the eimdall-ros2-bridge repository.

Import steps:

  1. In Grafana → Dashboards → Import
  2. Upload grafana-dashboard.json
  3. Select your Prometheus datasource when prompted
  4. Click Import

The dashboard includes: fleet KPIs (total/online/critical robots, insights, reports), online robots over time, anomaly events per hour, per-robot status table with ONLINE/OFFLINE badge, and cumulative insights/reports time series.

Grafana alerts

You can build Grafana alert rules on top of any Eimdall metric. For example: alert when eimdall_fleet_critical_count > 0 or when eimdall_robot_online{robot_id="..."} == 0 for more than 10 minutes. Eimdall's webhooks (via POST /v1/webhooks) provide a complementary push channel.

OpenTelemetry integration

Eimdall supports OpenTelemetry natively on two channels:

Architecture

The OTel Collector acts as the bridge: it scrapes Eimdall metrics, receives Eimdall traces, and forwards everything to Grafana Tempo, Jaeger, Datadog, or any OTLP-compatible backend — without changing Eimdall's config.

Activating trace export

Set one environment variable before starting Eimdall Central:

EIMDALL_OTEL_ENDPOINT=http://otel-collector:4317

If the variable is absent or empty, Eimdall runs in console-only mode — no OTLP traffic, zero overhead.

Eimdall sends spans with these resource attributes:

OTel Collector configuration

A ready-to-use Collector config is available at monitoring/otel-collector-config.yaml in the eimdall-ros2-bridge repository. It wires both channels (metrics scrape + traces receive) and forwards to Grafana Tempo.

receivers:
  prometheus:          # scrape Eimdall metrics (port 9091, mTLS + bearer)
    config:
      scrape_configs:
        - job_name: eimdall
          scrape_interval: 30s
          scheme: https
          tls_config:
            ca_file:   /etc/otelcol/certs/eimdall-ca.pem
            cert_file: /etc/otelcol/certs/collector-client.pem
            key_file:  /etc/otelcol/certs/collector-client-key.pem
          authorization:
            type: Bearer
            credentials_file: /etc/otelcol/eimdall-token
          static_configs:
            - targets: ["EIMDALL_HOST:9091"]

  otlp:                # receive Eimdall traces (EIMDALL_OTEL_ENDPOINT)
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    timeout: 5s

exporters:
  otlp/tempo:
    endpoint: GRAFANA_TEMPO_HOST:4317

service:
  pipelines:
    metrics:
      receivers:  [prometheus]
      processors: [batch]
      exporters:  [otlp/tempo]
    traces:
      receivers:  [otlp]
      processors: [batch]
      exporters:  [otlp/tempo]

Running the Collector with Docker

docker run --rm \
  -v $(pwd)/otel-collector-config.yaml:/etc/otelcol/config.yaml \
  -v /etc/eimdall/certs:/etc/otelcol/certs \
  -v /etc/eimdall/eimdall-token:/etc/otelcol/eimdall-token \
  -p 4317:4317 -p 4318:4318 \
  otel/opentelemetry-collector-contrib:latest

Supported OTel backends

BackendSignalsExporter in Collector config
Grafana TempoTraces + Metricsotlp/tempo
JaegerTracesotlp/jaeger (OTLP receiver in Jaeger)
DatadogTraces + Metricsdatadog exporter
New RelicTraces + Metricsotlp (New Relic OTLP endpoint)
HoneycombTracesotlp (Honeycomb OTLP endpoint)
Any OTLP backendTraces + MetricsStandard otlp exporter
Security

The Collector-to-Eimdall path uses the same mTLS + bearer token as direct Prometheus scraping. The Eimdall-to-Collector trace path (OTLP gRPC) should run on a private network or be secured with TLS — configure tls: on the Collector's otlp receiver if the Collector is not co-located with Central.