Your service (client library)
-> in-process aggregation (DDSketch + counters, per flush window)
-> Connect/gRPC upload (protobuf, zstd-compressed, TLS in production)
-> ingest handler (authenticate key, resolve tenant, apply guardrails)
-> ClickHouse (data plane: Summaries, rolled up over time)
Dashboard (server-rendered)
-> OIDC login (GitHub / Google) -> Postgres (control plane: tenants, members, keys)
-> tenant-scoped ClickHouse queries -> HTML response
Nothing is written to disk on the client side; aggregation lives in process memory for the duration of one flush window before being shipped.
The only credential a client needs is the ingest key, delivered via
MAPING_KEY. It is sent as a bearer Authorization header on every
request. The server resolves the tenant from the key; the tenant is never
configured client-side. A tenant can hold several independently
revocable/rotatable labeled keys (for example one per environment), all
resolving to the same organization's data: keys are a rotation convenience,
not an isolation boundary between them.
On startup, a recorder with a resolved key sends a one-time registration ping (service, instance, SDK version) before any metrics traffic. It proves the key is valid and the collector is reachable, and drives the dashboard's onboarding state. Setup problems (a bad key, clock skew, a frozen cardinality cap, network or TLS failures) are logged at a rate-limited level on the host and surfaced in the dashboard; metrics data itself still fails open, but setup problems are not silent.
The dashboard has three startup modes, selected automatically from configuration:
- No
MAPING_POSTGRES_DSN: auth is off, a single constant dev tenant is used, and there are no login routes. This is the local/dev default. MAPING_POSTGRES_DSNset, no OIDC credentials: a dev-login button starts a session as the seeded dev-org admin, for testing against a real control plane without registering an OAuth app.MAPING_POSTGRES_DSNplus at least one OIDC provider's credentials: real login via GitHub and/or Google. No passwords are stored; identity is resolved through the provider's userinfo endpoint after token exchange. Dev-login is disabled whenever a real provider is configured.
Sessions are stateless, HMAC-SHA256-signed cookies keyed by
MAPING_SESSION_KEY (32 bytes minimum), with a 7-day expiry. There is no
server-side session store, so a production HTTPS deployment must set this
key explicitly and keep it stable across restarts and across every instance
of a multi-instance deployment; a plain HTTP dev deployment falls back to an
ephemeral key with a startup warning.
State-changing dashboard actions (creating or revoking an ingest key) are
protected by a stateless HMAC synchronizer token bound to the caller's
organization, issued on the form page and verified before any mutation, in
addition to the SameSite=Lax session cookie. Every dashboard HTML response
carries a Content-Security-Policy restricting scripts to a single
self-hosted helper (script-src 'self'); the public documentation pages
(including this one) carry script-src 'none', since they contain no
JavaScript at all.
ClickHouse multitenancy is row-level: every row is stamped with the server-resolved tenant, and there is no infrastructure wall between tenants. Isolation is enforced in code: the read API only exposes queries bound to a resolved tenant handle, so an un-scoped, cross-tenant query is not something the query layer can express.
Client uploads use gRPC over HTTP/2. An https:// endpoint uses TLS; an
http:// endpoint (local/dev only) uses cleartext HTTP/2 (H2C). The
production docker-compose overlay publishes only the server's port; the
data plane and control plane stay on the internal network.