Skip to content

Releases: logtide-dev/logtide

v0.9.3

18 Apr 15:45
03f02a7

Choose a tag to compare

Added

  • Traces live tail (SSE): new GET /api/v1/traces/stream Server-Sent Events endpoint polls the traces table once a second and emits new trace_ids as they appear, filtered by projectId, service (CSV) and error. On the frontend the traces page now has a "Live tail" switch + rows-limit selector (50/100/200/500/1000, persisted in localStorage) in the filter bar; incoming traces are prepended and the list is capped at the chosen limit. The tracesEventSource is torn down on onDestroy and on every filter-key change
  • Traces row click-to-expand with inline span list: clicking a trace row now toggles an inline panel below it that fetches GET /api/v1/traces/:traceId/spans once (cached per trace_id for the session) and shows a compact table with Service / Operation / Kind / Duration / Status for each span. The "View" action still opens the full trace detail page
  • Traces keyboard shortcuts: / focuses the Trace ID input, r refreshes, j/k move the selection down/up with scroll-into-view, enter expands/collapses the selected trace. Registered via shortcutsStore under scope traces and unregistered on destroy
  • Traces export (JSON / CSV): new Export popover in the filter bar produces a client-side download of the current list (disabled during live tail or when the list is empty). CSV covers start_time / service / operation / duration_ms / span_count / error / trace_id
  • Monitoring entry in the command palette: cmd+k now lists Monitoring under Navigation (icon + g o shortcut hint), matching the sidebar's Detect group so the page is reachable without leaving the keyboard
  • trace_volume dashboard panel: new panel type that plots span count over time (optional errors line), configurable time range (1h/6h/24h/7d) and optional service filter. Mirrors the shape of the existing logs "Log Volume" panel but for OTLP traces. Reads the spans_hourly_stats / spans_daily_stats continuous aggregate first, falling back to the raw spans hypertable with date_trunc only when the cagg is empty for the window (covers the end_offset=1h refresh lag on freshly-ingested data). TimescaleDB-only
  • activity_overview dashboard panel: unified multi-series timeline combining logs, log errors, spans, span errors, detection events and alert triggers on a common bucket grid (hourly for ≤24h, daily for 7d/30d). Each series is individually toggleable in the config form. Per-source queries run in parallel and prefer the logs_*_stats, spans_*_stats, detection_events_*_stats continuous aggregates for cost; if a cagg returns zero rows for the window the fetcher falls back to the raw hypertable (logs, spans, detection_events) for that source only, so the panel stays correct on freshly-ingested data without running a raw scan in the hot path. Alerts always come from alert_history + alert_rules (no cagg exists for it)

