Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

Notable changes to RawTrail (formerly Fleet EDR), newest first. This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (pre-1.0).

## [0.9.0] (2026-11-06)

See what your AI coding agent is actually doing. Each Claude Code session gets a Summary, a Timeline grouped by prompt, its tokens and permission modes, and an alert when it reads your credentials, which protect mode can refuse. A one-command install runs it all on your own Mac.

### Upgrade notes (action required)

- **The agent now sends the prompts people type to Claude Code.** Each prompt is stored as an event, with recognizable credentials replaced and long text cut at 16 KiB. On upgrade, the agent rereads transcripts changed in the last 10 minutes from their start, so prompts typed shortly before the upgrade are sent too. To stop prompts being sent from any Mac, upgrade the server first and turn off **Capture prompts** in Admin settings > Data collection before upgrading agents: an agent sends no prompts until it has that setting, and the change is audited with a reason. `EDR_CAPTURE_PROMPTS=0` in an agent's configuration keeps prompts on that Mac whatever the setting says.
- **If you changed the event archive's retention by hand, set `EDR_EVENT_RETENTION_DAYS` to it before upgrading.** Set it on every server and ingest replica. The upgraded server resets the archive to the setting at startup, 30 days when unset, and a shorter window deletes the older events.

### Added

- **An alert when an AI coding agent reads your credentials.** If an agent session reads an SSH private key, AWS credentials, the GitHub CLI token, `.netrc`, Docker registry logins or Kubernetes credentials with anything but that credential's own tool, the new `agent_credential_read` rule raises one alert per session and credential, in plain words. `ssh` reading a key for `git push` is not reported. The credential guard policy (`/api/v1/detection-config/credential-guard`) sets the protected files, the tools allowed to read each, and the mode for the fleet: detect reports the read, protect refuses it and says so in the alert, and off watches nothing. In the session view, the command that read a credential says which one beside it.
- **A Timeline of each AI agent session, grouped by prompt.** Each prompt the person typed, or passed to `claude -p`, opens to the commands and tool calls the agent made for it, in order; turned off, the switch lists the commands alone. A prompt or command can be selected or copied. A command that started more than 100 processes lists its first ones and any alerted ones, with **Show all** for the rest. Operators without permission to read prompts see each prompt hidden.
- **Run RawTrail on your own Mac to watch your AI agent.** `scripts/laptop/rawtrail-laptop install` starts the server in Docker, reachable from that Mac only, installs the agent pointed at it, and opens the passkey setup for your admin; `uninstall` removes all of it. See [Watch your AI agent](docs/watch-your-agent.md).
- **A laptop profile for the console.** With `EDR_PROFILE=laptop`, which the laptop install sets, the console opens on Agent sessions and keeps single sign-on, containment and service accounts behind **Show advanced settings** in Admin settings. A first-run checklist (**Set up this Mac** in the account menu) walks the approvals a Mac needs, in order, from its live health, and shows a missing Full Disk Access grant as a red step.
- **Share an AI agent session as an image.** The Summary's card copies as a PNG, downloads, goes to LinkedIn or X with a suggested caption and the image ready to paste, or to the system share sheet, made in the browser with nothing uploaded and no share links. Usernames, hostnames, IP addresses, command arguments, prompts, the project's name and internal domains never appear.
- **An AI agent session has a Summary.** A session opens on a card of what it did: the tool calls the agent's own log reports beside the processes that actually ran, the files it changed (with those outside its project), the domains it contacted, the programs it ran most and where it talked to by organization. Files Claude Code keeps for itself, and temporary files the session made and removed, are not counted. Alerts are a count with a link.
- **An AI agent session's header shows its models, tokens and permission modes.** Each mode the agent ran in is listed with how many replies and for how long, and bypass permissions is flagged. The Agent sessions page lists each session's tokens.
- **See which AI agent sessions ran with permissions bypassed.** The Agent sessions page badges each one. A session forked from a bypassed one carries its parent's lines and is badged too. Sessions are marked from the upgrade on: one that ran bypassed only before it is not.
- **The agent reports an AI agent session's token usage and permission mode.** Each reply's model and tokens are sent, with the permission mode in force, whatever the prompt-capture setting; no prompt or reply text is included.
- **Read the prompts typed to an AI agent, behind their own permission.** The prompts of a session are served by the server to operators who hold the new `agent_prompt.read` permission: admins and senior analysts. Analysts and auditors do not have it, and every read is audited. The Agent sessions page searches them across every session over up to 30 days, and each search is audited with its text.
- **Event retention is a setting.** `EDR_EVENT_RETENTION_DAYS` (default 30) sets how long raw events are kept; the server applies it at startup. Shortening it deletes older events. Use the same value on every server and ingest replica. **Admin settings > Data collection** shows how much disk the stores take, and warns past `EDR_STORE_SIZE_WARN_GB` (default 100 GB).
- **Long agent sessions open quickly.** A session's commands, processes and each Activity list load a page at a time with the rest on request, a search in any of them runs on the server over the session's first 20,000 processes (the view says when a session has more), and the header counts the whole session's commands and processes. A command that started hundreds of processes lists its first hundred and its alerted ones, with the rest one click away.
- **An alertable count of service accounts about to expire.** The `edr.service_accounts.expiring` metric reports the active service accounts whose credential expires within 14 days, the same accounts the console flags. Alert when it is above zero and rotate the credential before the date.
- **The console shows the RawTrail logo.** It sits in front of the name in the top bar and is the browser tab icon on the console and the API documentation page.
- **An alert when a contained host stops reporting.** A host that is contained and has not checked in for more than 10 minutes raises a high-severity **Contained host stopped reporting** alert, once per containment, so a host whose agent was stopped does not just show as offline.

### Changed

- **The Agent sessions page is simpler.** Its host, agent and activity filters are removed, and each session shows how long ago it was last active and how long it ran, with the exact times on hover. `GET /api/agent-sessions` still filters.
- **MySQL keeps more in memory, so agent session views load faster.** The quickstart now gives MySQL a 1 GB buffer pool, set with `EDR_MYSQL_BUFFER_POOL_SIZE` in `.env`, and the laptop install 512 MB, which raises its MySQL memory cap to 1,024 MB. An existing install takes the new size when MySQL is next recreated.
- **The console's pages download several times less data.** The server now compresses its responses with zstd or gzip for browsers that accept it, so a long agent session's Timeline is about 1.5 MB on the wire instead of 20 MB.

### Fixed

