DocsOperatorsMetrics

Metrics

rim-server exposes Prometheus metrics in the text exposition format at GET /metrics on a listener of its own. The API port never serves them.

Turning them on #

Variable Meaning
RIM_METRICS_ADDR ip:port of the metrics listener. Unset (the default), no recorder is installed and no listener opens. A value that is not an ip:port address stops the server at boot.

On a host, 127.0.0.1:9090 keeps the endpoint on the machine; run Prometheus there or scrape it through an SSH tunnel. In a container, 127.0.0.1 is the container itself, so bind 0.0.0.0:9090 and do not publish the port: Prometheus reaches it over the compose network. deploy/compose.yml has the line commented out in the rim service.

The endpoint has no authentication. Keep it on an internal network, and never route it through Caddy or any other public reverse proxy: it shows request rates, limiter names and queue sizes to whoever can read it.

A minimal scrape configuration:

scrape_configs:
  - job_name: rim
    static_configs:
      - targets: ["rim:9090"]

The metrics #

Names start with rim_; counters end in _total, and the unit is part of the name (_seconds, _bytes, _connections, _items).

Requests #

Metric Type Labels Meaning
rim_http_requests_total counter route, status HTTP requests on the API port.
rim_http_request_duration_seconds histogram route, status Time from receiving a request to sending its response head. Buckets from 5 ms to 30 s. A WebSocket upgrade counts until the 101, not for the life of the socket.
rim_s2s_requests_total counter outcome Requests received under /s2s/v1/, counted once server-to-server traffic is on.
rim_forwarded_requests_total counter direction, outcome Requests forwarded to a host.

route is a route class, never the raw path, so the number of series stays fixed:

route Paths
api /api, /api/ws, /api/sdl, /api/hooks/… and anything else under /api not listed below
files /api/files/…, /api/attachments/…, /api/reports/…, /api/plugin-bundles/…
remote-forward /api/remote/…, a local user's request to a space hosted elsewhere
s2s /s2s/…
wellknown /.well-known/…
health /healthz, /readyz, /livez
other everything else, including unknown paths that answer 404

status is the status class: 1xx, 2xx, 3xx, 4xx or 5xx.

rim_s2s_requests_total{outcome}:

outcome Meaning
ok Every check passed and the endpoint answered below 500; a 404 for an unknown principal is ok.
signature_invalid The signature, its parameters, the key, the body digest or the clock window was refused (401), or the nonce was a replay (409).
peer_blocked The peer is denied, outside the host allow-list, blocked, could not be pinned from its origin, or its key log forked (403).
rate_limited The per-peer limit refused it, or the endpoint answered 429.
error A malformed or oversized request before the checks, or an answer of 500 or above.

rim_forwarded_requests_total{direction, outcome}: direction="outgoing" is this server as a relay sending a local user's request through /api/remote/…; direction="incoming" is this server as a host running a forwarded request at /s2s/v1/forward/….

outcome Meaning
ok Answered below 400.
refused Answered 4xx other than 429: no session, a bad device request, not delegated, a replayed device seq, refused by the host.
rate_limited Answered 429, by this server or passed through from the host.
upstream_unavailable Outgoing only: the host could not be reached or gave no verified answer (502).
error Answered 500 or above.

Refusals #

Metric Type Labels Meaning
rim_rate_limit_rejections_total counter limiter Requests a rate limiter or quota refused.
rim_ip_blocked_requests_total counter Requests refused because their address is on the IP deny list (rim_server ip block).
rim_pow_rejections_total counter Key-only signups whose proof-of-work did not meet RIM_REGISTRATION_POW_BITS or was malformed.

limiter values:

limiter What it limits
login Password logins per address, per address and account, and per account.
key_auth_failure Failed key logins per address.
key_challenge Key login challenges per address.
signup Open and proof-of-work signups per address.
preview_invite Invite token previews per address.
password_reset Password reset requests per address.
password_reset_account Password reset requests per account; the caller sees success and no email is sent.
device_link_start, device_link_offer, device_link_answer, device_link_refusal Device linking steps.
mls_claim, mls_one_time_claim, mls_readd MLS key package claims and re-add requests.
command, command_discover Command runs per user and command, and command discovery.
workspace_plugin_change, plugin_bundle_upload Workspace plugin changes and bundle uploads per user.
s2s_peer Server-to-server requests per peer (RIM_S2S_PEER_RATE_PER_MIN).
peer_discovery Discovery document fetches, one per peer and sixty in all per minute.
relay_forward_device, relay_forward_user Requests a local user forwards, per device per minute and per user per day.
host_forward_device Forwarded requests run here, per remote device per minute.
dm_invites Invites a local user sends to people on other servers per day.
dm_requests DM requests filed here by one sender per day, or past the recipient's pending limit.
stranger_claim Key package claims by servers with no tie to the user.
delegations_per_relay Active delegations one relay holds on this host (RIM_S2S_SUBSCRIPTIONS_PER_PEER).
direct_revocation Device revocations brought directly, per address.
api_ip Requests to /api routes per client address (RIM_RATE_REQUESTS_PER_MIN_PER_IP).
api_mutations Mutation fields per session, or per address without one (RIM_RATE_MUTATIONS_PER_MIN_PER_SESSION).
messages Messages per user (RIM_RATE_MESSAGES_PER_MIN_PER_USER).
ws_connections Open WebSocket connections per user (RIM_RATE_WS_CONNECTIONS_PER_USER).
ws_subscriptions Subscriptions per WebSocket connection (RIM_RATE_SUBSCRIPTIONS_PER_CONNECTION).

Slow mode in a conversation is not counted here.

State #

Metric Type Labels Meaning
rim_db_pool_connections gauge Open connections in the database pool, read on each scrape.
rim_db_pool_connections_in_use gauge Pool connections checked out by a query or transaction, read on each scrape.
rim_db_pool_max_connections gauge The most connections the pool opens.
rim_relay_queue_items gauge state Items queued for local users' devices.
rim_relay_queue_bytes gauge state Bytes of those items.
rim_pending_migrations gauge Migrations this binary has that the database has not applied.
rim_build_info gauge version Always 1; version is the server version.

state is queued for items waiting for devices to read them, held for items behind a DM request the recipient has not answered. The relay queue and the pending migrations are read from the database when the listener starts and every 30 seconds after; a read that fails or takes over 5 seconds leaves the last value in place and logs a warning.

Audit retention #

Metric Type Labels Meaning
rim_audit_rows_purged_total counter Command audit rows the daily retention purge deleted (docs/operations/audit-retention.md).
rim_audit_retention_days gauge RIM_AUDIT_RETENTION_DAYS, set at boot; 0 when rows are kept forever.

Scheduled purges #

Metric Type Labels Meaning
rim_attachments_cleaned_total counter Attachment uploads the daily cleanup marked failed because they never completed (docs/operations/background-jobs.md).
rim_workspace_state_purged_total counter Expired workspace state entries the hourly purge deleted.
rim_conversation_invites_expired_total counter Expired conversation invitations the hourly purge deleted.

Example alert rules #

Readiness is checked from outside, by probing /readyz on the API port with the Prometheus blackbox exporter (module http_2xx, job rim-readyz). The second rule fires when peers' signatures start failing: a peer whose key changed without a rotation, clock skew, or someone probing the S2S API.

groups:
  - name: rim
    rules:
      - alert: RimNotReady
        expr: probe_success{job="rim-readyz"} == 0
        for: 2m
        labels:
          severity: page
        annotations:
          summary: "rim-server at {{ $labels.instance }} has failed its readiness check for 2 minutes"

      - alert: RimS2sSignatureFailures
        expr: sum by (instance) (rate(rim_s2s_requests_total{outcome="signature_invalid"}[5m])) > 0.2
        for: 10m
        labels:
          severity: ticket
        annotations:
          summary: "rim-server at {{ $labels.instance }} refuses over 12 server-to-server signatures a minute"
          description: "Check the warnings 'refused a server-to-server signature' for the peer and reason."

Other useful expressions:

# Share of API requests answered with 5xx
sum(rate(rim_http_requests_total{route="api",status="5xx"}[5m]))
  / sum(rate(rim_http_requests_total{route="api"}[5m]))

# 95th percentile API latency
histogram_quantile(0.95, sum by (le) (rate(rim_http_request_duration_seconds_bucket{route="api"}[5m])))

# A deploy that started before its migrations ran
rim_pending_migrations > 0

# The pool is close to exhausted
rim_db_pool_connections_in_use / rim_db_pool_max_connections > 0.9