Skip to content

Telemetry Ingestion Server

telemetry-ingestion-srv is the HTTP service that accepts telemetry from instrumented processes. It writes event payloads to object storage and records the metadata (processes, streams, blocks) in PostgreSQL. Every acknowledged write is durable before the request returns — see Service Lifecycle & Shutdown.

This is the only service that producers talk to. It does no query or materialization work; those belong to FlightSQL and the maintenance daemon.

Running the binary

# from the rust/ directory
cargo run --release --bin telemetry-ingestion-srv -- \
  --listen-endpoint-http 0.0.0.0:9000

The Docker image (ingestion.Dockerfile) exposes port 9000 and runs the same binary as its entrypoint.

Environment variables

Variable Required Description
MICROMEGAS_SQL_CONNECTION_STRING Yes PostgreSQL connection for lake metadata
MICROMEGAS_OBJECT_STORE_URI Yes Object store for payloads (file:///path, s3://…, gs://…)
MICROMEGAS_OIDC_CONFIG No OIDC configuration JSON
MICROMEGAS_DEFAULT_AUDIENCE No The deployment's default audience (default: public) — what analytics-web-srv's key mint routes fall back to (API Keys). The ingestion role now reads it too: a process whose credential carries no audience is stamped with this value explicitly at write time, the same audience the roles that build a lakehouse (FlightSQL, Maintenance) apply where a legacy or replicated row's audience is read. One knob, one meaning: what anything arriving without an audience gets. Read unprefixed — see the monolith's "one prefix asymmetry" note.
MICROMEGAS_SHUTDOWN_GRACE_PERIOD_SECONDS No Drain timeout on SIGTERM (default: 25)

CLI flags

Flag Default Description
--listen-endpoint-http 127.0.0.1:8081 HTTP bind address
--disable-auth off Disable authentication (development only)
--shutdown-grace-period-seconds 25 Seconds to drain in-flight requests on SIGTERM

Bind address

The binary defaults to 127.0.0.1:8081, which only accepts local connections. To accept traffic from other hosts (or from inside a container), bind to 0.0.0.0 and the port you intend to publish, e.g. --listen-endpoint-http 0.0.0.0:9000.

Authentication

If neither MICROMEGAS_OIDC_CONFIG nor a non-empty ingestion_api_keys DB table is present, the server refuses to start unless --disable-auth is passed. This prevents accidentally running an open ingestion endpoint. For configuration details and provider precedence, see Authentication.

Schema migration and admin seeding

This is the process (alongside the monolith) that runs the data-lake schema migration, and that seeds the reserved admins group — see Groups for the seeding rules.

Key management

This service always attaches a DB-backed key store (ingestion_api_keys) built from its own data-lake connection for validating incoming API keys, but exposes no HTTP routes to mint, list, or revoke them — ingestion has no key-management HTTP surface of its own. Those operations are handled exclusively by analytics-web-srv's own /api/ingestion-api-keys* routes instead — see API Keys.

What gets stamped

A process, stream, and block registered under a credential that carries a write audience are each stamped with their own audience column — server-written from that credential, never trusted from the client payload. A block's or a stream's own stamp is the credential that wrote that row, never derived from the process_id/stream_id it points at. This is what makes the analytics-side audience filter (Authorization) a real security boundary instead of a client-asserted label.

  • DB-backed ingestion keys (ingestion_api_keys) each carry exactly one immutable write audience. Every process, stream, and block a key writes is stamped with that audience.
  • OIDC credentials carry no bound audience of their own. A row registered under one is stamped with the resolved deployment default.
  • No auth provider configured (--disable-auth): stamped with the deployment default too, for the same reason.

Every row registered through this path is stamped: a credential with no bound audience resolves to MICROMEGAS_DEFAULT_AUDIENCE (default public) at the write edge and is stamped with it explicitly, exactly like any other audience — see Authorization → Audience stamping. Admin bulk_ingest/replication hard-fails on a missing audience rather than writing a row with none.

The reserved micromegas.* property namespace is server-written only: any micromegas.* property a client sends is dropped at ingestion and logged (warn!), naming the key. A client cannot self-stamp micromegas.audience — data gets the deployment default unless its credential is a DB ingestion key bound to the audience it should carry.

Each audience gets its own OTLP id namespace, except the deployment default, which keeps the un-salted namespace — so traffic with no bound audience always derives the same process_id/stream_id/block_id. Rotating a DB-backed ingestion key to a different audience splits a long-lived producer's history across two process ids, since the data now genuinely belongs to two audiences — see Authorization → Audience stamping for the full mechanism.

Health and readiness

The server exposes GET /health (unconditional) and GET /ready (probes PostgreSQL and object storage) on the same port as ingestion. Point load-balancer health checks at /ready. See Readiness probes for ALB tuning.

Scaling

Ingestion is stateless — every instance reads and writes the same lake — so it scales horizontally behind a load balancer. Add instances to raise write throughput; PostgreSQL and the object store are the shared backends. Writes are idempotent: block payload objects are stored at deterministic paths with a create-only write (first write wins; a colliding write is rejected, not applied), and the row insert still uses ON CONFLICT DO NOTHING, so retried or duplicated requests never double-count or corrupt a previously stored payload.

The object store backing ingestion must support conditional put (PutMode::Create). AWS S3 supports it with no configuration. An S3-compatible store explicitly configured with aws_conditional_put=disabled will fail every block write rather than silently falling back to overwrite — see the CHANGELOG entry for this behavior.

Before depending on a new S3-compatible endpoint, verify it actually enforces conditional put: write a key, write different bytes to the same key, read it back, and confirm either an AlreadyExists error on the second write, or (if it succeeded) that the read still returns the first write's bytes. If neither holds, the store does not honor conditional put and the write-once guarantee does not hold against it.

Caveat: a store that accepts If-None-Match: * but doesn't enforce it (returns 200 and overwrites regardless) will make put_if_absent return Created on every call — no error, no log line — so the write-once invariant silently degrades to a plain overwrite. There is no code-level way to detect this; it must be verified operationally with the procedure above before depending on the store.

Producer configuration

Producers point at the ingestion endpoint with MICROMEGAS_TELEMETRY_URL. If the ingestion service falls behind or becomes briefly unreachable, the Rust telemetry sink buffers and retries; queue sizes, concurrency, and timeouts are tunable — see Telemetry Sink Configuration.