- **A search with a space or `*` in it no longer fails with "unsafe API path"** in the host timeline and the other searches that send free text.
- **Signing in again no longer discards what you were typing.** When saving the reachable destinations, the watched paths, an exclusion, a rule or an application control rule needs a fresh single sign-on, your unsaved changes are kept and put back when you return, with a notice. Nothing is saved for you: review them and save again.
- **A host told to contain before its network extension is connected is contained as soon as the extension connects,** instead of up to six hours later. The console still shows that first command as failed until the next catch-up confirms it.
- **The detection tuning table no longer shifts when you click a figure.** Observed and Cost figures are plain text, the Cost column now shows the slowest run beside the total and average, and each rule name links to its page. The evaluation count and exact match counts are no longer shown, and a long Cost cell wraps between figures, never inside one.
- **A freshly installed Mac gets the deployment's watched paths as soon as its extension is approved,** instead of up to six hours later.
- **A busy second of an AI agent session no longer loses its file activity.** When more files changed in a second than the extension sends, rows were folded up to a top-level directory such as `/private`, and a pipeline whose jobs all wrote one file lost most of its operations. Each row now keeps its own path or names the directory its files share, and a fan-out's jobs fold into one row on the process that started them, which the Files tab shows as "up to 80 processes under xargs". Anything still dropped is reported, and the session's Summary says how many file operations were not recorded.
- **A host that starts taking commands again no longer keeps showing "it is not taking commands".**
- **Rotating a service account sets a fresh expiry and refuses revoked or expired accounts.** A rotated secret used to keep the old deadline, and a revoked or expired account was rotated into a secret that could never sign in; that now returns `409`. The service accounts page shows each account's expiry, flags accounts expiring within 14 days, and states the expiry when a secret is issued.
- **The server keeps up with a large event backlog.** A Mac that uploads millions of queued events, after being offline or asleep, is now processed at tens of thousands of events a minute instead of slowing to about 1,500. A database deadlock while finishing a batch is retried instead of pausing that Mac's processing for 5 minutes.
- **An agent upgraded in place gets the fleet's settings and policies on its next check-in,** instead of up to six hours later. This covers the watched paths, agent settings and credential guard policy.
- **A missing Full Disk Access grant is reported as such.** Host health names it for the extension and for the agent, instead of a generic failure.
- **A contained Mac keeps restoring its network filter, however often a local administrator switches it off.** Before, the agent stopped trying after three failed repairs in a row, which could leave a contained host reachable.

## [0.8.0] (2026-10-21)

### Upgrade notes (action required)

- **Fleet EDR is now RawTrail, at github.com/rawtrail/rawtrail.** The images are `ghcr.io/rawtrail/rawtrail-server` and `ghcr.io/rawtrail/rawtrail-demo-seed`; point your compose file or manifests at them before upgrading, since the old image names receive no further releases. The `ingest` and `migrate` binaries are now `rawtrail-ingest` and `rawtrail-migrate`. Existing alerts credited to Fleet EDR are re-credited to RawTrail.
- **Verify release signatures against the new identity.** Releases are signed by `https://github.com/RawTrail/RawTrail/.github/workflows/release.yml` (see `docs/install-agent-manual.md`), and `gh attestation verify` needs `--owner rawtrail`. Releases through v0.7.0 keep the `getvictor/fleet-edr` identity and their `ghcr.io/getvictor/fleet-edr-server` image; verify those with the docs at their release tag.
- **Telemetry reports as `rawtrail-server`.** Update any dashboard, alert or saved query that filters on `service.name = 'fleet-edr-server'`; the shipped SigNoz dashboards and alert rules already use the new name, so re-import them.
- **Webhook deliveries send `User-Agent: rawtrail-webhook/1`.** Update any receiver or firewall rule that matches the old `fleet-edr-webhook/1`.
- **The break-glass account is now `admin@rawtrail.local`.** The upgrade moves the existing account, with its passkey and role, so sign in with the new address.
- **Reinstall the agent on every Mac: remove Fleet EDR first.** The agent, app and extensions have new names and bundle IDs (`RawTrail.app`, `io.rawtrail.*`), so the RawTrail package does not upgrade Fleet EDR in place. On MDM-managed Macs, remove the `com.fleetdm.edr.profile.*` profiles first: macOS refuses to remove the old extensions while their System Extensions profile is installed. Then run `sudo "/Library/Application Support/com.fleetdm.edr/uninstall.sh"` and confirm `systemextensionsctl list` shows no active `com.fleetdm.edr` extension, rebooting if removal is pending, before installing `rawtrail-<version>.pkg`. Each Mac enrolls again with the enroll secret and keeps its host record, since hosts are keyed by hardware UUID.
- **Deploy the new configuration profiles.** Push the System Extensions, Full Disk Access and Background Items profiles (`io.rawtrail.profile.*`) before the package.
- **The agent's settings file is `/etc/rawtrail.conf`** (`%ProgramData%\RawTrail\rawtrail.conf` on Windows). Move the enroll secret and server URL from `/etc/fleet-edr.conf` before installing, leaving out any `EDR_XPC_SERVICE` or `EDR_NET_XPC_SERVICE` line (those name the old extensions), and update any install script or MDM payload that writes it.
- **Update exclusions in other security tools.** The agent runs from `/usr/local/bin/rawtrail-agent`, the app from `/Applications/RawTrail.app`, and its telemetry reports as `rawtrail-agent`; see `docs/recommended-exclusions.md`.
- **The agent requires macOS 26 (Tahoe) or later.** The installer now refuses older Macs and says so. Earlier packages installed on macOS 13 to 26.1 but could not run there, so upgrade any such Mac to macOS 26 first.

### Added

- **LaunchAgent and LaunchDaemon loads are recorded.** When anything runs `launchctl load` or `launchctl bootstrap`, the agent reads each plist it names, and the plists in a directory it names (only those the user who ran it could read) and records the job's label, program and arguments. Process detail lists the jobs a command loaded, and the process tree links the command to each job launchd starts. Update the agent to collect them.
- **AI agent sessions.** A new Agent sessions page lists each Claude Code session the agents saw: its project, when it ran, and how many processes and alerts it produced, filterable by host, agent and recent activity. `GET /api/agent-sessions` serves the same list.
- **See what a session did.** The Commands view lists each command the agent ran with everything it started beneath it, including background processes, apps opened with `open -a`, and jobs started through `launchctl`. Each command shows the Bash tool call the agent reported in its transcript, labelled as the agent's own account; prompts and tool output are never read. The Processes view shows the whole session as one tree, and an optional toggle adds the agent's internal housekeeping. Alerts and privilege changes are badged where they happened.
- **Session activity.** An Activity view totals the files, network destinations, DNS names and Unix sockets a session touched, naming the process behind each; process detail shows the same for a process or its subtree (`GET /api/hosts/{host_id}/activity`). It counts only events from when the scope's processes started, so a process from before a reboot that reused a pid and pidversion is left out. A long file path is shortened from its start, keeping the file name, with the full path on hover. A connection to a well-known daemon's socket, such as 1Password's SSH agent or Docker, is tagged with the daemon's name. File changes and socket connections are collected for agent sessions only. A session started with `/fork` links to its parent.
- **Process search by agent and session.** Filter by agent, agent session, story, descendant of a process and argument over a time window, so every `curl` any Claude Code session ran this week is one search.
- **SigNoz alert rules.** Nine ready-made rules cover a silent server, stalled ingest, audit write failures, skipped detection rules, withdrawn event batches, failed maintenance sweeps and break-glass guessing. Apply them with `tools/signoz-alerts.sh`; see `docs/operations.md`.
- **SigNoz and Datadog users: set `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=delta`.** Without it those backends miss the first increment of each counter, so a rarely-moving counter such as audit write failures can increase without alerting. The quickstart compose now forwards this variable and `OTEL_EXPORTER_OTLP_COMPRESSION`.

### Changed

- **Much lower trace volume.** Detection batches are now sampled with agent ingest traffic, and database queries outside a request or batch no longer produce spans. One host exported about 500,000 spans an hour, almost all from this background work. Metrics are unchanged.

### Fixed

