Latest v0.17.0

Oct 16, 2026

Every release, in order. The server, the dashboard and the client packages ship together under one version number.

68 releases/705 changes

/minor

server · dashboard · github · alerts

GitHub Integration

Link projects to GitHub repositories, sync issues both ways, resolve from commits and open GitHub issues automatically

GitHub integration

01

Rustrak creates its own private GitHub App for your server, so no shared app or token sits between you and GitHub, and it only sees the repositories you pick. Connect it from Settings → Integrations, then choose a repository in each project's Settings → GitHub.

  • Every stack frame of your app links to its line on GitHub. Path mappings turn /app/lib/db.ts into src/lib/db.ts when the two differ
  • Issues create or link GitHub issues, and resolving, reopening, commenting and assigning stay in sync both ways
  • A commit on the default branch that says Fixes WEB-12 resolves WEB-12
  • Issues list suspect commits and the CODEOWNERS behind the file they broke in
  • New errors can open a GitHub issue automatically, filtered by level, environment and number of events

GitHub reaches Rustrak through webhooks at $PUBLIC_URL/api/github/webhook, so set PUBLIC_URL before connecting. Behind a firewall everything but the GitHub-to-Rustrak sync keeps working. See GitHub.

Alert rule filters

02

Alert rules can now filter by level, environments and, for New Issue rules, minimum events. A New Issue rule fires once per issue, on the event that reaches the count, even if retention later lowers it.

Improvements

03
  • The access log shows the client's address behind a reverse proxy instead of the proxy's, the same address ingest stores on events (@janatjak)
  • An event digested while muted no longer sends an issue alert when it is replayed
  • @rustrak/client lists alert rules whose stored conditions predate filters instead of failing the whole list
  • Telemetry reports whether GitHub is connected and how many projects use it. Counts and a boolean only, never a repository or account

/patch

server · sourcemaps · security

Source Maps for Sentry SDK v11

Source maps uploaded by the Sentry SDK v11 bundler plugins now upload and resolve, index maps included

Uploads from Sentry SDK v11 bundler plugins

01

The v11 bundler plugins, @sentry/nextjs among them, upload source maps with the sentry npm package instead of sentry-cli. That uploader requires a compression field in the chunk-upload capability, and Rustrak did not send one, so every upload failed before a single chunk went out. The capability now answers "compression": [], which means chunks arrive uncompressed (@chrisedington).

Index source maps resolve

02

An index map is built from sections, each with its own embedded map, and Rustrak skipped them, so their frames stayed minified. sentry-cli sourcemaps upload flattens them before sending, but the v11 uploader sends them as they are, and Next.js builds with Turbopack write one for nearly every chunk. A frame now resolves inside the section that contains its position. A position before that section's first mapping stays unresolved instead of borrowing the previous section's last token. Index maps already stored start resolving without a re-upload (@chrisedington).

Improvements

03
  • Re-uploading a source map for the same debug_id replaces the previous one, as in Sentry. Before, the first upload won and a corrected map for an unchanged chunk was silently dropped
  • rustls 0.23.46, for RUSTSEC-2026-0346 through RUSTSEC-2026-0349
  • Dependency updates, including minijinja 3, zip 9 and actix-web 4.16

/patch

server · ingest · postgres · performance

PostgreSQL Ingest Fixes

Payloads PostgreSQL used to reject with a 500 are now ingested, and transactions with many spans store faster

No more PostgreSQL 500s on real SDK payloads

01

PostgreSQL enforces column lengths and rejects NUL characters, and SQLite does neither. So a payload that SQLite stored without complaint failed the whole envelope on PostgreSQL, and the SDK got a 500 back. Rustrak now cleans those payloads the way Relay does:

  • NUL characters are stripped at ingest, and from event files queued by an earlier version
  • String fields longer than their column are truncated instead of rejected
  • Events, transactions, spans and breadcrumbs dated before 1970 are dropped, and a timestamp outside the range PostgreSQL can store reads as missing
  • An unknown level falls back to the default instead of being stored as is

Faster span storage

02

Spans from transactions, standalone span items and v2 span containers are written with multi-row INSERTs. A transaction with 1,000 spans goes from 1,000 database round trips to 4.

Improvements