Changed

  • Monitoring page reorganized into tabs: what was a single 1000-line scroll with three unrelated sections (Monitors, Incidents, Maintenance) plus a status-page config block wedged at the top is now a Tabs.Root with four tabs — Monitors, Incidents, Maintenance, Status page — each with its own header, its own primary CTA, and its own empty state. The Monitors tab gains a summary card grid (Total / Up / Down / Paused, each card clickable to filter the list by that status), a search input that matches name / target / type, and a "Clear filters" shortcut. Every row now has a one-click Pause/Resume button (previously buried in the edit form). Status-page visibility, password, slug and embed-badge config moved into the Status page tab. No changes to underlying API calls or permissions
  • Admin dashboard sidebar collapses on mobile: the 256px admin sidebar was previously always visible, taking up a huge chunk of the viewport on phones. It now hides below lg: and opens as a fixed drawer triggered by a hamburger in a new mobile-only sticky header that also shows the current section name (Dashboard / Users / Organizations / etc). The drawer has a backdrop, is dismissed on nav-click or backdrop-click, and locks body scroll while open. Desktop behaviour unchanged
  • Dashboard footer wraps properly on mobile: was a single justify-between row that squeezed "LogTide / Alpha v0.9.2" against "© 2026 LogTide · Documentation · GitHub" on narrow screens. Now stacks the brand block over the links block below sm: (each block keeps its own horizontal flow with flex-wrap gap-x-4 gap-y-1), and "Documentation" shortens to "Docs" under sm: to fit on a single line. Container padding also drops from px-6 py-4 to px-4 py-3 on mobile
  • User settings promoted from a dialog to a full page at /dashboard/account. The cramped scrollable UserSettingsDialog modal (profile + password + tutorial restart + danger zone stacked inside a 32rem-wide overlay) is now a proper route using a max-w-3xl container with each concern split into its own rounded-lg border bg-card section (Profile / Change password / Onboarding / Danger zone). The dropdown menu item still says "User Settings" but now navigates instead of opening an overlay. UserSettingsDialog.svelte is removed. The delete-account confirmation remains a nested AlertDialog since that one is genuinely destructive and modal-appropriate
  • Public status page polished for mobile and clarity: outer container shrinks from px-4 py-10 to px-3 py-6 on phones (only sm: and up gets the roomy desktop padding). The 45-day uptime bars drop their min-w-[6px] floor to 4px on mobile so the whole row fits comfortably on 320px viewports. The footer ("Last updated …" and "Powered by LogTide") grows from text-[10px] to text-xs with a taller top margin, and "LogTide" is now styled as text-primary font-medium hover:underline so the link affordance is obvious (previously only a hover recolor). Incident update timestamps/status labels also bumped from text-[10px] to text-xs. The monitor-type badge (HTTP/TCP/etc.) hides below sm to free up the row for the name + uptime%. The password input switches from a fixed w-64 to w-full max-w-xs so it never overflows narrow phones
  • Monitoring forms moved into modal dialogs: the previous inline create/edit monitor form, the "New incident" form, the "Post incident update" form and the "Schedule maintenance" form were expanding/collapsing sections that shifted the whole page layout when opened. They now open as centered Dialog modals with max-h-[90vh] overflow-y-auto so tall forms (monitor create) scroll independently of the page. The per-row "Post update" Dialog is a single instance driven by a showUpdateForm id + an updatingIncident derived lookup, replacing the one-per-row inline expand. Form state, validations and submit handlers are untouched
  • Traces filter UI reworked into the same two-row pill bar as the search page: row 1 keeps Time Range (TimeRangePicker in a popover), the Live tail switch, the List/Map view toggle and the Export menu. Row 2 exposes every filter as its own always-visible pill — Project (single-select), Services (multi-select), Status (tri-state: all / errors / ok), Duration range (min/max ms inputs), Trace ID (direct-navigate to the trace detail page). Pills switch to the secondary variant when a non-default value is set; "Clear all" appears when any filter is active. Mobile popovers use w-[Xpx] max-w-[90vw] so they never overflow the viewport. handleTimeRangeChange syncs picker state back into timeRangeType / customFromTime / customToTime so the trigger label survives popover close/remount
  • Traces backend accepts multi-value projectId and service (CSV on the query string) and two new minDurationMs / maxDurationMs bounds on GET /api/v1/traces. The cross-project access check loops over every project in the list. tracesService.listTraces widens its input types to string | string[] and forwards to reservoir.queryTraces, which already supported these filters
  • Search page paginator now uses the same ellipsis/windowed control as the traces page: shows up to four numbered buttons plus sentinel on each side when totalPages > 7. Falls back to the previous "Page N" label when the backend doesn't return a total (storage engines without total in the query response). "Previous" / "Next" labels hide on <sm screens so only the chevron shows on phones
  • Empty states wait for the first load to complete before rendering on the search, traces, errors and security-incidents pages. Each page now tracks a hasLoadedOnce flag flipped in the finally branch of its loader; until the first fetch resolves (success or failure) the page shows its skeleton instead of the "No Logs Yet / Start sending logs from your applications" onboarding card. Prevents the flash where an org that already has data showed the onboarding CTA during the initial render between mount and the first query. When a fetch legitimately returns zero rows, the page also distinguishes "nothing matches these filters" (with a Clear-filters shortcut) from "project has no data at all" (the full onboarding)
  • Search page filter UI reworked into a two-row pill bar: row 1 keeps Search + mode, a Time Range popover trigger, Live Tail and Export inline. Row 2 exposes every filter as its own always-visible pill (Projects, Services, Hostnames, Levels, Trace ID, Session ID, Metadata) — each pill displays the current value (e.g. "Service: api-gateway", "Levels: 2") and opens its own popover for editing. Pills switch to the secondary variant when a non-default value is set so active filters are visible at a glance. A "Clear all" link appears at the end of the row when any filter is active. Popover widths use w-[min(Xpx,calc(100vw-1rem))] so they never overflow the viewport on phones. No semantic change to the underlying filter behavior (state, query params, live-tail flo...
Read more

v0.9.2

16 Apr 19:16
784f9cf

Choose a tag to compare

Added

  • Generic metadata filters in log search and alert rules: the search page and alert rule dialogs now expose a "Metadata filters" section backed by a new MetadataFilterBuilder component. Supported operators: equals, not_equals, in, not_in, exists, not_exists, contains. Filters are applied server-side via a GIN-indexed JSONB query builder in reservoir; alert evaluation also runs the same matcher in-process so rules can fire only when a specific metadata field matches
  • Configurable metadata columns in log table view: users can add arbitrary metadata.* keys as extra columns in the search results table via a "Columns" picker. The selected column set is persisted per project in localStorage so it survives page reloads
  • Create user from admin panel (issue #198): admins can now provision new accounts directly from Admin → User Management via a "Create User" button, without having to temporarily re-enable public signup. Opens a dialog to set email, name, password and optional admin role. Backed by a new POST /api/v1/admin/users endpoint that bypasses the auth.signup_enabled gate and logs a create_user entry to the audit log
  • Set a custom dashboard as default from the UI: the dashboard switcher now shows a clickable star next to each org-wide, non-personal dashboard; clicking it promotes that dashboard to be the org's default. Backed by a new POST /api/v1/custom-dashboards/:id/set-default endpoint that atomically unsets the previous default and sets the new one in a single transaction, respecting the existing partial unique index. Personal and project-scoped dashboards are rejected with a 400
  • Editable project slug from monitoring page: the status-page settings card in /dashboard/monitoring now exposes a "Public URL slug" input that lets the user rename a project's slug, with inline validation against a shared format check (lowercase alphanumeric + hyphens, 2-50 chars), reserved-word list (api, admin, dashboard, status, auth, login, signup, logout, _app, health), and per-org uniqueness. Conflicts surface as 409 with a friendly inline error; race conditions are caught at the DB layer via the new composite unique index
  • Editable organization slug from settings: /dashboard/settings/general slug field is no longer read-only; owners can rename the org slug with the same validation rules and global uniqueness, with a warning that any existing status-page links and embed badges will break

Changed

  • Public status page URL is now scoped under the organization (BREAKING): page and badge URLs changed from /status/:projectSlug to /status/:orgSlug/:projectSlug. Affects the public web page, /api/v1/status/:orgSlug/:projectSlug/badge.svg, and /api/v1/status/:orgSlug/:projectSlug/badge.json. No redirect from the old URLs. Anyone embedding the badge SVG/JSON or sharing a status-page link must update the URL to include the org slug. Migration 040 simultaneously moves project-slug uniqueness from global back to per-org, so two different organizations can now both have a project named frontend without auto-suffixing

Fixed

  • Security dashboard crashed with "Cannot read properties of null (reading 'toLowerCase')" (issue #200): root cause was two parallel unnest(mitre_techniques) / unnest(mitre_tactics) calls in the same SELECT list of SiemDashboardService.getMitreHeatmap. PostgreSQL evaluates sibling set-returning functions in lockstep and NULL-pads the shorter array, so any detection event whose tactic and technique arrays had different lengths produced heatmap rows with a null tactic, which then crashed MitreHeatmap.abbreviateTactic on the frontend. The heatmap query now unnests techniques only and resolves each one to its canonical tactic via the shared MITRE_TECHNIQUES map (with sub-technique to parent fallback), eliminating the malformed pairs at the source. Frontend MitreHeatmap also filters cells with null tactic/technique and DetectionEventsList.getLogLevelClass defensively handles a null level as belt-and-suspenders

Security

  • Bump fastify to 5.8.5 (GHSA-247c-9743-5963, CVE-2026-33806, HIGH): body schema validation could be bypassed by prepending a single space to the Content-Type header. Parser and validator disagreed on how to trim the header, so the body was still parsed but the schema lookup returned no validator and validation was skipped entirely. Upgraded from ^5.8.3 to ^5.8.5
  • Bump @sveltejs/kit to 2.57.1 (GHSA-2crg-3p73-43xp, CVE-2026-40073, HIGH): BODY_SIZE_LIMIT could be bypassed under certain conditions in adapter-node. Tightened the pnpm override from >=2.53.3 to >=2.57.1
  • Bump @sveltejs/kit to 2.57.1 (GHSA-3f6h-2hrp-w5wx, CVE-2026-40074, MEDIUM): calling redirect inside the handle hook with a location containing characters invalid for an HTTP header threw an unhandled TypeError, enabling DoS if the location included unsanitized user input

New Contributors

Full Changelog: v0.9.1...v0.9.2

v0.9.1

13 Apr 15:46
a6642aa

Choose a tag to compare

Fixed

  • Identifier pattern update/delete failed with "Organization ID is required" (issue #193): PUT and DELETE routes required organizationId as an explicit query param or API key context, but session-based auth never sets that field. Now falls back to the user's first organization, consistent with GET and POST
  • Project rename failed with "Expected string, received null" (issue #195): updateProjectSchema used z.string().optional() which rejects null, but projects without a description send null from the DB. Changed to z.string().nullable().optional() and updated UpdateProjectInput accordingly
  • Pipeline create/preview/import returned generic "Validation error" (issues #193, #194): POST routes expected organizationId in the request body but the frontend sends it as a query param. Routes now merge the query param into the body before Zod validation. Frontend error messages now surface the first validation detail instead of the generic label
  • Invitation accept race condition: wrapping the membership check + insert in a transaction caused the accepted_at update to roll back when throwing "already a member". Split the early-exit path out of the transaction and added 23505 unique-constraint handling for true concurrent accepts
  • SSE live tail duplicate sends: latestLog picked the oldest entry from a DESC-sorted array instead of the newest, causing every poll to re-fetch and re-send all logs since the oldest timestamp. Replaced with a defensive max-time computation
  • Sigma sync corrupted alert_rule_id FK: fallback alertRuleId || existing.id wrote the sigma rule's own PK into the alert_rules FK column when no alert was auto-created. Now omits the column entirely unless a new alert rule was just created
  • Exception log viewer returned empty results for org-wide error groups: getLogsForErrorGroup passed an empty string as projectId to reservoir when no project filter was set. Now groups log IDs by their exception's project_id and issues one getByIds call per project
  • ReDoS in HTTP monitor body assertion: user-supplied regex pattern was compiled with only a 256-char length limit, no catastrophic-backtracking check. Added safe-regex2 validation and a compile-error catch
  • OTLP int64 precision loss: parseInt() silently truncated int64 attribute values exceeding Number.MAX_SAFE_INTEGER. Unsafe values are now kept as strings in metadata
  • PII salt race condition fallthrough: if two workers raced on the first hash for an org, the loser could return an unpersisted local salt when the retry read failed, permanently desynchronizing PII hashes. Now scopes the catch to 23505 and throws on unexpected errors
  • Monitor status fallthrough on missing row: processCheckResult silently skipped the entire state machine (no status update, no notifications) when monitor_status was undefined. Now re-reads from DB or creates a default row before proceeding
  • Test isolation: auth mode pollution across test files: system_settings was never reset between tests, so any test setting auth.mode='none' caused 12 unrelated "401 without auth" assertions to return 503. Added cleanup + cache invalidation to global beforeEach
  • Sigma detection tests used sigma_id after code migrated to id: fixture data still passed rule.sigma_id as sigmaRuleId, which would not match the new .where('id', '=', ...) queries
  • translateDelete dropped level filter in reservoir: pushFilter return value was discarded for the level condition, corrupting $N parameter slots when both service and level filters were present
  • SQL injection in querySpans via sortBy/sortOrder: user-controlled strings were interpolated directly into raw SQL in both TimescaleDB and ClickHouse engines. Added explicit column/direction allowlists
  • ingestSpans hardcoded ::uuid[] for project_id: ignored the projectIdType engine option, breaking text-based project IDs with a Postgres cast error
  • localStorage SSR crash in organization store: 7 direct localStorage calls without a browser guard would throw ReferenceError during server-side rendering. Added browser check on all accesses
  • Invite token not URL-encoded in redirect: goto(/login?redirect=/invite/${token}) corrupted the redirect path for tokens containing +, =, or /. Now wraps in encodeURIComponent
  • ruleId not URL-encoded in security dashboard navigation: inconsistent with serviceName and technique which already used encodeURIComponent
  • Missing UUID validation on monitoring route params: all /:id handlers passed request.params.id directly to DB queries without format validation. Added Zod .uuid() parsing on every route
  • Negative limit/days in monitoring routes: Number("-1") || 50 evaluates to -1 (truthy), passing a negative value to LIMIT. Replaced with a parsePositiveInt guard that clamps to [1, max]
  • Missing UUID validation on status-incident route params: same issue as monitoring - :id params were used unvalidated in DB queries
  • SIEM comment body has no max length: z.string().min(1) with no upper bound allowed arbitrarily large comment payloads. Added .max(10000)
  • Notifications and alerts limit/offset NaN passthrough: parseInt("abc") returned NaN, which was passed to Kysely .limit(NaN). Added safe integer parsing with fallback and max cap
  • Correlation referenceTime accepted invalid date strings: schema validated only {type: 'string'}, so new Date("garbage") flowed into Kysely WHERE clauses as Invalid Date. Added format: 'date-time' and a defensive 400 response
  • Log pipeline comment contradicted jsonb merge direction: code comment said "do NOT overwrite existing" but the in-progress fix had flipped the jsonb || operand order so pipeline fields now win. Updated comment to match actual behavior

Full Changelog: v0.9.0...v0.9.1

v0.9.0

11 Apr 19:58
4586177

Choose a tag to compare

🌊 Logtide 0.9.0

This release brings three long-requested pillars to Logtide: a fully customizable dashboard system, proactive infrastructure monitoring with public status pages, and a structured log parsing pipeline.

✨ What's New

📊 Custom Dashboards (#151)

  • Your layout, your rules: replace the previous fixed dashboard with as many team-specific dashboards as you need, each composed of freely arranged and resized panels.
  • 9 panel types: time series, single stat, top-N table, live log stream, alert status, metric chart, metric stat (OTLP with avg/sum/min/max/count/last/p50/p95/p99), trace latency (p50/p95/p99), detection events, and monitor status - every data source in Logtide is now representable.
  • Drag-to-reorder and drag-to-resize: panels snap to a responsive 12-column grid, collapsing gracefully to 6 columns on tablet and 1 column on mobile.
  • Inline edit mode: toggle editing directly on the dashboard - no separate page, no lost context. Changes are held in a local snapshot and discarded on Cancel.
  • Dashboard switcher: a header dropdown lets you switch, create, delete, import, and export dashboards. The default dashboard is protected from deletion.
  • YAML import/export: round-trip your dashboards through version-controlled YAML files alongside your infrastructure code.
  • Versioned schema with migrations: schema_version: 1 ships with a migration framework in @logtide/shared so future schema changes are applied automatically on read.
  • Zero visual regression for existing users: the auto-created Default dashboard replicates the previous fixed layout (4 stat cards + log volume + top services + top error messages) exactly.
  • Cross-org isolation: every panel data fetch verifies that config.projectId belongs to the requesting org, blocking data leaks via crafted YAML imports or stale references.

🖥️ Service Health Monitoring and Status Pages (#152)

  • 3 monitor types: HTTP/HTTPS (configurable method, expected status, custom headers, body assertion via contains or regex), TCP ping, and heartbeat (alert when no ping arrives within the grace window).
  • Uptime Kuma-inspired status page (/status/:projectSlug): 45-day heartbeat bar grid, per-monitor uptime badge, overall status banner, light/dark mode toggle. Visibility is configurable per project - disabled (default), public, password-protected, or org-members-only.
  • Auto-incident creation: when the consecutive failure threshold is crossed, a SIEM incident is created with source: 'monitor', linked via monitor_id, and dispatched through existing email/webhook notification channels.
  • Scheduled maintenances: define maintenance windows with start/end times; active windows suppress incident creation and display a banner on the status page.
  • Manual status incidents: publish incident communications (Investigating → Identified → Monitoring → Resolved) with a full update timeline, independent from SIEM incidents.
  • Heartbeat endpoint: POST /api/v1/monitors/:id/heartbeat accepts both API key and session auth, rate-limited to 600 req/min.
  • TimescaleDB storage: monitor_results hypertable with 7-day compression, 30-day retention, and a monitor_uptime_daily continuous aggregate refreshed hourly.
  • Monitoring dashboard (/dashboard/monitoring): monitor list with project selector, create/edit/delete forms, detail page with uptime chart and recent checks, and a one-click heartbeat URL copy.

🔩 Log Parsing and Enrichment Pipelines (#153)

  • 5 built-in parsers: nginx (combined log format), apache, syslog (RFC 3164 and RFC 5424), logfmt, and JSON message body.
  • Custom grok patterns: %{PATTERN:field} and %{PATTERN:field:type} syntax with 22 built-in named patterns and optional type coercion (:int, :float).
  • GeoIP enrichment: extract country, city, coordinates, timezone, and ISP data from any IP field using the embedded MaxMind GeoLite2 database.
  • Zero ingestion latency impact: pipelines run as BullMQ background jobs after ingestion acknowledgment.
  • Project-scoped vs org-wide: pipelines can target a specific project or apply across the entire organization; project-specific pipelines take priority.
  • Pipeline preview: test any combination of steps against a sample log message and inspect per-step extracted fields and the final merged result before saving.
  • YAML import/export: import pipeline definitions from YAML; re-importing the same pipeline for the same scope performs an upsert.
  • In-memory cache: getForProject caches the resolved pipeline per project for 5 minutes, automatically invalidated on create/update/delete.
  • Settings UI (/dashboard/settings/pipelines): list, enable/disable toggle, create, edit, delete, and a live step builder for adding, reordering, and configuring parser, grok, and geoip steps.

🔧 Improvements

🧭 Navigation

  • Monitoring section: added to the sidebar under "Detect", alongside Alerts and Security.
  • Dashboard switcher: replaces the previous single fixed entry point with a full dropdown for managing multiple dashboards.

⚡ Performance and Correctness

  • Batch panel data endpoint (POST /:id/panels/data): single round-trip fetches all panel data via Promise.allSettled - individual panel errors do not fail the dashboard.
  • BullMQ monitor polling: worker checks all due monitors every 30 seconds in batches of 20 concurrent checks via Promise.allSettled.
  • Atomic incident dedup: consecutive-failure state machine uses a WHERE incident_id IS NULL guard to prevent duplicate incidents under concurrent checks.

🐛 Bug Fixes

  • Status page slug collision: getPublicStatus now filters by status_page_public flag instead of returning the first project matching the slug, preventing cross-org data leaks.
  • createMonitor atomicity: monitor and monitor_status inserts are now wrapped in db.transaction() to prevent orphaned rows on partial failure.
  • Redundant DB read eliminated: processCheckResult now receives status data from the already-fetched monitor object instead of issuing a second SELECT.
  • Target validation on update: the PUT endpoint validates target format against monitor type (HTTP must start with http:///https://, TCP must contain :).
  • $derived.by fix: monitor detail page uptime calculation now uses $derived.by() instead of $derived(() => ...) for correct Svelte 5 reactivity.
  • @const placement: replaced invalid {@const} inside <div> elements with {#if}/{:else} blocks for Svelte 5 compatibility.
  • uptimePct type coercion: Postgres ROUND() returns numeric as string - status page now coerces to number before calling .toFixed().
  • Default failureThreshold aligned: frontend form default corrected from 3 to 2 to match the backend default.
  • Pipeline edit page redirect: navigates back to the pipeline list when the active organization is switched, preventing stale-ID errors.
  • mapMonitor typed: replaced any parameter with a proper MonitorWithStatusRow interface for compile-time safety.
  • Org membership check optimized: monitoring routes now use a single SELECT WHERE user_id AND organization_id query instead of fetching all user orgs and filtering in JS.

Full Changelog: v0.8.7...v0.9.0

v0.9.0-rc2

09 Apr 00:37

Choose a tag to compare

v0.9.0-rc2 Pre-release
Pre-release

Full Changelog: v0.9.0-rc1...v0.9.0-rc2

v0.8.7

07 Apr 13:11
a57d5e9

Choose a tag to compare

Fixed

  • Quick Start cURL example failed validation: the empty-state code snippet sent {logs: [{level, service, message}]} without a time field, but logSchema required it for the standard ingestion path (only the array-format path ran normalizeLogData). The schema now defaults time to the current ISO string when missing, so copy-paste examples and minimal payloads validate without requiring users to inject a timestamp.
  • Noisy Sigma worker logs: [SigmaDetection] No matches found was emitted at info level on every batch with no detections, flooding worker output in normal operation. The line is now gated behind DEBUG_SIGMA=true so it only appears when explicitly opted in.
  • No admin user when INITIAL_ADMIN_* not set (#188): on a fresh instance without INITIAL_ADMIN_EMAIL/INITIAL_ADMIN_PASSWORD, no usable admin existed and admin settings were unreachable. The bootstrap no longer creates a system fallback user; instead, the first user to register (via /register or external auth provider) is automatically promoted to admin if no admin exists yet.

Full Changelog: v0.8.6...v0.8.7

v0.9.0-rc1

06 Apr 15:41
5d543a3

Choose a tag to compare

v0.9.0-rc1 Pre-release
Pre-release

What's Changed

  • Feature/151 feature custom dashboards with configurable panels by @Polliog in #185

Full Changelog: v0.9.0-beta.2...v0.9.0-rc1

v0.9.0-beta.2

05 Apr 23:46

Choose a tag to compare

v0.9.0-beta.2 Pre-release
Pre-release

What's Changed

  • chore(deps): bump fastify from 5.8.1 to 5.8.3 in the npm_and_yarn group across 1 directory by @dependabot[bot] in #181
  • chore(deps): bump the npm_and_yarn group across 2 directories with 1 update by @dependabot[bot] in #182
  • 0.8.5 by @Polliog in #183
  • 0.8.6 by @Polliog in #184

Full Changelog: v0.9.0-beta.1...v0.9.0-beta.2

v0.8.6

30 Mar 12:30
00d186f

Choose a tag to compare

Fixed

  • ClickHouse traces/metrics data-availability always empty: queryTraces and queryMetrics passed raw 0 for epoch dates as DateTime64(3) parameter, which ClickHouse can't parse; now uses the same toDateTime64() clamp used by log queries
  • Stale session after volume reset: dashboard only checked localStorage for a token without validating it against the backend; now calls /auth/me on load and auto-logs out if the session is invalid

Full Changelog: v0.8.5...v0.8.6

v0.8.5

28 Mar 09:59
5139b0c

Choose a tag to compare

Security

  • Cross-org isolation fix in SIEM: linkDetectionEventsToIncident now scopes detection events to the requesting organization, preventing cross-tenant data corruption via crafted API calls
  • Cross-org auth bypass in pattern routes: PUT and DELETE handlers for correlation patterns now verify organization membership before mutating data (same check GET/POST already had)
  • SSRF protection for legacy webhook path: the alert-notification job's direct fetch() call now validates URLs against private/internal IP ranges, matching the WebhookProvider safeguard
  • Disabled user login blocked: POST /login now checks the disabled flag before creating a session, preventing disabled accounts from obtaining tokens
  • Expired invitation info leak: getInvitationByToken now filters on expires_at > NOW(), preventing enumeration of expired invitation details

Fixed

  • SIEM dashboard timeline crash: time_bucket() call was missing ::interval cast on the parameterized bucket width, causing a PostgreSQL type error that broke the timeline widget for all users
  • SSE real-time events broken: SIEM store and incident detail page read auth token from localStorage('session_token') (wrong key), so the SSE connection never authenticated; now uses getAuthToken() from the shared auth utility
  • SSE log stream duplicate emission: when multiple logs shared the same timestamp, the inclusive from bound caused them to be re-sent on every poll tick; stream now tracks sent log IDs to deduplicate
  • Incident severity auto-grouping wrong: MAX(severity) used PostgreSQL alphabetical ordering (medium > critical), producing incorrect severity on auto-grouped incidents; now uses ordinal ranking
  • Sigma notification failures silent: notification job payload was missing organization_id and project_id, and markAsNotified was called with null historyId; both now handled correctly
  • Incidents pagination total always zero: loadIncidents in the SIEM store never wrote response.total to incidentsTotal
  • Memory leaks on navigation: 20+ Svelte components called authStore.subscribe() without cleanup; all now store the unsubscribe function and call it in onDestroy
  • offset=0 silently dropped: API client functions used if (filters.offset) which is falsy for zero, so page-1 requests never sent the offset parameter; changed to if (filters.offset != null)
  • Search debounce timer leak: searchDebounceTimer was not cleared in onDestroy, causing post-unmount API calls when navigating away mid-search
  • verifyProjectAccess double call: when projectId is an array, the first element was verified twice (once before the loop, once inside it); consolidated into a single loop
  • updateIncident silent field skip: title, severity, and status used truthy checks (&&) instead of !== undefined, inconsistent with description and assigneeId
  • Webhook error messages empty: response.statusText is empty for HTTP/2; error now reads the response body for useful detail
  • Retention job crash on empty orgs: Math.max(...[]) returns -Infinity, cascading to an Invalid Date in the drop_chunks call; early return added when no organizations exist
  • escapeHtml DOM leak: PDF export's escapeHtml created orphaned DOM nodes in the parent document; replaced with pure string replacement
  • Webhook headers validation missing: CreateChannelDialog silently swallowed invalid JSON in the custom headers field; now validates on submit
  • getIncidentDetections no org scope: query now accepts optional organizationId for defense-in-depth filtering
  • Stale shared package types: dist contained outdated Project and Incident interfaces with phantom fields (slug, statusPageVisibility, source, monitorId); rebuilt from source

Changed

  • Docker config sync: docker-compose.build.yml now matches docker-compose.yml with all environment variables (MongoDB, TRUST_PROXY, FRONTEND_URL, INTERNAL_DSN, DOCKER_CONTAINER), MongoDB service, and fluent-bit-metrics service
  • NODE_ENV for backend: production docker-compose.yml now sets NODE_ENV: production on the backend service (worker and frontend already had it)
  • docker/.env.example: added STORAGE_ENGINE, ClickHouse, and MongoDB configuration sections

Dependencies

  • picomatch 4.0.3 → 4.0.4 (fix ReDoS via extglob quantifiers + POSIX character class method injection)
  • brace-expansion 5.0.2 → 5.0.5 (fix zero-step sequence DoS)
  • fast-xml-parser 5.5.6 → 5.5.9 (fix entity expansion limits bypass)
  • fastify bumped via dependabot
  • kysely bumped via dependabot

Full Changelog: v0.8.4...v0.8.5