- **No application-control rule can block the agent itself.** The extensions and the app were already exempt, but the agent was not, so a block on the RawTrail signing team or certificate would have stopped it from restarting.
- **A command containing a single quote now shows as the agent ran it** in an agent session, with its "Reported by agent" label, instead of garbled.
- **The agent session view's Commands, Processes and Activity switcher no longer moves** when switching views.
- **The server now works out the alert timeline's process chain.** With the alert-chain focus on, the timeline no longer stops at what the graph had loaded, and still narrows when the graph cannot load. When it reaches only part of the chain, it says so and why.
- **The process tree no longer misplaces a process whose parent's pid was reused.** The server now records which process created each one, so a child appears under its real parent rather than a later process with the same pid, and a daemon stays under the process that launched it after launchd adopts it. This needs an agent from this release; older agents' processes are placed as before.
- **Routine events no longer log as faults.** A clean server stop no longer logs three gauge warnings and now exports their final values. An agent that disconnects mid-upload, as on a restart or sleep, is logged at INFO rather than ERROR; the agent resends the batch.

## [0.7.0] (2026-10-05)

### Upgrade notes (action required)

- **The quickstart keeps the process graph for 30 days, not 7.** This matches the event timeline, so an old alert's graph no longer shows its process alone. Expect the MySQL process table to grow to about four times its size; set `EDR_RETENTION_DAYS=7` in `.env` to keep the old window.

### Added

- **Sensitive privacy permissions granted to third-party apps are detected.** `tcc_sensitive_grant` alerts when an app that is not Apple's is granted Full Disk Access, Accessibility, Screen Recording or Input Monitoring (T1548.006). Grants deployed by MDM profiles are not reported. Exclude a vendor by `team_id`. Needs this release's agent.
- **Browser credential theft is detected.** `credential_browser_store_read` alerts when a program other than the browser opens Chrome's, Brave's, Edge's, Arc's, Vivaldi's or Firefox's saved passwords, cookies or their key material, the core behavior of infostealers (T1555.003). Exclude a backup or security tool by its `team_id`. Needs this release's agent.
- **SSH key persistence is detected.** `persistence_ssh_authorized_keys` alerts when a user's or root's `~/.ssh/authorized_keys` is written or replaced (T1098.004). Every host now watches those files in every home. Exclude a configuration-management agent by its path.
- **Watched file paths can name every user's home folder.** Start a path with `~/`, such as `~/.ssh/authorized_keys`, and each host watches it for root and every user account, including accounts added later. Needs this release's agent; older hosts skip the path.
- **Root certificates trusted from the command line are detected.** `trusted_root_certificate` alerts when `security add-trusted-cert` makes a host trust a certificate, or `trust-settings-import` imports trust settings (T1553.004). Certificates deployed by MDM profiles are not reported.
- **Unsigned installer packages are detected.** `installer_unsigned_package` alerts once per package when a package that is unsigned, or whose signature macOS does not trust, runs an install script (T1546.016). Exclude an in-house package by its path.
- **Login item persistence is detected.** `persistence_login_item` alerts when a login item launches an app that is not Apple's or MDM-managed (T1547.015), whether it is an app's background helper or an app added to the user's login items. Exclude a vendor by the app's `team_id` or `signing_id`. Needs this release's agent, which reports the app's signature.
- **Emond and startup-item persistence are detected.** Every host now also watches `/etc/emond.d/rules/`, `/private/var/db/emondClients/` and `/Library/StartupItems/`, which lets two SigmaHQ rules the product already carried run: Emond launch rules and new startup items. Like other imported rules they start in monitor mode.
- **Detection tuning counts the processes a rule gave up on.** When a rule needs a process record that never arrived, it now counts that process as abandoned, shown in the Cost column beside the undecided evaluations it retried. A rule failing to decide no longer looks the same as a rule with nothing to report. Rules that cannot yet count this, and days before the upgrade, show no figure rather than zero.
- **A known-good phone-home can be waived.** `dns_c2_beacon` alerts can now be excluded by the domain the program looked up, which covers its subdomains, or by the program's path, team, signing identifier or cdhash. Until now the rule could only be silenced as a whole. See [recommended exclusions](docs/recommended-exclusions.md).

### Changed

- **A built-in rule's page shows its rule file**, including the values the rule reads, instead of saying there is no document. Every rule's file can be downloaded from its page.
- **Upgrading the agent no longer needs a reboot.** The new extensions take over when the install finishes, or at the next login if no one is logged in, and network and DNS events resume within about 30 seconds. The agent itself restarts during the install. Earlier notes asked for a reboot after every upgrade.
- **Package installs can be excluded by who signed the package.** Installing a `.pkg` raises a suspicious-exec alert for each of its install scripts. Exclude a vendor's installers with a `package_team_id` exclusion, the team that signed the package; an unsigned package cannot be excluded this way. The alert now names the package and its signer.
- **Login item registrations say where the item is and who signed it.** A login item was recorded with no signature, and a background helper with a path relative to its app. Hosts now record the item's full path, the app that added a helper, and the signature of the app that will launch. Requires the updated agent.
- **LaunchAgent persistence is judged on the program it registers.** The rule now watches LaunchAgent registrations rather than `launchctl`, so it also catches a plist that becomes active at the next login, and skips Apple's own and MDM-managed agents. Vendor agents can be excluded by `team_id` or `signing_id`, which a planted file cannot defeat; existing plist path exclusions keep working. If the background items profile is not installed, exclude team `FDG8Q7N4CC` for this rule so the EDR's own LaunchAgent is not reported.

### Fixed

- **Signature exclusions and built-in skips now apply to processes running before the agent started.** A process already running at boot or across an agent upgrade had no signature on record until it exited, so `team_id` and `signing_id` exclusions missed it and Spotlight's indexer could be reported as browser credential theft.
- **An osascript download-and-run chain is no longer missed when its last step is recorded late.** The rule now waits for that step's record, as the other rules do, rather than giving up on the chain.
- **The process graph says when its window predates retention.** A window reaching back past process retention now shows a notice, rather than an alert's process alone with nothing around it.
- **Sigma rules no longer drop an exec whose own record arrives late.** A rule that checks the parent program now waits for the record it is found from, instead of reading the parent as missing. Detection tuning now also counts the events Sigma rules give up on.
- **A rule that runs in monitor mode links to what it has matched.** Those records were reachable only from the Observed count in Detection tuning, which needs a permission the analyst role does not hold, so the records were readable in principle and unreachable in practice for the people who investigate. A rule's own page now offers them.

## [0.6.0] (2026-09-22)

### Upgrade notes (action required)

