All releases
v2.28.0
March 22, 2026
Minor release
In this release
New5
Fixed13
Total18

Bug Fixes & Stability

This release includes 1 update focused on bug fixes & stability.

New Features

5

Add compound query API and pre-built insight endpoints (#...

Adds flexible compound diagnostics query endpoint (POST /query) with allowlisted dimensions/metrics for safe SQL generation, 6 pre-built insight endpoints (cost-efficiency, escalation-waste, user-segments, playbook-effectiveness, failure-trends, antibot-landscape), and CSV export. JOINs across scrap

Add per-user COGS and tier distribution analytics

Three new admin endpoints under /api/v1/admin/economics/: - GET /users/top-cost: users ranked by proxy cost with margin - GET /users/{user_id}/cogs: per-user COGS with tier breakdown and top domains - GET /users/margin-distribution: histogram of margin percentages across users

Add monitoring and Grafana monitoring stack

Adds production sidecar monitoring: monitoring scrapes API /metrics and load balancer metrics, Grafana auto-provisions 3 dashboards (Platform Health, Proxy Economics, Domain Intelligence). Both services bound to localhost for secure connection access. Includes SOPS credentials and ENV_MAPPING wiring

Bridge proxy COGS data into scrape_diagnostics

Add bandwidth_bytes and proxy_cost_microcents to the diagnostics accumulator in unified_consumer and write them through to both scrape_diagnostics (per-job) and domain_analytics (per-domain aggregate) in _sync_diagnostics_to_postgres. The columns were added in migration 0042 but never populated.

Add time-bucketed domain analytics for trend analysis (#2...

Adds domain_analytics_daily table with daily UPSERT (same pattern as domain_analytics), two new admin API endpoints for domain trends and trending failure detection with period-over-period comparison.

Bug Fixes

13

Correct ARGV comment order in Lua script header

Correct ARGV comment order in Lua script header.

Restore Redis atomicity in create_job via Lua script (#...

The binary/text client split (commit 7363e6e5) broke the atomic pipeline that previously batched SET+ZADD. A crash between the two separate awaits could leave a phantom job — credits debited, status key written, but job never enqueued for the worker. Replace the two-await pattern with a Lua script

Re-add SessionUser auth to deprecated endpoints

Re-add SessionUser auth to deprecated endpoints .

Reject Resend webhooks when signing secret is unset

Return 503 instead of silently accepting unverified payloads when RESEND_WEBHOOK_SECRET is not configured, preventing forged bounce events from flagging user emails as undeliverable.

Replace non-existent plan_name column with billing_produc...

Replace non-existent plan_name column with billing_produc....

Add Redis pool health check to queue admission control (#...

check_queue_admission now pings Redis with a 500ms timeout before checking queue depth. If the pool is unhealthy, admission is denied with a 503 and credits are refunded. This prevents jobs from being accepted when pool exhaustion would block all processing.

Debounce Run button and abort in-flight requests on navig...

- Add synchronous ref-based guard to prevent double-click race conditions - Add AbortController to cancel in-flight scrape fetch/polling on unmount or new request - Add 2-second post-completion cooldown with visual "Wait..." state on Run button - Pass isCooldown prop to DashboardConfigurationPanel f

Add per-API-key rate limiting on scrape and crawl endpoin...

Wires existing balance-tier rate limits (30/100/300/500 RPM) into the /api/v1/scrape and /api/v1/crawl handlers via RedisRateLimiter sliding window. Returns 429 with Retry-After header when exceeded. Fails open on Redis errors to avoid blocking all scrapes during outages.

Graceful degradation on T4 browser timeout and crash

When T4 browser times out or crashes, extract partial DOM content instead of returning nothing. Flush BlackBox recorder on failure paths so TrafficAnalyzer can learn from failed attempts. Preserve detected challenge type through exception boundaries so diagnostics capture what protection was identif

Thread CSR detection to T4 hydration-aware content wait (...

When content_validator detects a CSR shell at T1/T2/T3 (React/Vue/Angular empty div with JS bundles), the is_csr_shell flag was logged but never passed to the browser tier. T4 used generic domcontentloaded + selector waiting, which returns before JS hydration completes on SPAs. Changes: - Add wait_

Filter ReactNode FAQ answers from JSON-LD schema

pricingFaqItems has mixed string and ReactNode answers. generateFAQSchema expects string answers for JSON-LD. Filter to string-only items.

Replace f-string SQL interpolation with safe ORDER BY bui...

Adds _safe_order_by that builds ORDER BY clauses using validated CASE expressions instead of directly interpolating column names via f-strings. Applied to margin_by_domain and users_top_cost endpoints.

Inline FAQ schema data and update homepage title

- Inline string-only FAQ items for JSON-LD schema instead of importing from client component (fixes SSG prerender crash) - Remove "AlterLab —" prefix from homepage h1