03
  • Ingest latency in telemetry is measured from when the request body has arrived, so a slow client upload no longer counts
  • Telemetry counts database errors by code (db_errors_by_code): a SQLSTATE on PostgreSQL, a result code on SQLite, never the message

/patch

server · sessions

Unhandled Sessions

Sessions with the unhandled status are counted, and session ingest no longer deadlocks on PostgreSQL

Unhandled session status

01

The JavaScript SDKs send unhandled instead of crashed since 11.x, for an unhandled error the process survived (protocol 1.6.0). Rustrak now accepts it (@edsonmartins).

  • Session updates with "status": "unhandled" and session aggregates with an unhandled count are both ingested
  • They count as errored, not crashed, so the crash-free rate is unaffected

No more session deadlocks on PostgreSQL

02

Session counts were written to the database on every ingest request. Under load on PostgreSQL those writes deadlocked on the same rows and the SDK got a 500 back.

  • Counts are buffered in memory and written by the aggregator on its own interval, as Relay does
  • A graceful shutdown waits for any flush in progress, then writes what is left, so sessions already accepted are not lost

Improvements

03
  • A session status this version does not know counts as errored instead of dropping the session, matching Relay
  • Healthy session counts are clamped at zero like Sentry, so an SDK that resends a terminal update cannot drive them negative

/patch

server · dashboard · storage

Faster Storage Stats

Storage stats and cleanup previews estimate span counts instead of counting every span

Faster storage stats

01

The storage page and the cleanup preview no longer count the spans table row by row. On a large instance that count read 37M rows and took over a minute.

  • Spans under transactions are estimated from a random sample of 1,000 transactions per project, then scaled to the project's transaction count. On a 21M-span production cleanup the estimate came within 1% and took about a second
  • The count is exact when a project has 1,000 transactions or fewer, and spans that belong to no transaction are always counted exactly
  • The dashboard marks estimated span counts with ≈

/minor

server · dashboard · client · mcp · storage · metrics

Prometheus Metrics

An opt-in Prometheus endpoint, bulk issue deletion by state, and storage cleanups that run in the background

Prometheus metrics

01

Set RUSTRAK_METRICS=on and the server answers GET /metrics on its existing port. It is off by default, and the route returns 404 until it is enabled (@WahidinAji).

  • Reports ingest totals and latency, digest outcomes, 5xx responses by route, failed alerts by provider, the pending spool and the database size
  • Labels never carry project IDs, user details or event contents
  • /metrics has no authentication: keep it on a trusted network or restrict it at the reverse proxy

Background storage cleanup

02

A cleanup now runs on the server in small batches, each committed on its own, so ingestion keeps working while it runs. The storage page shows its progress, and you can leave the page and come back to it later. Only one cleanup runs at a time.

  • Breaking: POST /api/storage/cleanup answers 202 with the job instead of the final counts, and client.storage.executeCleanup() returns a CleanupStatus
  • GET /api/storage/cleanup/status reports idle, running, completed or failed with the rows removed so far. It is available as client.storage.getCleanupStatus() and as the MCP tool get_storage_cleanup_status
  • If the server restarts mid-cleanup, what was already deleted stays deleted

Improvements

03
  • DELETE /api/projects/{id}/issues takes either ids or a filter (open, resolved, muted, all), in the client and the MCP bulk_delete_issues tool (@WahidinAji)
  • The storage page estimates per-project size from a sample of recent rows, so it stays fast however much data the instance holds. Row counts stay exact
  • The integrations page links back to the alert settings you came from (@WahidinAji)
  • Dependency updates

/patch

server · dashboard · auth · alerts · client

Change Password

Change your own password from account settings, and alert rules with a zero cooldown fire every time

Change your password

01

Account settings has a Change password dialog. Enter the current password and a new one, and the change is made without signing out.

  • POST /api/auth/me/password takes current_password and new_password and answers 204. A wrong current password or an empty new one answers 400 with the field that failed
  • Two overlapping requests cannot both succeed: the new hash replaces only the one that was verified
  • @rustrak/client exposes it as auth.changePassword()
  • The dialog is translated into all five dashboard languages

Improvements

02
  • An alert rule with a cooldown of zero or less fires on every matching event, and the cooldown is checked against the rule's current value rather than the one loaded when the alert started (@jav-ed)