- **Rotate any credentials in a proxy URL the agent cannot use.** An agent configured with a proxy scheme it does not speak (anything but `http` or `https`) connected to that host anyway and sent the credentials in clear text first. It now refuses such a proxy at startup and reaches the server directly. Treat those credentials as exposed and rotate them.
- **Rewrite any `signing_id` exclusion with the team that signed the binary.** A value is now `<TEAMID>:<identifier>` (for example `Q6L2SF6YDW:com.anthropic.claude-code`), or `platform:<identifier>` for a binary Apple ships; `codesign -dv <binary>` prints both. Bare values no longer suppress anything, so those rules fire again until rewritten. An ad-hoc signature can claim any vendor's identifier, so a bare exclusion could be inherited by a planted binary.
- **Application-control API clients must name each rule's enforcement.** Creating a rule, and each item of a bulk upsert, now requires `enforcement`: `PROTECT` to block or `DETECT` to record without blocking. Add `"enforcement": "PROTECT"` to existing automation to keep its rules blocking.
- **A third profile keeps users from turning off the agent.** `edr-login-items.mobileconfig` marks the Fleet EDR background items as managed, so they cannot be switched off in Login Items & Extensions (macOS 13 and later). Push it through your MDM with the other two profiles.
- **Alerts are now deleted 180 days after their last triage activity.** Nothing deleted them before. Set `EDR_ALERT_RETENTION_DAYS` (0 keeps them forever) before upgrading if you want them kept longer: deletion starts as soon as the upgraded server does, then repeats hourly.
- **Subscribe webhook destinations to sensor health faults.** A host whose sensor cannot be restored no longer raises an alert, so a destination that learned about it through alert deliveries stops hearing about it until you tick **Sensor health fault** for it in Settings, Webhooks.

### Added

