Skip to content

Changelog

Version v0.4.0-beta

v0.4.0-beta

This release contains breaking changes. Please read the upgrade notes before updating.

This update is a long time coming, and I apologize for not having been very active. This release is mostly maintenance and bug fixes: dependencies are up to date, several security issues are closed, and a lot of the jank around the decision table and the delete flow is gone.

I finished my bachelors degree in CS this summer, and started working full-time, so it has been busy. I’m going to put in more effort maintaining this project going forwards.

Highlights

Security

Server functions could be called without being logged in. Only page loads were protected, so anyone who could reach the dashboard could create users or delete decisions from LAPI. Every function that reads or changes data now requires a session. If your dashboard is reachable from the internet, update.

Note that TanStack Start uses RPC’s internally with endpoints generated from the function hash. While possible to exploit, it would be unlikely.

Sync that recovers on its own

A failed sync used to lose the decisions in that pull until the next restart, and a hung connection to LAPI could stop syncing entirely. Every LAPI request now has a timeout, a failed sync forces a full pull on the next poll, and decisions past their expiry are deactivated even if LAPI never reported them. When LAPI cannot be reached, a banner at the top of the dashboard says so, with the error and the time of the last successful sync.

Deleting decisions

Deleting a decision now updates the row in place and pushes the change to every open tab and to the Hosts page immediately. Decisions that expire between polls move to the expired section instead of disappearing until the next reload. Deleting a decision that already expired no longer fails.

Smaller things

  • The live connection sends heartbeats, so reverse proxies stop dropping it and the Live indicator stops flapping
  • Host “last seen” is no longer reset for every host on each restart
  • Navigating between pages no longer re-downloads the full decision list
  • The service worker was removed. It never ran, and would have served stale data if it had. The app is still installable from the browser
  • Decision history is now pruned by default, see below

⚠️ Breaking Changes

1. OIDC redirect URI has changed

The auth library moved its callback route. If you use SSO, update the redirect URI in your provider (Authentik, Keycloak, Okta, …) from

https://your-dashboard/api/auth/oauth2/callback/oidc

to

https://your-dashboard/api/auth/callback/oidc

Until you do, the provider rejects the login with a redirect URI error.

2. Reverse proxy headers

The dashboard now takes its public URL from the X-Forwarded-Host and X-Forwarded-Proto headers sent by your reverse proxy. Traefik, Caddy and nginx set these by default. If yours does not, or you see Invalid origin when logging in, set the public URL explicitly:

BETTER_AUTH_URL: https://your-dashboard.example.com

3. Decision history is pruned by default

The decision table used to grow without limit. It is now capped at 5000 rows. The oldest inactive decisions are removed first, together with hosts and alerts that nothing references anymore. Active decisions are never pruned. To keep everything, set:

DECISION_RETENTION_COUNT: "0"

The database migrates automatically this time. No volume reset is needed.


Reminders from v0.3.0-beta

The image is published as ghcr.io/ollioddi/crowdsec-local-dashboard. The old crowdsec-dashboard name stopped receiving builds in February, and the compose file in this repo pointed at it until this release. If you still pull the old name, you are running a stale image.

Pin a tagged version instead of latest while the project is in beta. latest is rebuilt on every merge to main and can change under you between releases.

image: ghcr.io/ollioddi/crowdsec-local-dashboard:0.4.0-beta

What’s next

In the coming weeks I’m going to overhaul the entire table implementation. Right now a live update forces the table to reset, which closes expanded rows and jumps the page. I intend to keep all table state in the URL and have it survive updates. No more expanded rows snapping shut when an event arrives.

I’ll also move to TanStack Table v9, mostly because v8 plays badly with the React Compiler, and put some effort into mobile. Thin scrollable rows are not great on a phone, and that view deserves its own layout.

Stay tuned!

Changes

Features

  • sync: Prune decision history by default (2f1b1be)
  • ci: Draft releases with generated notes and keep the changelog in sync (e2834aa)
  • ui: Show a banner when the CrowdSec sync fails or is not configured (89285de)

Bug fixes

  • auth: Migrate generic OAuth to better-auth 1.7 social sign-in (8a19409)
  • auth: Add user.image column required by better-auth 1.7 (9a3b23e)
  • auth: Require a session on every data server function (9118741)
  • lapi: Add request timeouts, share watcher logins, tolerate deleting a gone decision (4288049)
  • sync: Recover from failed pulls, keep host timestamps stable, batch count updates (282e791)
  • dev: Keep a single LAPI poller across SSR module reloads (2fe0bad)
  • decisions: Broadcast after delete and record the actual expiry (be44043)
  • decisions: Update the deleted row in place and keep expired rows (dc0f0cf)
  • sse: Send heartbeats, share the route handler, answer 401 when logged out (02aa520)
  • client: Stop redundant refetches and reconnects (425c8ab)
  • build: Remove the service worker plugin (9a81e91)
  • docker: Point the compose file at the renamed image (2cbdbe4)
  • auth: Trust reverse proxy headers when inferring the base URL (3a31ded)

Documentation

  • Describe the release process (d2f9940)

Dependencies

  • deps: Bump all dependencies to latest majors (a277a9f)
  • deps-dev: Bump oxc-transform-react in the minor-and-patch group (03cea1b)
  • deps: Bump the actions group with 8 updates (55a3da4)

Full diff: v0.3.0-beta…v0.4.0-beta