/patch

server · dashboard · auth · sso · rate-limits · client · docker

Single Sign-On

Sign in through any OpenID Connect provider, look up an event by the ID the SDK returned, and quotas that hold exactly at their limit and tell the SDK how long to back off

Single sign-on with OpenID Connect

01

Rustrak signs people in through any OpenID Connect provider, such as Pocket ID, next to the email and password form (@GhaziBenDahmane). Set OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET and OIDC_REDIRECT_URL, and the sign-in page shows the provider's button. Leave OIDC_ISSUER_URL unset and nothing changes.

  • An identity is tied to the provider's issuer and subject, not the email, so changing an address at the provider keeps the same account
  • OIDC_AUTO_PROVISION creates an account on first sign-in, and OIDC_LINK_EXISTING_ACCOUNTS links a sign-in to an account with the same email. Both are off by default
  • Linking never happens on the provider's word alone: the owner types the account's password once, and the link is made after that
  • OIDC_REQUIRE_EMAIL_VERIFIED (on by default) and OIDC_ALLOWED_DOMAINS decide which identities are let in
  • Signing keys are fetched per sign-in, so key rotation at the provider keeps working
  • An OIDC_ISSUER_URL without the other required settings stops startup instead of leaving SSO silently off

The single sign-on guide covers the provider setup.

Find an event by its Sentry ID

02

GET /api/projects/{id}/events/sentry/{event_id} resolves the ID a Sentry SDK returns from captureException, compact or hyphenated, to the full event and its issue, without knowing the issue first. @rustrak/client exposes it as events.getBySentryId() (@jav-ed).

Quotas that hold exactly

03

Rate limits are counted in fixed minute and hour windows, checked and incremented in the same transaction that stores the event, the way Relay does it. Concurrent bursts can no longer slip past a limit.

  • A 429 carries X-Sentry-Rate-Limits next to Retry-After, and browsers can read both, so SDKs back off for the right time
  • The defaults are raised: 60,000 events a minute and 1,000,000 an hour overall, 20,000 and 300,000 per project
  • A project can set its own lower limits in its general settings. The operator's MAX_EVENTS_PER_PROJECT_* stays the ceiling
  • Events a quota dropped are counted per project and shown in the same place

Improvements

04
  • The account page has a timezone picker, so each reader chooses the zone dates are shown in
  • Breadcrumb timestamps are read with the grammar Relay accepts, including lowercase t and z, a space separator and the leap second
  • docker-compose.yml runs one SQLite container serving the dashboard and API, and refuses to start without SESSION_SECRET_KEY

/minor

server · dashboard · deployment · docker · performance · telemetry

One Container

The server serves the dashboard, so one container and one address answer both the page and the API. Memory comes back down after a burst, parsed source maps are kept across events, and the server sends an anonymous heartbeat that one variable turns off

Before you upgrade. Read Upgrade to 0.15. Take a backup first: 0.15 adds one migration, and going back to 0.14 means restoring it. The ui container is no longer needed, the sign-in page moves to /login, anonymous telemetry is on unless RUSTRAK_TELEMETRY=off is set, and RUSTRAK_VERSION_CHECK_ENABLED no longer exists.

One container does everything

01

The dashboard is compiled to static files that the server hands out at /, so the same process answers the page and the API. Open the server's own address, :8080 by default, and sign in there.

  • One origin. The browser calls the API where it loaded the page from, so the session cookie is first-party and no CORS applies. Behind a reverse proxy there is one upstream, and PUBLIC_URL is the only address to get right
  • The sign-in page is at /login, where it was /auth/login. Every other address is unchanged
  • Still optional. RUSTRAK_DASHBOARD=off keeps the server API-only, and an image built without a dashboard serves the API as before
  • rustrak/rustrak-ui stays for a dashboard hosted away from its data: nginx serving the same bundle and proxying the API to RUSTRAK_API_URL, about 30 MB on disk and under 15 MB of memory idle
  • The dashboard is rebuilt on Vite and TanStack Router. It looks and behaves the same

Memory that comes back down

02

