Release candidate. Notes may still change. Not published as
latest.
Jarvis v2.0.0 — a lighter resolved history, easier silences and a documentation site
A big round since v1.12.0: the Resolved view now pages through the full history with much lower memory use, silences are easier to extend and configure, logins resume where they were interrupted, and the Helm chart can authenticate against a protected Alertmanager. SSO users can now see the groups their identity provider reports.
Breaking Changes
- Jarvis: The Resolved view changed how it filters. Regex filters there now use RE2 (no lookaheads/lookbehinds, no backreferences), and search matches individual label names and values instead of serialized JSON. Equal resolve timestamps are ordered by event ID, and Alerts Overview in the Resolved tab summarizes only the currently loaded page. Migration: review saved Resolved filters that use JS-only regex or JSON-fragment searches. Live (Active/Suppressed) filters and the legacy
GET /api/v1/alerts?state=resolvedresponse shape are unchanged. - Jarvis (SSO):
JARVIS_OIDC_ADMIN_CLAIMwas renamed toJARVIS_OIDC_GROUPS_CLAIM, without an alias. The old variable is ignored, and an SSO user's role is evaluated from the token at every login, so an SSO admin is demoted touserat the next login until the variable is renamed. Migration: renameJARVIS_OIDC_ADMIN_CLAIMtoJARVIS_OIDC_GROUPS_CLAIM(same value, e.g.groupsorcognito:groups) before upgrading;JARVIS_OIDC_ADMIN_VALUEkeeps its name and meaning. Jarvis logs a warning at startup whenJARVIS_OIDC_ADMIN_VALUEis set withoutJARVIS_OIDC_GROUPS_CLAIM. - Helm chart 2.1.0: No breaking changes. The new
clusters[].auth.*andconfig.silenceDurationsvalues are empty by default, so an existing release renders unchanged.
What's in this release
The headline is the Resolved view. It no longer pulls the whole history into the browser (see the benchmarks below): it loads only when you open the tab, pages through the complete stored history on the server, and filters and searches across all of it. Behind that sits a memory pass over the backend (streamed history reads, cached WebSocket snapshots, bounded broadcast queues) and a fix for browsers being disconnected during bursts of alert events. Around it, silences got an Extend action and configurable durations, a login no longer costs you the action you were in the middle of, and the documentation now lives on its own website.
Memory: before and after
Measured with go test -bench '^BenchmarkMemory' -benchmem -count=5 on an Apple M4 Pro with synthetic data, comparing the state before the memory work with the final state. The numbers are local benchmarks, they vary with hardware and data.
Live alert list (GET /api/v1/alerts, 8,000 active alerts). The encoded snapshot is cached and only rebuilt when something actually changes, so repeated requests serve the cached bytes instead of re-marshaling the whole list.
| Before | After | |
|---|---|---|
| Memory per request | ~6.0 MB | ~5.9 KB |
| Time per request | ~8.3 ms | ~0.001 ms |
| Allocations per request | 104,026 | 17 |
Resolved history (100,000 rows, one unfiltered page). The new bounded page endpoint replaces loading the whole history at once. The legacy full fetch still exists as a compatibility fallback, but the UI no longer uses it.
| Full fetch (legacy) | Paged (new) | |
|---|---|---|
| Memory per request | ~750 MB | ~0.17 MB |
| Allocations per request | ~8,000,000 | ~2,100 |
WebSocket broadcasts and the snapshot codec allocate about the same per call as before. That work targeted bounded queues and stability under load, not per-call allocations.
Pod memory over several hours of real traffic, same axis scale, before and after:
Added
- Paged Resolved history. The Resolved tab loads on demand and shows the history as a flat list, newest event first, with a page browser at the top and bottom and a per-page selector (10 / 25 / 50 / 100, remembered in the browser). Search, label matchers, cluster and severity filters apply to the complete stored history, not just the visible page. The API gains additive
limit/offsetparameters onGET /api/v1/alerts/resolved. See Breaking Changes for the filter-syntax change. - Extend a running silence with one click. Every active or pending silence gets an Extend icon with a menu of durations. The picked duration is added to the silence's current end time and updates the same silence in place, so it can never shorten one.
- Configurable silence durations. One list now feeds the Fast-Silence menu, the Extend menu and the default duration of the silence form. Set the instance default with
JARVIS_SILENCE_DURATIONS(Helm:config.silenceDurations, comma-separated, up to 12 entries, each 1m to 365d) and let users override it in Settings > Silences. An invalid value makes the app refuse to start. Closes #219. - Copy a link to an alert. The detail panel has a "Copy link" button. The link opens that alert (Active or Resolved) for anyone with access and carries no search or filters, so it stays short. Copying falls back to a legacy method when the page is served over plain http.
- Login without losing your place. One app-wide login prompt opens on top of the current page and the interrupted action continues once you are signed in. Silence Preview no longer needs a login (it is asked at Create), and a write that fails with an expired session is replayed once after login. SSO runs in a popup so the page state survives, with a full-page redirect as fallback.
- Groups from your SSO login. With
JARVIS_OIDC_GROUPS_CLAIMset (for examplegroups, orcognito:groupson AWS Cognito), Jarvis stores the groups your identity provider reports at each login. Your name in the user menu opens an Account panel with your user, e-mail, role (Admin or User) and groups, plus a Copy button, so you can tell support which groups you are in when an alert seems to be missing. Changes in the identity provider apply at your next login. - Helm chart: upstream authentication. New
clusters[].auth.*values for an Alertmanager behind OAuth2 client credentials, bearer token, basic auth or custom headers, withexistingSecretsupport. Secrets go into the chart's Secret, never the ConfigMap. - Animated owl-mesh backdrop replaces the static icon in empty alert and silence views. It follows the theme, pauses when the tab is hidden and stays still under reduced-motion.
- Opt-in memory profiling.
JARVIS_PPROF_ADDRstarts a loopback-only debug server (heap, allocs and goroutine profiles only). Off by default; reach it viakubectl port-forward.
Fixed
- Lower memory use across history, WebSocket and API: resolved history is streamed row by row, WebSocket alert snapshots are cached and broadcast queues are bounded, follower snapshot rebuilds are coalesced, and every entry in the live resolved buffer now expires with its own episode after 20 minutes.
silencedByandinhibitedByare always arrays, nevernull. WebSocket reconnects use a jittered delay. - Browsers disconnected during alert bursts. A burst of five or more messages (several claims plus an alerts update during an incident) could disconnect every healthy tab at once, each followed by a full refetch. Snapshots are now coalesced to one per client and discrete events get their own deeper bound.
- Keyboard access. Fast-Silence and Extend popovers can be operated with Tab, Enter, Space and Escape; pressing Enter on a button inside a list row no longer also opens the row behind it; alert details open from a real button in the card; header popovers, cluster status and modal dialogs work from the keyboard. Popovers no longer claim ARIA menu roles they did not implement.
- Contrast. Focus rings, field edges, toggle tracks and icon-only controls now meet 3:1 contrast, and muted text no longer stacks opacity on its color.
- Regex hint in the Resolved view appears only while a regex filter is set and says in plain words what the server-side syntax lacks.
- Fast-Silence menu stays on screen after hover plus focus, and the offline state of the live connection is spelled out.
- Slow retention sweeps. Four retention-sweep queries (comments, released claims, silence events, orphan fingerprints) had no usable index and scanned whole tables; new indexes are created automatically on start. Verified on PostgreSQL with
EXPLAINin the commit. - Wrong pool size in the startup log on SQLite. The "database connected" line reported the configured PostgreSQL pool size instead of the single writer SQLite actually uses. Logging only, behavior unchanged.
Security
- Rate limits simplified. The per-IP limiters on
/setup, write endpoints,/poll, settings and/admin/usersare gone, since behind a proxy the client IP comes from a caller-controlled header.POST /auth/loginkeeps a single global limit (30 requests per minute, burst 10, per process, so per pod on multi-replica setups). One client can therefore exhaust the login bucket for everyone, and a 429 now shows "Too many login attempts" instead of "Invalid username or password". The deployment model (internal tool behind a VPN or auth proxy) is documented in the security guide.
Changed
- Administration. The Admin entry in the user menu is now called Administration.
- Refreshed look. The owl mark is permanently in the header, Inter is bundled as the UI face, the claimed-card accent moved to the right edge and is thicker, and colors, radii and status tokens now come from one shared definition.
- Documentation website. The docs are published as a website on GitHub Pages, restructured around reader intent, with new getting-started, deployment, upgrade, configuration, troubleshooting, FAQ and reverse-proxy guides, a landing page with a five-scene product tour, and regenerated screenshots.
docs/persistence.mdis nowdocs/postgres-ha.md. - Dependency and CI updates.
modernc.org/sqlite1.59.0, website build dependencies, frontend and GitHub Actions minor/patch bumps; backend, lint and E2E CI jobs now run in parallel.
Full diff: v1.12.0...v2.0.0
Container image
docker pull ghcr.io/kj187/jarvis:2.0.0-rc.2Digest: sha256:ada24e6b7621f1210da35af955f353734b594696c51ae8c038d1bf108d4a362c
Verify image signature (cosign)
cosign verify ghcr.io/kj187/jarvis@sha256:ada24e6b7621f1210da35af955f353734b594696c51ae8c038d1bf108d4a362c \
--certificate-identity-regexp="https://github.com/kj187/jarvis/.*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"Verify build provenance (GitHub attestation)
gh attestation verify oci://ghcr.io/kj187/jarvis:2.0.0-rc.2 --repo kj187/jarvisHelm chart
Release candidates don't publish a chart. To test this RC on Kubernetes, install the current chart with the RC image:
helm install jarvis oci://ghcr.io/kj187/charts/jarvis --version 2.0.0 --set image.tag=2.0.0-rc.2SBOM
The SPDX SBOM is attached to this release (sbom.spdx.json) together with its keyless signature bundle (sbom.spdx.json.sigstore.json), and also embedded in the image manifest (docker buildx imagetools inspect).
cosign verify-blob sbom.spdx.json \
--bundle sbom.spdx.json.sigstore.json \
--certificate-identity-regexp="https://github.com/kj187/jarvis/.*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"