- **Contain a compromised Mac.** **Contain host** on a host's page cuts the Mac off from the network, except the EDR server and the DHCP and DNS it needs, and **Release host** gives its access back. Open connections are cut, and containment survives restarts and re-enrollment until released. Each change needs `host.isolate` and a reason, is recorded in the audit log, and is made against the state you were shown, so two responders cannot overwrite each other. The host page and host list report whether the Mac has confirmed it, and say so when a Mac is too old to contain or its name filtering cannot be confirmed. Deployment-wide, you can also list extra destinations a contained host may still reach, such as an MDM or forensic server. See [Network containment](docs/operations.md#network-containment).
- **Rules can be browsed and written in the console.** A new **Rules** page lists every rule the deployment runs, with its severity, its mode, and whether it ships with the product or was written here. Operators with `rule_content.write` can create, edit, delete and roll back rules, each change asking for a reason that is audited. A change takes effect within 30 seconds, and a new rule runs in monitor mode until promoted.
- **Application-control rules can run in Detect mode.** A `DETECT` rule blocks nothing and records each exec it matches, so you can see what a rule would block before promoting it to `PROTECT`, and it never unblocks an exec another rule blocks. Every new rule asks for one or the other, with no default. The policy page shows what each Detect rule would have blocked over the last week and promotes it from there. See [Detect mode](docs/operations.md#detect-mode).
- **Monitor-mode matches can be read.** A match from a rule in monitor mode is now kept as a record carrying what an alert would: the process, techniques, and triggering events. Judge a rule for promotion from what it matched rather than from a count. The records stay out of the alert queue and webhooks, cannot be triaged, and are deleted after 7 days (`EDR_MONITOR_RETENTION_DAYS`).
- **SSO roles can follow identity provider groups.** Map IdP groups to EDR roles, and each SSO sign-in sets the operator's role to the most privileged role among their mapped groups, or the default role when none match. A role set by hand is replaced at the next sign-in, a group cannot grant super admin, and every change is audited. Set it in **Admin settings, Single sign-on**; see [Okta setup](docs/okta-setup.md#map-okta-groups-to-edr-roles).
- **Watch more files for tampering.** In **Detection tuning**, add files or directories that every host records writes, renames, truncations and deletions of, on top of the sudoers files it always watches. A saved change reaches online hosts within seconds, and hosts that were offline or enroll later within minutes.
- **A SigmaHQ threat-hunting rule for clipboard collection.** Runs of `pbpaste` are recorded in monitor mode, from SigmaHQ's threat-hunting rules, which are now vendored alongside its main macOS rules.
- **Coverage now shows what is not covered.** The ATT&CK page could report the techniques your rules detect and not the ones they miss, so the number it gave had nothing to be a fraction of. It now reports how many macOS techniques no rule covers, out of how many there are, and lists them by tactic. An operator who can write rules is offered a link from each gap that opens the rule editor with that technique already tagged. Techniques ATT&CK does not list for macOS are left out rather than counted as holes.

### Changed

- **A sensor that could not repair itself is now host health, not an alert.** A host whose capture provider stopped and could not be restarted is a fault in this product rather than an attack, so it leaves the alert queue and is recorded against the host, under **Details**, with how long the host went uncaptured. It can be delivered to webhook destinations as its own event; alert deliveries are unchanged, and alerts of this kind already raised are left as they are.
- **Withdrawing a command is recorded as its own audit action.** Cancelling a command wrote an audit row saying `command.issue`, the same action an issuance writes, with the same host and command, so the audit log could not tell a command that was sent from one that was taken back. A withdrawal now records `command.cancel`. A SIEM rule counting `command.issue` will see fewer events and should count both actions if it means "response actions taken".
- **ATT&CK coverage is now a tab of the Rules page** rather than its own navigation entry. The coverage view is built from the rules it lists, so the two sit together and each offers the other. Existing links to the coverage page still work.

### Fixed

- **Signature exclusions now match a process that was forked and never re-executed.** Such a process runs its parent's image but carried no code-signing record, so every `team_id`, `signing_id` and `cdhash` exclusion silently missed it. Exclusions that looked ineffective on forking services, `sshd` among them, take effect.
- **A single sign-on save can no longer overwrite a change it did not see.** A page left open, or a script that read the settings earlier, silently replaced everything saved since. A save now names the configuration it was editing and is refused if anything changed, with nothing written. On the API the read reports a `version` and a save that returns it is refused with `409`; omitting it overwrites as before, so existing automation is unaffected.
- **A single sign-on change no longer lands on a sign-in already under way.** Saving while somebody was signing in could give that operator the role their groups map to under the new settings. A save now applies from the next sign-in.
- **Application control no longer offers rule changes an operator cannot make.** An operator with read access and no rule permissions could open any dialog and type an audit reason before being refused.
- **Concurrent application-control rule changes no longer fail.** Two operators changing rules in one policy at once could have the database abort one and return a server error for a change that was valid.
- **The server finishes shutting down in the deployments this product ships.** Every shipped Compose file stopped it before the drain completed, losing in-flight work on every restart and upgrade.
- **An audit row can no longer go missing for a command that was issued.** The row was written after the change was saved, so a crash in between left it unrecorded. It is now saved with the change and delivered afterwards; it may arrive late, but it is not lost.
- **A response action whose outcome was lost no longer stays stuck.** The server re-asks after a minute and the Mac answers from its own record, without repeating the action.
- **A Mac that needs a restart after an agent upgrade says so**, instead of reporting its network extension as not activated.
- **A renamed remote-access tool is detected by its signature.** A shipped rule catching MeshAgent renamed to hide it could not run; the compiled-in name is now read from the code signature, which a rename cannot change. 67 of the 69 shipped rules run, up from 66.
- **An application-control alert links to the policy that blocked**, so an analyst can reach the rule that denied the executable.
- **Saving an application-control rule without changing it no longer fails** with "rule not found", and does not push the policy to hosts or add an audit entry.
- **An agent restart no longer reads as a fault in the control-channel telemetry**, which had made error-rate panels report the fleet's uptime rather than its health.
- **A long exclusion value no longer widens the exclusion table**, so the Reason, Expires and Created by columns stay on screen.
- **Analysts and auditors can reach the Rules page, a rule's detail and its monitor records.** The console asked for a permission those roles do not hold, though the server had been serving them the rule catalogue all along. Rule authoring, including the built-in rules panel and its rollback, still needs `rule_content.read` or `rule_content.write`.
- **An operator whose role grants nothing is told so, instead of landing on a page that fails.** Signing in sent them to Coverage, the one page the console never gated, where the read was refused and the page reported `API error: 403`.
- **An application-control rule's identifier is readable, and its actions are separable.** The identifier was cut to 16 characters whatever the column's width, and copying the cell gave that fragment rather than the value. It now fills the column, copies in full, and offers a copy button. The four row actions, which ran together as one line of text, become one **Actions** menu with Delete set apart.
- **Rules that come with the product are called built-in**, not "shipped", which describes a release rather than where a rule came from. The Rules page, the rollback controls and the exported ATT&CK layer all follow. The Rules filter row also drops its **Search:** and **Show:** labels, which sat in different places; each control names itself now.
- **An alert's process graph shows the alert's own chain.** It asked for every process on the host across a 24-hour window and narrowed in the browser, so on a busy host it opened slowly, warned that most of the day was left out, and often did not contain the alerted process at all. It reads that alert's chain directly now, and the whole-host view keeps the alerted process and its parents.
- **Release host no longer looks like the control beside it that only opens a panel.** Details and Release host were near-identical buttons, so the one that cuts a Mac off the network was hard to tell from the one showing its agent version. Details is plain text with a caret now, leaving containment the only button in the row.

## [0.5.1] (2026-09-12)

Re-release of 0.5.0. No product changes: the server, agent, and installer are byte-identical to what 0.5.0 built, and the 0.5.0 notes below describe everything in this release. 0.5.0 was withdrawn shortly after publication to correct authorship metadata in the repository's own commit history, which is why this carries a new number rather than being republished under the old one.

Upgrading from 0.5.0 is unnecessary. Upgrading from 0.4.0 or earlier, read the 0.5.0 upgrade notes below: they apply in full.

## [0.5.0] (2026-09-11)

Feature release on top of 0.4.0. The headline is rule content: the detection rules a deployment runs move out of the binary and into the database, so an upgrade delivers new rules, you can write your own through an audited API, and a rule set that turns out wrong can be rolled back. Also in this release: per-rule match counts and cost in the detection-tuning table, capture-provider health per host, a new alert for destruction of sudo policy, and a long list of detection-accuracy fixes.

### Upgrade notes (action required)

- **Upgrade your agents.** Process events are now stamped from the system's own event time, and an agent that loses its control connection reconnects instead of waiting. The server tolerates the old stamps, so an older agent already stops missing most connection detections, but only upgrading gets the control-channel fix.
- **Reboot each Mac after upgrading its agent.** macOS keeps the previous network extension registered until the machine restarts, so network and DNS telemetry stop while process events keep flowing, and the usual health indicators stay green. The sign is a repeating `xpc_bridge_connect failed for group.com.fleetdm.edr.networkextension` in `/var/log/fleet-edr-agent.log`.
- **Each server start now installs the rules that ship with the build.** Rules you wrote and tuning you applied are untouched, but expect the rule list to change on the first start after upgrading.
- **`Suspicious exec chain` gives up one class of detection to make the rest tunable.** Chains whose parent is missing from the host's process tree used to alert naming the parent `(unknown)`, which no exclusion could suppress; they now raise nothing. Chains started by `launchd` still alert and name `/sbin/launchd`, so an exclusion works.
- **Exported rule files have a different shape.** `GET /api/rules/{id}/export` now carries the logic a rule matches on. What the rules detect is unchanged; re-check anything that parses the export.

### Added

- **Destroying sudo policy is now detected.** Emptying `/etc/sudoers` or a fragment under `/etc/sudoers.d/`, and deleting one, both raise a new `Sudoers policy destroyed` alert; neither produced any telemetry before. It is separate from `Sudoers tamper`, which means the opposite: one grants root access, the other takes it away.
- **Detection rules are stored in the database and picked up without a restart.** Each server re-reads within about thirty seconds of a change. While unusable content is stored, a restarting server falls back to the release's rules and can differ from its peers.
- **You can write your own detection rules through the API.** A change that would stop your rules loading is refused, and a check endpoint reports what a change would do without making it. Every change needs a reason and is audited, new rules start in monitor mode, and your rules are credited to your deployment rather than to the community project. Editing requires admin, viewing admin or senior analyst.
- **A rule that is too broad or too slow is caught when you submit it.** A search with no narrowing condition is flagged by name, because a rule resting on one fires constantly and buries the alerts you care about. It warns rather than rejects; a pattern too expensive to match is refused outright.
- **You can roll back a bad rule set.** See which rules were added, removed and changed, and put the previous set back. A rollback sticks across a restart while the next release installs normally, your own rules are kept, and you are told which shipped rules were not restored.
- **The detection-tuning table shows what each rule has been matching and what it costs.** Each row reports matches over the last 7 days, how many hosts they came from, and what the rule's evaluations cost. Read matches as volume rather than alerts, since repeated matches on one process collapse into a single alert. Both honor `EDR_RETENTION_DAYS` and start accumulating on upgrade.
- **A rule that is consistently too slow is dropped on the server that measured it.** One expensive rule used to slow detection for every rule on that server, with nothing to say which. It is now named in the logs and in a counter, and a restart gives it another chance.
- **Host health names each capture provider, and flags one that has stopped silently.** The content filter and the DNS proxy are listed in their own right. One reporting itself running but delivering no events for two hours on an active host shows as degraded, and clears once capture resumes.
- **Two alerts for a sensor that stopped capturing.** `EDR sensor disabled` (High) fires when a provider stops and does not return within 5 seconds; investigate it even once the host reads healthy, because the agent's automatic repair clears the warning without making the stop legitimate. `EDR sensor could not be restored` (Critical) fires once that repair is exhausted, and names the provider to re-enable.
- **A command can be cancelled, and one that waits too long is dropped instead of running late.** A command queued for an unreachable host used to run whenever the host came back, which is a real risk for Kill process since PIDs get reused. Undelivered commands now show as cancelled or expired rather than failed.

### Changed

- **The `dns_c2_beacon` rule is now called "Suspicious process phoning home".** It never detected beaconing: it looks for a program launched from a temporary or world-writable folder that resolves a domain and then connects to the address returned, with no measurement of periodicity. The identifier is unchanged, so exclusions, per-rule settings and historical alerts are unaffected.

### Fixed

- **Network-connection detections no longer miss fast activity on busy hosts.** Process events were stamped up to 0.7 seconds late under load, so a connection could appear to precede the process that made it and nothing fired. Hosts on an older agent improve too.
- **`Suspicious exec chain` now catches payloads it used to miss entirely.** A command run through `zsh -c` was invisible while the same command under `bash` or `sh` alerted, and a payload behind two layered shells was judged by the outermost, often too old for the rule's window.
- **Alerts are no longer dropped while the server is catching up on process data.** A backlogged server could discard a finding instead of retrying it, losing that alert permanently and silently; `Suspicious process phoning home` was the most exposed. Alerts missed before this upgrade are not backfilled.
- **A host whose events cannot be processed no longer stops reporting detections indefinitely.** A batch that failed the same way every time was retried forever, and every rule stopped seeing that host, indistinguishable from one gone quiet. Such a batch is now given up on and counted per host so you can alert on it.
- **Alerts name the right binary for a process that replaced its own image.** A process that spawned something and then ran a different binary could be named by the later one, and a rule looking for a suspicious parent could miss it. Kill process now also refuses a PID that was reused or re-executed.
- **A process's Network tab shows the connection its alert fired on.** Short-lived processes read "No network activity" even when an outbound connection was the reason for the alert. Connections also stop appearing under a different process that shared a PID.
- **Lowering a rule's severity no longer flattens the findings it had singled out.** `DNS beacon to a suspicious domain` rates a finding higher when the domain looks algorithmically generated, and your setting used to overwrite that. It now moves the whole rule and keeps the escalation on top.
- **Detection no longer slows down as you add servers.** Recording what each rule cost set a ceiling on how fast the fleet's events could be processed. That ceiling is gone. Separately, a batch that outlived its lease is no longer finished by two servers at once, which double-counted a rule's matches.
- **`fleet-edr-migrate` applies every MySQL migration, not all but one.** It reported success having skipped one component's tables, so a deployment that migrates as a privileged step and then starts the server without schema-change permission failed on start, with nothing to explain why.
- **Alerts a rule had already found survive a database outage.** When a lookup a rule depends on fails, the retry used to discard whatever that rule had already found, so a long enough outage lost those detections rather than delaying them.
- **Pasting a list of certificate hashes or paths into an application-control policy now works.** The paste-many flow labelled the `CERTIFICATE` and `PATH` rule types "coming soon" and refused any line it inferred as one, though the same rules could be created one at a time.
- **The process graph loads on hosts with a long history.** On a host carrying millions of process rows the graph failed outright, showing an error instead of the tree, because the server counted every matching row to report how many it was not showing. A window matching more than 10,000 now reports "more than 10,000" rather than failing.
- **The event timeline now says when it could not narrow to an alert's chain.** It can narrow only to processes carrying the generation identifier it matches on, so a four-process graph could sit beside a timeline of the whole host with nothing to explain the difference. It now says which of those you are looking at.
- **The ATT&CK coverage page names techniques and groups them by tactic.** It showed most as "Unmapped" with the bare technique id for a name, carrying a hand-maintained list of 12 techniques while the rules covered 65. It now reads from the published ATT&CK data, and the count of techniques covered only by silent rules links to where you tune them.
- **Smaller fixes.** The application control policy list shows how many rules each policy holds, instead of a dash for every one. Exporting a rule you have written over a shipped one returns your file. Submitting a rule reports only what concerns the rule you changed. `Dylib injection via DYLD_INSERT_LIBRARIES` understands options before the assignment, such as `env -i`, and no longer treats unsetting a variable as setting it, while `DYLD injection on exec` stops claiming a shell-assignment form the sensor cannot see. A process tree showing fewer processes than matched now says so. Alerts raised by a community rule before this release are credited to its author. The Mac-free demo's application-control alert cites a policy rule that exists, and its replayed processes carry the generation identifier an alert timeline needs.

## [0.4.0] (2026-07-07)

Feature release on top of 0.3.0. A re-architected event store adds fleet-wide hunting (process, connection, and DNS search), a per-host event timeline, and flexible time navigation across the investigation views. Also new: every service-account action is now fully attributable, agent health shows on the Hosts page, and the groundwork for Windows agents lands (no change for a macOS-only fleet). Read the upgrade notes before upgrading: `EDR_CLICKHOUSE_DSN` is now required and the `EDR_OIDC_*` SSO variables are removed.

### Upgrade notes (action required)

- **ClickHouse is now required.** Set `EDR_CLICKHOUSE_DSN` (or `EDR_CLICKHOUSE_DSN_FILE`) and run a ClickHouse instance alongside MySQL; the server and ingest service will not start without it. The reference compose stacks (`docker-compose.quickstart.yml`, `docker-compose.prod.yml`, and the multi-replica stack under `packaging/`) provision it for you. This is a hard cutover with no data migration: pre-upgrade event history is not carried over, though alerts and their evidence are preserved.
- **The `EDR_OIDC_*` variables and `EDR_AUTH_ALLOW_NO_OIDC` are removed.** Configure SSO under **Admin settings -> Single sign-on** (the in-product path since 0.3.0). Existing SSO keeps working: the configuration seeded on first boot under 0.3.0 remains the source of truth. Setting the old variables is now ignored, not an error.

### Added

- **Fleet-wide search.** A new **Search** page hunts across every host for processes (by host, path, SHA-256, uid, or signing verdict), outbound connections (by remote address), and DNS lookups (by domain). Results link back into the host's process tree, and the process detail panel gains one-click "search all hosts" pivots on a path, hash, IP, or domain.
- **Host event timeline.** The host page gains a **Timeline** tab beside the process graph: a filterable, newest-first list of the host's process, connection, and DNS events for the selected window, cross-linked with the graph in both directions. When opened for an alert, it scopes to the alert's process chain.
- **Flexible time navigation.** The fixed range buttons become one time control with relative quick-picks (up to 7 days) and an absolute from/to picker, plus an activity histogram of process starts over the window that you can click to zoom into a spike.
- **Richer process tree.** Hovering a node shows its full command line and a code-signing verdict (Apple platform, Developer ID with team, ad-hoc, signed, or unsigned; ad-hoc and unsigned are ringed amber). Repeated identical sibling executions collapse into a single `×N` node that expands on demand.
- **Host identity header.** The host page opens with hostname, online/offline status, OS, agent version, last seen, source IP, event count, and enrollment date, with the raw host id kept copyable for log correlation.
- **Inline MITRE ATT&CK technique tags.** Technique ids now appear where you investigate (process tooltips, the detail panel, and alert-triggering timeline rows), each linking to the rule's documentation.
- **Alert triage on the alert header.** An alert's status and its acknowledge / resolve / reopen controls live on the alert header, so an alert with no associated process can finally be triaged from its page.
- **Self-contained alert evidence.** Alerts capture their full triggering events at creation, so the evidence stays readable after those events age out of retention.
- **Agent health on the Hosts page.** Each host reports a health status (healthy / degraded / unhealthy) with per-component conditions, surfacing the previously invisible case of a host that enrolls but whose sensor never activates (it now reads "needs attention").
- **The account email is no longer shown in the top bar.** The account menu shows only an avatar initial until opened, so the signed-in identity is not passively disclosed on a screen-share.
- **Windows agent groundwork.** First steps toward Windows support: events carry an operating-system platform, the Hosts page shows a Platform column, detection rules are scoped per platform, and the Windows agent collects process start/stop through a driverless (user-mode ETW) sensor. Additive; nothing changes for a macOS-only fleet.

### Changed

- **The app now opens on Alerts.** Signing in lands on the alert list instead of the host list, matching the alert-first workflow; the host list moves to its own **Hosts** page and the navigation reorders to Alerts, Hosts, Application control, Coverage. Update any bookmark of the application root, which now opens Alerts.
- **Service-account actions are fully attributable.** Every operator action is recorded against a typed principal (a user or a service account), so a service-account change names the specific account instead of appearing anonymous. Service accounts can now manage detection exclusions and rule settings, and the audit log and per-row attribution identify the acting principal for every entry.
- **Events are stored in a ClickHouse archive.** Ingested events now live in ClickHouse (the new search, timeline, and correlation features read from it), with retention as ClickHouse-native time-based expiry. This is the change behind the now-required `EDR_CLICKHOUSE_DSN` in the upgrade notes.
- **Host identity stays current without a re-enroll.** The agent's periodic check-in now carries the host's hostname, OS version, and agent version, and the server refreshes the host record from it, so a macOS upgrade, rename, or agent upgrade shows in the console within about a minute instead of staying frozen at enrollment time.
- **Alert and host page refinements.** The alert page drops the self-referential "related alert" link and quiets controls that would do nothing: an empty "Show system" toggle is hidden, and the "Kill process" button is disabled (with the reason shown) once the process has exited. Agent health moves into the host header's Details popover, with an attention dot only when a host is not healthy. Host and alert lists show the enrollment hostname rather than the raw hardware UUID.
- **Oversize mutation requests return 413.** The application-control and detection-config write endpoints now reject a request body past their size cap with `413 Request Entity Too Large` instead of a misleading parse error.

### Fixed

- **Detection alerts are no longer lost to a processing race.** Under concurrent event processing, a rule could evaluate an event before the batch carrying that process's exec had committed, treat the lookup miss as "no finding," and permanently drop the alert with nothing logged. Rule evaluation now retries when an event's process is not yet materialized. Affects the sudoers-tamper, keychain-dump, launchd-persistence, DYLD-injection, shell-from-Office, osascript, suspicious-temp-exec, DNS C2 beacon, and application-control detections.
- **Re-processing a batch no longer duplicates processes or alerts.** A batch retry that replayed the same fork/exec/exit events could duplicate process-graph rows, and the alerts keyed on them; the graph builder is now idempotent under replay.
- **Alert graphs render correctly with repeated siblings.** An alerted process that had identical-path siblings could be folded into the collapsed `×N` node and vanish from its own chain, rendering a blank graph; it now stays a first-class node. The collapse chevron on an expanded sibling group also no longer shows a stale state during search.

## [0.3.0] (2026-06-26)

Feature release on top of 0.2.1. The headline is operator self-service: detection tuning, single sign-on, API service accounts, and user management all move from boot-time environment variables into governed, audited admin screens that apply without a server restart. Also in this release: a clearer Hosts page, sharper persistence-attribution in alerts, several telemetry-delivery and on-device DNS reliability fixes, and a simpler, safer configuration surface.

### Upgrade notes (action required)

This release retires a number of `EDR_*` environment variables in favor of in-product configuration. Review these before upgrading:

- **Detection tuning is no longer set by environment variables.** The `EDR_*` variables that defined false-positive allowlists and disabled rules are removed. Re-enter any exclusions and per-rule settings through the new Detection settings screen (see Added). This is a hard cutover: suppressions not re-entered will no longer apply.
- **SSO is now configured in-product.** The `EDR_OIDC_*` variables now only seed the configuration on first boot; after that, the stored configuration is the source of truth and is edited from the Single sign-on settings screen.
- **Database connection is configured only via `EDR_DSN` / `EDR_DSN_FILE`.** The separate `EDR_MYSQL_*` variables are removed.
- **22 rarely used tuning variables are removed** and fixed as safe constants. Setting a removed variable is ignored rather than failing boot. The minimum TLS version is now unconditionally TLS 1.3. All security, compliance, and documented operational levers are retained.

### Added

- **Detection tuning moves to a governed admin surface.** View and edit false-positive exclusions and set each detection rule to alert, monitor (evaluate quietly without raising alerts), or disabled, from an audited UI and API. Changes take effect without a server restart and are scoped per host group where applicable.
- **Single sign-on settings screen.** Configure your OIDC identity provider (issuer, client ID and secret, scopes, and default role), test the connection before saving, and apply changes without a server restart. Just-in-time provisioning is always on: anyone who signs in through the provider is auto-created with the default role.
- **API service accounts.** Create non-human principals so automation, CI/release pipelines, and integrations can call the EDR API with a short-lived OAuth client-credentials bearer token instead of a human's browser session. Each account is scoped to a single role, rotated, and revoked from an admin screen, and every lifecycle action and token issuance is audited.
- **In-product user management.** Promote and demote operator roles and enable or disable accounts from a Users screen instead of editing the database by hand, with anti-lockout guardrails (the last admin cannot be demoted, you cannot change your own role, and break-glass users are protected) and an audit row on every change.
- **MITRE ATT&CK coverage map.** A ready-to-share ATT&CK Navigator layer, scoped to the macOS techniques the product detects, ships in the repo for handing to a buyer, auditor, or SOC analyst.
- **Runtime trace-sampling controls.** Tune the server's trace-telemetry volume from an audited admin API without a restart: cap high-frequency agent traffic, lift sampling fleet-wide during an incident-debugging window, and drop health-probe noise entirely. Aggregate latency and alerting keep reading from metrics, which are never sampled, so dialing traces down does not blind your monitoring.

### Changed

- **Redesigned Hosts page.** The page leads with a fleet summary (Online, Offline, and Total host counts) and identifies each machine by hostname and OS version rather than only its raw hardware UUID, so operators can recognize machines at a glance.
- **Single VM is the recommended deployment.** Stand up MySQL, the server, and a Caddy reverse proxy with automatic Let's Encrypt TLS on one operator-controlled VM with a single command. Managed-PaaS edges that run a content-inspecting WAF can silently block agent telemetry, so the supported getting-started path is one where you control the edge.
- **Reduced telemetry bandwidth and on-device overhead.** The agent now compresses uploads (gzip), cutting upload bandwidth several-fold, and repetitive network-connection and DNS-lookup events are coalesced into a single counted event before upload while preserving detection signal. On-device application-control allow decisions are cached at the kernel with safe invalidation on policy change, reducing CPU overhead during heavy process activity.
- **Smaller database footprint.** Per-process liveness heartbeat events (previously a large share of stored rows) are processed for freshness but no longer persisted, and two redundant indexes on the events table are dropped online with no downtime.
- **Fewer false positives from the suspicious-execution rule.** Parent-process allowlist entries now support `*` wildcards so a suppression survives a tool version upgrade, and a lookup to the host's own local DNS resolver no longer counts as a triggering connection. Lookups to public resolvers still fire.
- **Application-control policy changes apply reliably on large fleets.** Saving an application-control policy now pushes the new rules to assigned hosts in batched database writes instead of one write per host, so a change across hundreds or thousands of endpoints commits well inside the request budget instead of risking a timeout and a stuck save.

### Fixed

- **On-device DNS no longer wedges name resolution.** DNS forwarding now has timeouts and a self-healing watchdog that fails open (resolution keeps working) when no blocking policy is active, while still enforcing any active blocks. This fixes an incident where the DNS path could break all name resolution on an endpoint until a reboot, and the break-glass disable command no longer hangs.
- **Clear reboot prompt after a package upgrade.** Network and DNS coverage can stop after upgrading the installed package until the endpoint reboots; the product now surfaces an explicit "reboot required to restore network and DNS coverage" signal instead of an ambiguous warning.
- **Telemetry is retried, not discarded, when the edge rejects uploads.** When a proxy, WAF, edge, or unhealthy origin blanket-rejects uploads (for example with a 403), the agent keeps that telemetry queued and retries until the endpoint recovers, and emits a loud warning and metric so operators see the misconfiguration.
- **Alerts not tied to a single live process now explain themselves.** Persistence and similar alerts no longer open into a blank process graph; they show the alert description and MITRE technique tags with a clear explanation and an opt-in to widen to surrounding host activity.
- **More accurate exclusions and forensic attribution.** Detection exclusions now match correctly regardless of the macOS `/private` path form, so an allowlist you write takes effect; in multi-server deployments every replica picks up detection-config changes within seconds instead of serving stale config; and a network or DNS event from a process that re-launches itself is attributed to the correct generation in the alert timeline.
- **More accessible admin UI status messages.** The single sign-on connection-test result and the account-menu authentication-method badge now meet the WCAG AA color-contrast minimum, and the SSO status banners are emitted as semantic `<output>` live regions so assistive technology announces them.

### Removed

- **Render deployment support.** The one-click Render blueprint (`render.yaml`) and its guide are removed. Render's managed edge runs a content-inspecting WAF that blocks agent telemetry by default and cannot be disabled by the customer, so events silently fail to upload. Use the single-VM quickstart ([docs/quickstart-vm.md](docs/quickstart-vm.md)) instead, where you control the edge.

## [0.2.1] (2026-06-16)

Patch release on top of 0.2.0. Fixes the Mac-free Docker demo (`docker-compose.demo.yml`) so it presents correctly to evaluators. No agent or server runtime behavior changes; the fixes are confined to the demo seeder.

### Fixed

- **Demo process view stays populated across restarts.** On a restart against the persisted demo volume the seeder now slides the seeded timestamps forward, so the host process graph still falls inside the UI's default one-hour window instead of aging out and rendering empty. The shift is scoped to the demo's own hosts.
- **Demo alerts show a realistic process chain.** Woven attack scenarios are now re-parented under the captured host's interactive shell session instead of rooting directly at launchd, so an alert's process tree shows the full ancestry (for example `sshd -> zsh -> /usr/bin/security`) the way a real detection would.

## [0.2.0] (2026-06-16)

Incremental release on top of 0.1.1. Highlights: release signing modernized to the cosign v3 Sigstore bundle format, a detection-pipeline stall fixed, and the macOS system extensions now show recognizable names during a manual install.

### Added

- **Recognizable system-extension names.** The Endpoint Security and Network extensions now appear in macOS System Settings (Login Items & Extensions, Full Disk Access) as "Fleet EDR Security Extension" and "Fleet EDR Network Extension" instead of the generic "extension" / "networkextension", so an operator doing a manual (non-MDM) install can tell which Full Disk Access entry belongs to Fleet EDR.
- **Process-graph retention pruning.** The server prunes the process table on the configured retention window, bounding storage growth on long-running deployments.

### Changed

- **Modernized release signing (cosign v3).** Release artifacts are now signed as a single Sigstore bundle (`<file>.sigstore.json`) per file, and the server and demo-seed container images store their signatures and SBOM attestations as OCI 1.1 referring artifacts. Verify a download with `cosign verify-blob --bundle <file>.sigstore.json --certificate-identity-regexp ... --certificate-oidc-issuer https://token.actions.githubusercontent.com <file>`.
- **Pinned MySQL to 8.4.9** in the reference deployment stack.
- Updated UI build dependencies (esbuild, Vite, `@vitejs/plugin-react`).

### Fixed

- **Detection-pipeline stall on out-of-range UID/GID.** The process `uid`/`gid` columns are now `INT UNSIGNED`, so the macOS `nobody` account and the `KAUTH_UID_NONE` sentinel no longer overflow on insert; a single unpersistable event is isolated rather than wedging the whole detection pipeline.
- **Session-expiry handling.** The web UI redirects cleanly when a session expires instead of rendering affordances that then fail with 403.
- **OpenTelemetry noise.** Trimmed spurious telemetry output and tightened span/log scoping.

### Security

- **Hardened host enrollment tokens.** Host tokens are now verified with a keyed HMAC derived from a root secret.

### Observability

- Emit a `deployment.environment` resource attribute and scope the bundled SigNoz dashboards to it; added a dashboard filter plus render and metrics tuning.

### Documentation

- Tightened the install-server, MDM, and manual-agent deployment guides; led the README with deployment; documented the supported-version policy.

## [0.1.1] (2026-06-13)

First stable release. The product ships as two components, released together for now: the **macOS agent** (Apple Silicon, macOS 26+) and the **server** (a Linux container that runs anywhere containers do, cloud or on-prem). Core capabilities:

- **Real-time macOS endpoint monitoring.** An Endpoint Security system extension and a network extension capture process execution, fork/exit, file access, DNS queries, network connections, and background-task (launch item) registration, streamed continuously to the server.
- **Live process-graph correlation.** The server reconstructs a per-host process tree, including parent/child lineage and re-exec chains, so every alert carries the full process ancestry that led to it.
- **Out-of-the-box detection rules.** A catalog of behavioral detections covering credential access (keychain dumping), persistence (launch agents and daemons), privilege escalation (sudoers and launchd tampering), process injection (DYLD insertion), command-and-control beaconing, and suspicious execution chains.
- **Cross-stream correlation.** Rules reason over execution, DNS, and network together, catching multi-step behavior a single-signal tool misses, such as a process that beacons to a suspicious domain and then connects out.
- **Application control.** Block execution on the endpoint by binary hash, path, CDHash, signing ID, team ID, or leaf certificate, enforced at exec time before the process runs.
- **Process response.** Kill a running process on a host on demand from the server.
- **Tunable allowlists.** Per-rule allowlists suppress known-good management tooling, such as MDM agents that drop launch daemons or write sudoers, so legitimate activity does not generate noise.
- **Operator web UI.** Browse hosts, drill into process trees, triage and comment on alerts, and manage application-control policy.
- **SSO and break-glass authentication.** OIDC single sign-on with just-in-time user provisioning, plus a WebAuthn passkey break-glass path for recovery when SSO is unavailable.
- **Role-based access control.** Five built-in roles (super admin, admin, senior analyst, analyst, auditor) enforced through a single authorization chokepoint that no privileged action can bypass.
- **Append-only audit log.** Every privileged action and authorization decision is recorded immutably for later review.
- **Offline-tolerant agent.** A durable on-device SQLite queue buffers events when the server is unreachable and uploads them on reconnect, so nothing is lost during an outage.
- **Built to scale.** A stateless server runs as multiple replicas behind a load balancer, drains gracefully on deploy, applies schema migrations automatically, and enforces configurable event retention.
- **Flexible deployment.** The server is a standard Linux container image, so it runs on any container host (a Docker VM, Kubernetes, AWS ECS/EKS, GCP, Azure, or on-prem), with a one-click Render blueprint for the fastest start. Agents reach Macs through any MDM (Fleet, Jamf, Kandji, Intune, mosyle).
- **Supply-chain-hardened releases.** Every release ships a Developer ID-signed, Apple-notarized package alongside SBOMs, cosign signatures, and build provenance attestations.

[0.7.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.7.0
[0.6.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.6.0
[0.5.1]: https://github.com/getvictor/fleet-edr/releases/tag/v0.5.1
[0.5.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.5.1
[0.4.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.4.0
[0.3.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.3.0
[0.2.1]: https://github.com/getvictor/fleet-edr/releases/tag/v0.2.1
[0.2.0]: https://github.com/getvictor/fleet-edr/releases/tag/v0.2.0
[0.1.1]: https://github.com/getvictor/fleet-edr/releases/tag/v0.1.1