The server uses mimalloc as its allocator. With glibc, resident memory climbed with every burst and stayed there: 700 MB at one event per second in production, 2 GB under a burst heavy on source maps. The same load now peaks around 520 MB and returns to 150 MB when idle.

Source maps are parsed once per event instead of once per frame, and the parsed form is kept across events in a cache bounded by SOURCEMAP_CACHE_MB (default 64). A burst that took about five minutes to digest now takes under ten seconds.

Anonymous telemetry

03

The server sends one anonymous report every six hours: version, platform, database backend, memory use, rounded counts and health counters. Never IPs, hostnames, URLs, names, DSNs, payloads or messages. The first report leaves ten minutes after startup, the startup log says whether telemetry is on, and GET /api/telemetry/preview shows the exact document as an admin. RUSTRAK_TELEMETRY=off or DO_NOT_TRACK=1 sends nothing. The telemetry page lists every field.

Improvements

04
  • Issue links in alert notifications follow PUBLIC_URL when DASHBOARD_URL is not set. They used to point at http://localhost:3000
  • Sign-in treats the email address case-insensitively, and accounts created before with capitals in their address still sign in (@edideaur)
  • Accepting an invitation for an address that already has an account, in any casing, is refused instead of creating a second account
  • The server image declares a single volume, /data. Source maps live in it by default, so -v rustrak_data:/data keeps them across upgrades
  • The docs gain an Upgrading section with a guide per release that needs steps from you

/minor

server · telemetry · configuration

Anonymous Telemetry

The server sends one anonymous heartbeat every six hours, so a memory regression or a new panic shows up across installations after a release. Every field is documented, nothing that came from an SDK, a user or a project ever leaves, an admin endpoint shows the exact document before it is sent, and one variable turns it off

Release candidate. This is rustrak/rustrak-server:next (and :v0.15.0-rc.2, :next-postgres), @rustrak/client@next and @rustrak/mcp@next. latest stays on 0.14 until 0.15 ships. It adds one nullable column, installation.telemetry_id, and touches nothing else.

Anonymous telemetry

01

Rustrak runs on machines we never see, so "is this version leaking memory for anyone?" and "does anyone still run SQLite on ARM?" were guesses. The server now sends one heartbeat document, about 2 KB, every six hours. The first one leaves ten minutes after startup, which is time to read the notice in the log and set the switch.

  • Identity and build. A random UUID minted on first start and stored in the database, never derived from the host; the version, OS, architecture, whether it runs in a container, the database engine and version, whether the dashboard is mounted, and uptime
  • Resources. CPU count, host memory, and the process's resident memory over the window, sampled once a minute
  • Volume. Projects, users, open issues, and events, transactions, sessions and logs in the last 24 hours, each blurred to two significant digits before it leaves: 4,517 events is reported as 4,500
  • Health. Counters since the previous report: ingests accepted and rejected by reason, ingest latency p50 and p99, digest successes and failures, server errors per route pattern (/api/issues/{id}, never the real path), failed alert deliveries per provider kind, and panics per source location inside Rustrak's own code, without the message
  • Configuration. Booleans and kinds only: whether SSL proxy, PUBLIC_URL, SMTP and a session secret are set, and which alert provider kinds exist

Never sent: IP addresses, hostnames, PUBLIC_URL, DATABASE_URL, email addresses, project or team names, DSNs, event payloads, stack traces, error messages, request paths, release or environment names, panic messages, log lines. The telemetry page lists every field.

Turning it off, and seeing what would go

02
RUSTRAK_TELEMETRY=off   # or DO_NOT_TRACK=1; either one is enough
  • RUSTRAK_TELEMETRY accepts on, off, true, false, 1 and 0. Any other value refuses to start, so a typo is never read as "on"
  • The startup log says which state it is in and prints the instance id
  • GET /api/telemetry/preview, as an admin, returns the next heartbeat exactly as it would leave, plus enabled and the reason if it is off. It works either way, so the decision can be made on the real document
  • A binary built from source without the project's key (a fork, a local cargo build) has telemetry off and says so at startup. The published images receive the key as a BuildKit secret, so it never lands in the image metadata
  • Cost on the server: one atomic increment per request, one small /proc read per minute, three count queries and one POST with a 10 second timeout every six hours. A failed send is a debug line and is not retried

10 of 68 releases