> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenightowl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> New features, improvements, and fixes across the NightOwl agent, API, and dashboard.

What's new in NightOwl. Each entry covers the agent package, the backend API, and the dashboard UI together, so you can see everything that changed on a given date in one place.

<Note>
  Entries tagged **Agent** ship as a Composer package, so you pick up those changes by running `composer update nightowl/agent`. Some agent releases need a follow-up command (a migration or a new env var). Each Agent entry below links to the [agent changelog](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md), which lists the exact upgrade steps per version. API and dashboard changes are deployed for you on the hosted platform.
</Note>

<Update label="July 31, 2026" tags={["Agent", "API", "Performance"]}>
  **A summary table could stop updating silently, and wide-range charts time out**

  If your 7-day or 30-day charts have started timing out, or the Peak Concurrency chart has flat-lined, this release is the fix.

  * **A summary table could stop being written and never say so.** The per-minute concurrency summary read one of the raw tables by name. Once your retention window passed the 2.0 storage switchover and `nightowl:prune` retired that old table, the summary hit a missing-table error every minute and wrote nothing from then on — while every other summary stayed current, so nothing looked wrong. The dashboard then fell back to scanning raw telemetry for wide ranges, which is what makes a 14-day chart time out. The agent now checks for both storage formats on every pass instead of assuming, and `nightowl:backfill-rollups` does the same, so the repair itself can't fail the same way.
  * **A deploy now repairs a summary that stopped, not just one that started late.** `nightowl:migrate` has always rebuilt incomplete summaries for you, but it only ever checked whether history was missing from the *start* — and a table that freezes still has all of its early history, so it looked healthy on every subsequent deploy. It now also checks the newest bucket against the newest raw telemetry, and rebuilds anything more than two hours behind. The tolerance is deliberately generous so that a busy agent working through a backlog is never mistaken for a broken one.
  * **Percentile charts over wide ranges are dramatically faster.** The function behind p50/p95/p99 got slower with every row it read, so a long range didn't just take longer — it hit your database's statement timeout and returned nothing at all. A 14-day per-route read went from 103 seconds to 0.48, and a case that never finished inside two minutes now takes 2. Results are byte-identical to before. This needs `php artisan nightowl:migrate`.
  * **An unreachable alert receiver no longer stalls the agent.** Sending health alerts ran inline on the agent's main loop, so one unroutable SMTP or webhook host held everything — ingest, drain, health reporting — for the full connect timeout, measured at 30 seconds. Alerts now go out in a separate short-lived process. The symptom was an agent at almost no CPU reporting heavy lag.
  * **New health signal: a summary table that stops updating while its peers keep going** now reaches your dashboard and alert channels instead of only the agent's log file. It grades each table against others of the same granularity, so it needs no assumption about which telemetry your app emits, and it stays quiet when everything stopped together — that case already has a clearer signal.
  * **`nightowl:drop-v1-histograms` is usable again.** 2.0.2 made it refuse to run, because dropping that data left the percentile function above as the only source and it couldn't serve a wide range. That's fixed, so the command works again; it now asks you to run `nightowl:migrate` first if the faster function isn't in place yet. It's still irreversible.
  * **Stale critical alerts no longer stick to your dashboard forever.** An agent announced a problem as resolved using an identity that includes its process ID, so any restart, deploy or crash left the old warning with nothing to ever clear it — one app was showing a critical drain alert raised by a process last seen 15 days earlier. Warnings that no running agent is still reporting are now retired automatically after 15 minutes. They're marked resolved rather than deleted, so the history stays.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate`, then restart the agent. The migrate step is required — it installs the faster percentile function and rebuilds any summary that stopped. If a summary froze more than a few days ago, run it soon: the rebuild needs raw telemetry from that period, and once your retention window passes it the gap can't be filled. See the [agent changelog (2.1.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#210---2026-07-31) for details.
</Update>

<Update label="July 30, 2026" tags={["Agent"]}>
  **An upgrade could stop the agent accepting telemetry on a high-volume app**

  If you upgraded to 2.0 on a busy application, please take this one.

  * **The post-upgrade summary rebuild could lock out the drain.** After an upgrade the agent rebuilds the per-minute summaries behind the wide-range charts. It worked through history in fixed time slices, and a time slice is a poor guess at how much work it contains: on an app writing tens of thousands of rows a minute, one slice could hold a summary table long enough that the agent's own writer couldn't get in. Telemetry then queued on disk, correctly and safely, until the buffer hit its limit — after which the agent began refusing new telemetry outright, and that data is not recoverable. Slices are now measured rather than guessed, and resized continuously so the writer is never locked out for more than about a second. On the workload that reproduced this, failed writes went from 4 in 8 to 0 in 56, and the longest period with no telemetry written at all went from 23.6 seconds to none.
  * **Contention no longer costs you telemetry.** Independently of the above, a write that can't get at a summary table now stores its raw telemetry anyway and notes what summaries it still owes. The next `nightowl:migrate` rebuilds them. Previously the whole batch was postponed, which is what allowed the queue to build in the first place — so the worst case is now a temporary dip in a wide-range chart rather than data you never get back.
  * **Two new health signals.** Both of these states were previously visible only in the agent's own log file. The agent now reports when it owes a summary repair, naming the tables and the command that fixes them, and reports while a post-upgrade rebuild is still running — as information, escalating to a warning after six hours, by which point the likelier explanation is that it never finished.
  * **New: `NIGHTOWL_AUTO_BACKFILL=false`** if you would rather run the rebuild yourself in a maintenance window. Leave it on unless you have a reason: an empty summary table makes wide-range charts read zero rather than falling back to raw telemetry, and only this pass fills it. With it off, the agent tells you each boot which tables are still empty.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent. If your dashboard is showing a gap in any wide-range chart from an earlier 2.0 upgrade, run `php artisan nightowl:migrate` afterwards to rebuild the affected summaries. See the [agent changelog (2.0.1)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#201---2026-07-30) for details.
</Update>

<Update label="July 29, 2026" tags={["Agent", "Dashboard", "Performance"]}>
  **NightOwl agent 2.0 — a new storage format, and a drain that survives a database outage**

  This is a major version. The normal upgrade is still `composer update` plus a restart, and your history stays visible in the dashboard throughout — but two changes are worth reading if you query the telemetry tables yourself.

  * **Telemetry is written in a new, much smaller format.** Every raw table now has a `_v2` twin that the agent writes instead: event times stored as numbers rather than text, IDs as native `uuid`, and repeated values (environment, server, route, SQL statement, stack trace) stored once in a lookup table and referenced by ID. One production sample carried 28M query rows a day across just 225 distinct statements — those are now stored once each. Nothing is lost: every value from the old format is exactly recoverable. **The dashboard reads both formats, so you see no gap.** What breaks is anything of yours pointed at the old table names — a BI extract, a report, a `psql` query — which will keep returning only rows written before the upgrade, with no error to warn you. The [upgrade guide](https://github.com/lemed99/nightowl-agent/blob/main/UPGRADE.md) has the equivalent query for each renamed column. `NIGHTOWL_STORAGE_V2=false` reverts to the old format with no schema change if you need time.
  * **The old tables are eventually retired.** Once the switchover is older than your retention window and an old table is genuinely empty, `nightowl:prune` drops it. Emptiness is required, not caused, so no rows are ever destroyed and an older agent still writing to those tables can't be pruned out from under. `--keep-v1` disables this permanently — use it if you have a view, foreign key, or monitoring query naming those tables.
  * **A PostgreSQL blip no longer freezes your dashboard until you restart the agent.** If the database went away underneath an open transaction, the agent could keep reusing a dead connection forever: 60 seconds after PostgreSQL was healthy again, a measured test still had the drain failing \~15 times a second and burning \~30% of a core while writing nothing. Your data was never lost — it stayed buffered on disk — but it stopped arriving, so the dashboard sat frozen at the moment of a blip you'd already fixed, and only restarting the process cured it. Fixed, and covered by a test that reproduces the exact failure.
  * **An unreachable database no longer triggers a retry storm.** One 90-second outage previously produced 1,369 near-identical error lines and \~27 CPU-seconds of retries. Reconnects now back off from 100ms to a 10-second ceiling, and the log collapses to about one line per doubling. The first successful batch clears the backoff immediately.
  * **The buffer file gives disk space back.** SQLite reuses deleted pages but never returns them, so the buffer stayed at its high-water mark — a load test measured it at 3.8 GB and it stayed 3.8 GB long after those rows had drained. On a host where the buffer shares a volume with your database, one traffic spike could fill the disk permanently. New buffers reclaim space incrementally on the drain's cleanup tick; existing ones get a one-time compaction while idle.
  * **Failed alerts now tell you they failed.** Webhook, Slack and Discord alerts were sent with the response thrown away, so a revoked webhook or a receiver returning 500 looked exactly like a delivered alert. Failures now carry the status and the receiver's own response (with the URL redacted, since a Slack webhook path is itself a credential). Agent email had four SMTP conformance bugs that each cost real mail — a `HELO` name that Exchange, Office 365 and many Postfix configs refuse outright; capabilities not re-read after `STARTTLS`; always trying `LOGIN` on relays offering only `PLAIN`; and missing `Date`/`Message-ID` headers, which get a message accepted and then silently spam-filed. The dispatch budget rose from 5 to 30 seconds, which was less than one real TLS handshake plus authentication.
  * **New: `php artisan nightowl:test-alert`.** The dashboard's "Send test" button and your triage alerts go out from our API; new-exception and reopened-exception alerts go out from the agent on your own machine, over its own SMTP and HTTP. Two transports reading one configuration row means you could test green in the dashboard and still never be told about a new exception. This command exercises the second one and reports pass/fail per channel with the reason. It also warns when a channel passes but has new-exception alerts switched off.
  * **The Peak Concurrency chart is no longer capped at 6 hours.** It's now backed by a per-minute summary instead of scanning raw requests, so it covers any range you can select.
  * **The cache page groups by key shape.** `user:8213:profile` and `user:9147:profile` were separate rows, so an app keying cache by ID filled the page with near-identical entries. IDs, UUIDs, emails and timestamps in a key now collapse to placeholders (`user:{int}:profile`). Raw cache events keep the literal key, and `NIGHTOWL_CACHE_KEY_TEMPLATE=false` turns it off.
  * **Mail and notification detail pages got the index they were missing.**
  * **A rollup table could go stale for the life of the process after one failed probe.** A check that couldn't run because the connection was dead was cached as "table missing", so that table's summaries were skipped for every later batch — under a log line blaming a migration that had never been the problem. Wide-range views read summaries when they exist, so the damage outlived the blip as a growing hole. Only a check that actually answered is cached now.
  * **Long list views could skip or repeat a row** while an app held telemetry in both storage formats. Fixed.
  * **New: hourly database statistics reported to NightOwl** (`NIGHTOWL_TABLE_STATS=false` to opt out). This exists so that a wedged drain, a gap in your summaries, a missing index or a disk filling up is something we diagnose from our side, instead of asking you to run SQL against your own database. It reads PostgreSQL's catalog and statistics views only — table sizes, row counts, scan and write counters, vacuum timestamps, index usage, connection and lock state, server settings and version. `pg_stat_statements` and query text are excluded on principle and the exclusion is enforced by a test: a tenant database can be shared with your own application, and those are content rather than counts. Row counts are still information, so this is disclosed in the privacy policy. It runs on its own short-lived connection, never the drain's.

  **In the dashboard:**

  * **Detail-page lists now page with Previous / Next.** The tables on a route, query, job, command, mail, notification, outgoing-request or exception detail page moved from numbered pages to cursor paging, which stays correct while new telemetry is arriving underneath you — numbered pages could show you the same row twice, or skip one, as rows shifted between requests. Changing the time range, environment or user filter resets paging rather than stranding you on a stale position.
  * **Log search tells you when it only matched part of the row.** A `?search=` on the logs list can't always reach every column it looks like it should, and nothing said so — a term that lives only in a log's context read as "no such log". The page now shows, above the table, which columns were actually searched and why the scope narrowed.
  * **A failed connection test says what actually failed.** Adding or editing an app always answered "Check your credentials", which was simply wrong for a host rejected by our SSRF guard, an unreachable port, or a missing database. It now shows the real reason, in red for a hard failure alongside the existing amber warnings for schema and permission problems.
  * **The storage breakdown names the four dictionaries** instead of showing "Dict string". They hold real disk and survive a clear, and the caption now says so.
  * **Revoking an MCP token clears its reveal card**, which previously stayed on screen offering a dead credential to copy.
  * Fixed a console error on the sidebar that could make the app list fail to refetch after creating or renaming an app, switching teams, or accepting a transfer.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent — migrations run on boot (or run `php artisan nightowl:migrate` yourself if you've set `NIGHTOWL_AUTO_MIGRATE=false`). Read the [upgrade guide](https://github.com/lemed99/nightowl-agent/blob/main/UPGRADE.md) first if you query the telemetry tables directly. See the [agent changelog (2.0.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#200---2026-07-29) for full details.
</Update>

<Update label="July 20, 2026" tags={["Agent"]}>
  **The agent migrates itself — one less step after every update**

  * **Migrations run themselves.** On startup the agent applies any pending `nightowl:migrate` migrations before it accepts traffic, so schema changes arrive with the code that needs them and you no longer have to remember the migrate step after a `composer update`. It runs under a hard deadline, so a busy or locked database can never hold the ingest port closed — if the migration can't finish in time the agent starts anyway and retries on the next boot. Long rollup backfills continue in the background without delaying ingest, and are retried on the next start if one dies partway.
  * **A heads-up when the agent is running older code than you installed.** After `composer update` the running process keeps the version it booted with. The agent now notices the newer version on disk and says so in its log, so a restart that got skipped doesn't go unnoticed. It won't restart itself — when to bounce the process stays your call.
  * Opt-outs: `NIGHTOWL_AUTO_MIGRATE=false` (use it when the agent's database role can't run DDL), `NIGHTOWL_UPDATE_CHECK=false`.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent — the migration step now happens on boot. Shipped in 2.0.0; see the [agent changelog (2.0.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#200---2026-07-29) for details.
</Update>

<Update label="July 20, 2026" tags={["Agent", "API"]}>
  **Partition conversion survives interruption, and billing access is more reliable**

  * **`nightowl:partition` is now safe to interrupt and safe to run twice.** Two runs at once no longer collide — the second reports its tables as skipped and exits `3`, leaving them untouched. A run killed outright is repaired by the agent within about a minute, instead of leaving a table that rejects every write until you found it by hand.
  * **It refuses to run through a transaction pooler.** Pointed at PgBouncer or Supavisor in transaction mode, the command now stops with an explanation instead of converting unsafely. Point it at your database port.
  * **Clearer results.** New exit code `4` means every table converted but some daily partitions are still owed — nothing is lost, and a running agent creates them within the hour. Exit `1` now means a table genuinely failed.
  * **Billing: revoked and recovered subscriptions are tracked correctly.** A failed renewal that you then fix restores access immediately, webhooks that arrive out of order can no longer undo a newer decision, and an account that subscribed and lapsed is no longer reported back to itself as being on a trial.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent — no migration needed. See the [agent changelog (1.4.1)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#141---2026-07-20) for details.
</Update>

<Update label="July 19, 2026" tags={["Agent", "Dashboard"]}>
  **Know when you need more workers — concurrency charts and a saturation alert**

  * **Two new charts on the Requests page answer "do I need more Octane/FPM workers?".** *Avg Concurrency* shows how many requests were in flight on average per interval (computed from your existing telemetry — a request occupies a worker for its full duration). *Peak Concurrency* reconstructs the true maximum of simultaneous requests from individual request timings, catching short bursts the average hides. Peak scans raw data, so it covers up to the most recent 6 hours and says so on the card.
  * **Tell NightOwl your worker counts.** A new **HTTP Workers** tab in app settings takes a per-environment total (Octane workers, or `pm.max_children` under FPM — the total across all servers in the environment). Both charts then draw a threshold line at that count, so saturation is visible at a glance.
  * **Worker saturation alert.** Opt-in, with a configurable trigger: when average concurrency holds at or above your chosen percentage of the worker count for your chosen number of consecutive minutes, the agent opens a performance issue and notifies your alert channels — same lifecycle as duration thresholds (dedup, resolve, auto-reopen). Environments without a worker count are never alerted. Configure it on the HTTP Workers tab.

  **Upgrading the agent:** the charts and settings work on any agent version; the saturation alert needs the agent evaluating it — run `composer update nightowl/agent` and restart the agent (no migration needed). See the [agent changelog (1.4.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#140---2026-07-19) for details.
</Update>

<Update label="July 18, 2026" tags={["Agent", "Dashboard"]}>
  **Pruning shows its progress, and the storage page tells the right disk story**

  * **Agent: pruning a large backlog no longer looks stuck.** The first prune after converting to partitioning deletes all your pre-conversion history — previously one silent, minutes-long delete. It now deletes in bounded batches and prints progress as it goes (`--delete-chunk` tunes the batch size).
  * **Dashboard: the Storage tab's explainer now matches your schema.** Converted apps see the real story — expired days are dropped whole and their space returns to disk, with pre-conversion history freeing as it ages past your retention window. Unconverted apps keep the previous guidance.

  **Upgrading the agent:** run `composer update nightowl/agent` — no migration needed. See the [agent changelog (1.3.2)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#132---2026-07-18) for details.
</Update>

<Update label="July 17, 2026" tags={["Agent", "Performance", "Dashboard"]}>
  **Faster long-range charts, sharper percentiles, and a lighter write load on your database**

  * **Long-range charts are much faster.** NightOwl now keeps hourly and daily summaries next to the per-minute ones, so a 30- or 90-day chart reads far fewer rows — up to 1440× fewer on the widest views — instead of scanning every minute.
  * **More accurate percentiles.** Duration percentiles (p95/p99) are now backed by a DDSketch estimator with about 1% relative error, down from roughly 2.8% on the previous histogram — with no change to how you read them.
  * **Lighter footprint on your PostgreSQL.** A reader audit removed 24 unused indexes from the raw telemetry tables and tuned how the summary tables pack and vacuum, so the agent does less work per row it writes.
  * **Raw tables are partitioned by day.** Pruning old telemetry becomes an instant drop of a day's partition instead of a slow delete. Existing installs convert with one command (see below).
  * **Storage breakdown in Data Management** now reflects the tiered summaries.

  **Upgrading the agent:** run `composer update nightowl/agent`, then in the monitored app run `php artisan nightowl:migrate` and restart the agent. Migrate populates the new hourly/daily summaries from your existing data automatically, so the faster long-range charts apply to your history too — no manual backfill step. To convert an existing install's raw tables to partitioning, also run `php artisan nightowl:partition` — do this in a quiet window if your logs table is large (it rewrites that one table under a lock). See the [agent changelog (1.3.1)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#131---2026-07-17) for full details and tuning options.
</Update>

<Update label="July 16, 2026" tags={["Agent", "Dashboard", "API"]}>
  **A network stall can no longer wedge the agent's writes, and clearing data now runs in the background**

  * **Fixed: a network problem while the agent was saving telemetry could stall it for around 15 minutes.** The agent had no working timeout on writes to your database, so if the network path dropped without closing the connection, the agent sat blocked until the operating system gave up. While it was stuck, its local buffer filled and it began refusing new telemetry rather than queuing it, so data was lost rather than delayed. Writes are now bounded by a deadline on the connection itself, typically clearing a dead path in about 30 seconds and re-sending the buffered data. Nothing in the buffer is lost.
  * **A new health diagnosis, `Drain wedged`,** names the exact database call the agent is stuck in, if it ever happens again. On Linux with libpq 12 or newer it should stay dormant — the agent warns at startup if your setup can't enforce the deadline.
  * **Clearing data no longer needs the page open.** Deletions now run in the background: you get a progress card you can leave and come back to, on any device, plus a cancel button. Previously a large clear could run past the request limit and die halfway with nothing to tell you what had been removed.
  * **Preview now shows which filters a table can't honour.** Filters like route or status code don't exist on every data type, so they silently didn't narrow those tables — the whole window went. Preview now marks this per table, and clearing asks you to confirm before proceeding.
  * **Fixed: paying through Polar under a different email than your NightOwl account no longer leaves the account locked.** Checkouts now carry your account ID, so payment is matched to you regardless of the address entered on the payment form.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent (no migration needed). See the [agent changelog (1.2.14)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1214---2026-07-16) for the tuning options and details.
</Update>

<Update label="July 15, 2026" tags={["Agent", "Dashboard", "API"]}>
  **Clear guidance when NightOwl's database role can't read your tables, and faster issue counts**

  * **NightOwl now tells you when its database role is missing read access.** If the role NightOwl connects with can reach your database and the `nightowl_*` tables exist, but the role was never granted `SELECT` on them, you'd previously hit a generic error on every dashboard read. Now **Test Connection** flags it directly ("Connected — no read access"), and if it's discovered while you're using the app, the dashboard sends you to a dedicated page with the exact `GRANT` to run (including default privileges so future rollup tables are covered too) instead of stranding you on a raw error.
  * **The sidebar issues badge no longer times out on large issue tables.** The issue-count query now runs off a dedicated index in a single pass instead of scanning the full table, so the badge and issue tab counts stay fast even with a large backlog on a remote database. Requires the agent update below.

  **Upgrading the agent:** run `composer update nightowl/agent`, then in the monitored app run `php artisan nightowl:migrate` to create the new index (built online — it won't block the agent). See the [agent changelog (1.2.13)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1213---2026-07-15) for details.
</Update>

<Update label="July 11, 2026" tags={["Agent", "Dashboard"]}>
  **Data Management is now rollup-aware, and command/scheduled-task history survives cleanup**

  * **The Data Management page was rewritten** around what each table keeps versus loses. Every data type shows its rollup coverage, so you can clear raw telemetry to reclaim disk knowing your charts, totals, and percentiles are safe. It's split into Storage / Retention / Clear tabs, with a working range delete and a one-week protected floor on the most recent data.
  * **Commands and Scheduled Tasks now keep their aggregates when you clear the raw rows** — success/failure counts, duration charts, and percentiles are preserved. Previously, clearing them lost everything.
  * **Fixed: malformed log timestamps could leave log rows that pruning would never delete.** Such rows now get a valid timestamp so retention can clean them up.

  **Upgrading the agent:** run `composer update nightowl/agent`, then in the monitored app run `php artisan nightowl:migrate` and `php artisan nightowl:backfill-rollups`, and restart the agent. See the [agent changelog](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1213---2026-07-15) for the exact steps.
</Update>

<Update label="July 8, 2026" tags={["Agent"]}>
  **The agent no longer over-counts telemetry on some databases**

  * **Fixed: on certain setups the agent could write the same requests to your database many times over** — inflating request and query counts (by tens to hundreds of times) and filling your database far faster than your real traffic would. After saving a batch of telemetry, the agent marked it "already sent" in a single operation that older SQLite versions reject as too large; the mark failed, so the agent re-sent the same batch on every cycle. It now records that progress in small chunks that work on any SQLite version. Your capture and batch-size settings are unchanged — nothing about how much you collect changes.
  * **The agent now refuses to start if it can't write its local buffer** — a full disk, an exhausted disk quota, a read-only mount, or the buffer file being owned by a different user than the agent runs as. Instead of starting and silently re-sending data, it stops with a message telling you exactly what to fix. Together with the July 6 change, a buffer the agent can't write can no longer cause duplicated telemetry.
  * **`php artisan nightowl:clear` now removes all NightOwl data**, including the pre-aggregated summary tables it previously left behind. Clearing your telemetry no longer leaves wide time-range dashboard views reading stale numbers.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent (no migration needed). See the [agent changelog (1.2.11)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1211---2026-07-08) for details.
</Update>

<Update label="July 6, 2026" tags={["Agent"]}>
  **Dashboard counts no longer drop to zero after updating the agent**

  * **Fixed: after updating the agent and running `php artisan nightowl:migrate`, dashboard numbers like jobs, authenticated users, and mail could read 0** even though your data was intact. Migrate created the pre-aggregated summary tables the dashboard reads from but left them empty until you separately ran `nightowl:backfill-rollups`, and the dashboard reads an empty summary as zero. Migrate now populates those tables from your existing telemetry automatically, so the counts are right as soon as it finishes. Nothing was ever lost — only the wide-range view was affected. (Pass `--no-backfill` if you'd rather backfill manually on a very large database.)
  * **The agent won't duplicate telemetry if its local disk fills up.** When the agent's local buffer disk is full, data it had already saved to your database could be written a second time. The agent now pauses draining until the disk is writable again instead of re-sending, and the stall is now visible on the health page rather than silent.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` (which now backfills the summary tables for you) and restart the agent. See the [agent changelog (1.2.10)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1210---2026-07-06) for details.
</Update>

<Update label="July 5, 2026" tags={["Dashboard", "API"]}>
  **See how much space NightOwl is using in your database**

  * App settings has a new **Storage** tab showing the on-disk footprint of your NightOwl telemetry in your own PostgreSQL — a total, plus a per-table breakdown (requests, jobs, queries, exceptions, and the rest) with indexes included. It reads the database's own size catalog rather than scanning your tables, so it's instant no matter how much history you've collected, and it only ever reports NightOwl's own `nightowl_*` tables — never the size of your application data or your database as a whole.
</Update>

<Update label="July 3, 2026" tags={["Agent", "API", "Dashboard", "Performance"]}>
  **Run NightOwl on Laravel Vapor, and faster wide time-range pages**

  * **You can now run NightOwl on serverless hosts like Laravel Vapor.** The instrumented app used to require a NightOwl agent running on the same machine, which Vapor and AWS Lambda can't do. You can now point the app at an agent running on a separate always-on box in the same private network by setting `NIGHTOWL_INGEST_URI=host:port`. Existing single-host installs are unchanged. See the new [Laravel Vapor guide](/agent/vapor).
  * **The Users, Mail, Notifications, and Exceptions pages now stay fast over wide time ranges on high-volume apps.** Their lists, overview stats, and charts now read from compact per-minute summaries the agent maintains, instead of scanning your full raw telemetry for the selected window — the same speed-up already in place for the Queries and Requests pages. The numbers match your raw data (with histogram-estimated percentiles on wide ranges, as elsewhere in the app).
  * **The query and exception detail pages stay fast on high-volume apps too.** A few detail-page spots still scanned your full raw telemetry for the busiest individual queries and errors — a query's environment filter and slow-query (duration) filter, an exception's environment filter and occurrence chart, and the exception "Copy as Markdown" export — and could time the page out. They now read the same per-minute summaries the rest of the app uses, so they load no matter how much history you've collected. The numbers match your raw data.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` (adds the new rollup tables) and restart the agent. To backfill history for time ranges before the upgrade, run `php artisan nightowl:backfill-rollups`. See the [agent changelog (1.2.7)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#127---2026-07-03) for details.
</Update>

<Update label="July 2, 2026" tags={["Agent", "Dashboard", "Performance"]}>
  **Faster job detail pages, and the IP to allowlist for BYO databases**

  * **Job attempt detail pages load again on job-heavy apps.** Opening a job attempt pieces together the job's family — its retries, the job that dispatched it, and any jobs it dispatched — through a series of lookups. On databases holding a large volume of job history those lookups were slow enough to hit the request time limit and fail the page with a timeout. A new database index makes each lookup fast, so the page loads regardless of how much job history you've collected. The same index also speeds command, scheduled-task, and request detail pages.
  * If your PostgreSQL sits behind a firewall or IP allowlist, the [Quick Start](/quickstart) now lists the static IP the dashboard connects from (`178.156.227.16/32`) so you can whitelist it on your database's security group, network restrictions, or firewall.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` (adds a job index) and restart the agent. See the [agent changelog (1.2.6)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#126---2026-07-02) for details.
</Update>

<Update label="June 29, 2026" tags={["Agent", "Dashboard", "API"]}>
  **Accurate timelines after an outage, clearer database errors, and correct job counts**

  * Telemetry is now dated by when each event actually happened, not when it reached your database. After a database outage, the backlog the agent catches up on lands in the minutes the events occurred instead of bunching at "now" — so time-range charts and percentiles stay accurate across a recovery.
  * The health page now tells "can't connect to your database" apart from "connected but can't write," so a network/credential problem and a not-yet-migrated schema no longer show the same message.
  * If the agent can't start because its port is already in use — most often because Nightwatch's agent already holds the shared default port 2407 — it now prints a clear message and how to run the two side by side, instead of a stack trace.
  * **Job counts and durations are now correct.** Job totals across the dashboard count actual job attempts rather than double-counting the dispatch and the attempt, and reported minimum/p95 job durations are no longer dragged down by queued-dispatch rows.
  * The **Source** badge on mail, notification, and log lists now deep-links to the parent request or job it ran inside.
  * Fixed a case where a detail page could briefly show another entity's cached data, and made detail-list lookups for job-sourced rows faster (a new database index).
  * New `nightowl:prune --hours` option for retaining only a few hours of raw telemetry, and the agent no longer re-sends or double-counts data if a local buffer write fails after the rows were already saved to your database.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` (adds a job index) and restart the agent. See the [agent changelog (1.2.5)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#125---2026-06-29) for details.
</Update>

<Update label="June 26, 2026" tags={["Agent", "Dashboard", "API"]}>
  **Clearer answers when telemetry stops flowing**

  * When the agent can reach your database but can't write to it — the NightOwl tables aren't migrated, the database role can't INSERT, or the credentials/database name are wrong — the health page now names the exact cause and the fix (for example, "Run `php artisan nightowl:migrate`") instead of a generic "Postgres may be unreachable." Only the error code and table name are reported; your row data never leaves your database.
  * **Test connection** when adding or editing an app now also checks that the NightOwl tables exist, so a not-yet-migrated database is caught at setup instead of showing an empty dashboard later.
  * New opt-in setting (`NIGHTOWL_DRAIN_QUARANTINE`) lets the agent set aside an individual telemetry row your database rejects so the rest of your data keeps flowing, instead of the pipeline stalling on one bad row; anything set aside is reported on the health page.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent. See the [agent changelog (1.2.4)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#124---2026-06-26) for details.
</Update>

<Update label="June 24, 2026" tags={["Dashboard", "API"]}>
  **Agency tier: run every client from one place**

  * **App-scoped access.** Limit a team member to specific apps instead of your whole fleet — handy for contractors or per-client staff. Invite several people across several apps with a single shareable, email-gated invite link.
  * **Fleet portfolio overview.** One page showing request volume, error rates, and open issues across all your apps, so you can spot the client that needs attention without opening each app. Live traffic counts in this view come from the agent's health report (agent 1.2.3+ — see below); no request content ever leaves your database.
  * **Onboarding templates.** Save a new-app setup as a reusable template instead of re-entering the same configuration for each new client.
</Update>

<Update label="June 22, 2026" tags={["Agent"]}>
  **More accurate timestamps and percentiles**

  * Fixed telemetry timestamps drifting on agents or databases not set to UTC. The drift could make recent data vanish from short time-range filters (1H/6H) or show "last seen" times in the future. Every telemetry table is now stamped in UTC. Rows written by older agents on a non-UTC server stay skewed — let them age out via `nightowl:prune`, or run `nightowl:clear` on a throwaway dataset; there's no automatic correction.
  * High percentiles (p95/p99) over wide time ranges no longer overshoot past the slowest query actually observed.
  * The agent's health report now carries lightweight per-app request, error, and exception counts (plus an open-issue gauge) that power the new Agency **portfolio overview**, and a new `nightowl_reports` table is added by the migration. Both are counts/snapshots only — no request content leaves your database.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` (adds the reports table) and restart the agent. See the [agent changelog (1.2.3)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#123---2026-06-22) for details; the timezone fix completes one started in [1.2.2](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#122---2026-06-07).
</Update>

<Update label="June 19, 2026" tags={["Dashboard", "API", "Performance"]}>
  **Profile images, environment colors, and faster 1-hour views**

  * Upload a profile avatar, and give each app its own logo.
  * Assign a color to each environment (production, staging, and so on) in settings, so they're easy to tell apart across the dashboard.
  * The 1-hour view now reads from the same pre-aggregated rollups as the wider ranges, so it loads quickly instead of timing out on high-volume apps (charts now use 60-second buckets). Filter dropdowns are also bounded to rollups so they no longer time out on busy databases.
</Update>

<Update label="June 18, 2026" tags={["Dashboard", "API"]}>
  **Time format preference and steadier dashboards on flaky databases**

  * Choose 12-hour or 24-hour time display in your preferences (defaults to your browser's locale).
  * If your database server freezes mid-query — host out-of-memory, a network partition — the dashboard now returns a clear error quickly instead of hanging until the request is killed.
</Update>

<Update label="June 16, 2026" tags={["API", "Performance"]}>
  **Wide-range charts no longer crash high-volume apps**

  * Percentile, duration, and count charts over wide time ranges are now served entirely from rollup summaries, eliminating the out-of-memory crashes and timeouts that hit busy tenants when these were computed from raw telemetry.
  * The MCP (Claude) data tools now work with transaction-mode connection poolers such as Supavisor, PgBouncer, and RDS Proxy.
</Update>

<Update label="June 7, 2026" tags={["Agent", "API"]}>
  **Health reports no longer dropped on long hostnames or extreme metrics**

  * If you run the agent on Kubernetes or a host with a long FQDN, its instance identifier (`hostname:pid`) could be too long for the platform to store, which silently rejected the entire health report — so that instance never showed up under agent health. The platform now accepts longer instance IDs, and the agent caps its own identifier to fit.
  * Two health gauges, Postgres write latency and buffer utilization, could overflow the platform's columns under a stalled database or a very low `NIGHTOWL_MAX_PENDING_ROWS`, again dropping the report just when you most needed the signal. Both are now clamped on the agent and stored in wider columns on the platform.

  **Upgrading the agent:** run `composer update nightowl/agent`, then restart the agent. See the [agent changelog (1.2.1)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#121---2026-06-07) for details.
</Update>

<Update label="June 6, 2026" tags={["Agent", "API", "Performance"]}>
  **Fast dashboards on high-volume apps**

  * The agent now maintains per-minute rollup tables alongside your raw telemetry, and the dashboard reads them for wide time ranges instead of scanning every raw row. Queries, requests, jobs, outgoing requests, and cache views now load quickly even on busy apps where wide ranges used to time out. Percentile charts over long ranges are served from a built-in histogram.
  * To make historical ranges fast right after upgrading, run `php artisan nightowl:backfill-rollups` once (it's throttled and safe to run alongside a live agent). New telemetry is rolled up automatically as it drains.
  * Rollups are tiny, so they're kept far longer than raw telemetry (90 days by default, via `NIGHTOWL_ROLLUP_RETENTION_DAYS`) — keep long-range trend charts while pruning raw data aggressively.
  * When a tenant query against a very large table runs too long, the dashboard now returns a clear "try a narrower time range" message instead of failing with a generic error.
  * Added **Sign in with GitHub** alongside Google.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate` and restart the agent. To backfill historical rollups, also run `php artisan nightowl:backfill-rollups`. See the [agent changelog (1.2.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#120---2026-06-06) for details.
</Update>

<Update label="June 5, 2026" tags={["API", "Performance"]}>
  **Faster duration charts on large datasets**

  * Percentile and duration charts are now computed inside PostgreSQL instead of loading every row into memory. This fixes out-of-memory errors on apps with high request volume and makes the charts noticeably faster.
  * Raised the API worker memory ceiling so heavy dashboards stay responsive under load.
  * Quickstart now documents routing your application logs into NightOwl with `LOG_STACK`.
</Update>

<Update label="June 4, 2026" tags={["Agent", "Dashboard"]}>
  **Migration tracking and a master on/off switch**

  * New `nightowl:migrate` command applies the agent's schema and records its migration history in your own database, so shared-database setups and upgrades are predictable. The new `nightowl:install` step is documented in the quickstart.
  * Added `NIGHTOWL_ENABLED` as a single master switch for the agent, plus `NIGHTOWL_RUN_MIGRATIONS` to opt out of automatic migrations.
  * Fixed an integer overflow on duration and size columns that could occur on very large values.
  * When your tenant database is unreachable, the dashboard now shows a clear status page instead of hanging, and lets you remove the app from there.

  **Upgrading the agent:** run `composer update nightowl/agent`, then `php artisan nightowl:migrate`. See the [agent changelog (1.1.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#110---2026-06-04) for details.
</Update>

<Update label="June 3, 2026" tags={["Agent", "Dashboard"]}>
  **Drain stability and a smoother first-run experience**

  * Fixed the drain worker pinning a CPU core to 100% when running under Octane/Swoole.
  * Added a watchdog so a stalled PostgreSQL SSL handshake or COPY no longer hangs the drain worker.
  * Added `NIGHTOWL_DB_SSLMODE` so you can set the PostgreSQL SSL mode the agent uses (defaults to `prefer`).
  * New onboarding page: if your database schema isn't initialized yet, the dashboard guides you through setup instead of erroring out.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent. See the [agent changelog](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#1010---2026-06-03) for the 1.0.6–1.0.10 fixes.
</Update>

<Update label="June 1, 2026" tags={["API", "Performance"]}>
  **More accurate P95**

  * P95 latency is now calculated with PostgreSQL's `percentile_disc`, giving exact percentiles on query and request detail pages. Added a `avg` sort option alongside it.
</Update>

<Update label="May 28, 2026" tags={["Agent"]}>
  **Cleaner dependencies**

  * Removed the `react/http` dependency in favor of raw sockets. This drops an old `psr/http-message` version pin that could conflict with modern Laravel packages, so the agent installs cleanly alongside more of your dependencies.

  **Upgrading the agent:** run `composer update nightowl/agent` and restart the agent. See the [agent changelog (1.0.3)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#103---2026-05-28).
</Update>

<Update label="May 27, 2026" tags={["API"]}>
  **Signup anti-abuse**

  * Added honeypot fields, disposable-email blocking, and Gmail dot-normalization to signup, login, and password reset, cutting down on spam and duplicate accounts.
</Update>

<Update label="May 26, 2026" tags={["Launch"]} description="NightOwl 1.0">
  **NightOwl is live**

  The self-hosted monitoring platform for Laravel. Laravel Nightwatch collects your telemetry, the NightOwl agent buffers it and drains it into your own PostgreSQL database, and the NightOwl dashboard gives you a full monitoring UI on top. Your request data never touches NightOwl's infrastructure.

  **Monitoring**

  * All 12 Laravel event types: requests, exceptions, queries, jobs, commands, scheduled tasks, cache, mail, notifications, outgoing requests, logs, and users.
  * P50/P95/P99 percentile charts and sortable, filterable lists for every event type.
  * Detail pages with full traces, including the dispatched-job tree and job-attempt history.
  * Environment filtering across exceptions, issues, and queries.

  **Issue management**

  * Exceptions grouped by fingerprint and slow routes opened automatically when they cross the p95 thresholds you configure.
  * Status (open / resolved / ignored), priority, assignment, comments, and a full activity timeline.
  * Bulk actions to resolve, ignore, reopen, prioritize, and assign in one step.
  * Automatic reopen when a resolved issue recurs (regression detection).

  **Alerts**

  * Multi-channel notifications over Slack, Discord, Email (bring your own SMTP), and Webhook (HMAC-signed).

  **Plans and teams**

  * Three flat tiers: $5 Hobby, $15 Team, \$69 Agency. Bring your own PostgreSQL, with no event limits or retention caps.
  * Teams with shared apps, owner-pays billing so invitees inherit the owner's plan, and Agency-tier app ownership transfers.

  **Integrations**

  * MCP server with full dashboard parity, so Claude Code, Codex, Cursor, Windsurf, and Zed can query and triage your monitoring data over personal access tokens.
  * Google OAuth sign-in.

  **Agent**

  * High-throughput ReactPHP ingest with a local SQLite WAL buffer and a PostgreSQL drain over the COPY protocol.
  * Runs standalone or in parallel mode alongside an existing Laravel Nightwatch Cloud setup.
  * Multiple drain workers, configurable batch sizes, and per-instance health monitoring.
  * Laravel 11, 12, and 13 supported.

  **Agent install:** `composer require nightowl/agent`. See the [agent changelog (1.0.0)](https://github.com/lemed99/nightowl-agent/blob/main/CHANGELOG.md#100---2026-05-08) and the [quickstart](/quickstart).
</Update>
