Surface the existing "Certificates" card on the host details page for
Windows hosts, with parity to macOS. Requires osquery 5.23.1 or higher.
<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves#31294
Demo video: https://www.youtube.com/watch?v=kGRp-YtnnJc
Docs: https://github.com/fleetdm/fleet/pull/48493/changes
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [x] QA'd all new/changed functionality manually
## Database migrations
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
* **New Features**
* Windows host certificates now display on the host details page (gated
by minimum agent/osquery version), including scope (**System** vs
**User**) and improved scope-aware certificates list details.
* **Bug Fixes**
* Certificate table labeling and help text are now platform-appropriate
(with “Keychain” renamed to “Scope”).
* Windows certificate reconciliation is more resilient, preserving
certificates for scopes not observed during a collection run and
preventing row collapsing when ids repeat across scopes.
* **Tests**
* Expanded coverage for Windows/malformed DN parsing and scope-aware
reconciliation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#48530
Fixes wrong platform-title capitalization in the "Filtered" tag tooltip
on the Hosts and Vulnerabilities dashboard charts.
# Checklist for submitter
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved chart tooltip text for host/platform filters, ensuring
branded platform names keep correct capitalization and combined lists
use correct punctuation.
* Refined chart tooltip/platform display mapping to avoid incorrect
capitalization variants.
* **New Features**
* Updated browser chart category labeling to show “Browsers” with
consistent capitalization.
* **Tests**
* Expanded ChartCard tests to validate platform tooltip line formatting
for single and multiple selections.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Moves opening context paragraphs in several articles to follow the key
takeaways section for a more consistent structure. Also refines a few
takeaway phrases for clarity and updates calls to action, including
linking the Managed Migration Assistant article to the setup guide and
simplifying the closing CTA in the config-as-code article.
Fixes data race detected in
https://github.com/fleetdm/fleet/actions/runs/28769705097/job/85300822820.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved reliability of log delivery by ensuring buffered log data is
copied before being sent, preventing intermittent issues when batches
are processed.
* Reduced the risk of log entries being corrupted or lost during
transmission.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves#45650
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
* **Bug Fixes**
* Improved MySQL migration handling for MDM command results by safely
removing an outdated foreign key when present, preventing issues during
upgrade and re-run scenarios.
* Updated the database schema definition to keep related response
foreign key behavior consistent.
* **Chores**
* Added the latest migration version to the migration status seed data
to ensure version tracking stays in sync.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves #
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Added a new customer testimonial highlighting Fleet’s device
management experience, including the customer quote, role, and profile
link.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Eric <eashaw@sailsjs.com>
- [x] QA'd all new/changed functionality manually
- Enrollment methods: ADE, profile-based manual, Managed Apple Account
- Also, update Controls > Setup experience copy. "Automatically" only
applies to macOS:
<img width="505" height="168" alt="Screenshot 2026-07-02 at 9 41 17 AM"
src="https://github.com/user-attachments/assets/ff46682f-e87b-4d2e-9889-7d0382b0d3d5"
/>
- Every other platform:
<img width="517" height="174" alt="Screenshot 2026-07-02 at 9 41 31 AM"
src="https://github.com/user-attachments/assets/0ab115fb-76fb-4080-afb4-52edcf4900b7"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Updated the setup guidance text for platform-specific enrollment so
macOS now shows “automatically enroll to Fleet.”
* Other platforms continue to show “enroll to Fleet,” improving clarity
in the install experience.
* **Tests**
* Adjusted the Android empty-state test to match the updated enrollment
wording.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** NA
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] QA'd all new/changed functionality manually
---
Brings `articles/control-apple-beta-programs-with-ddm.md` up to the
current article format:
- Moved the "Key takeaways" section and CTA button directly under the
dek, ahead of the intro, so the full argument sits at the top of the
page.
- Reworded two takeaways ("Beta control is no longer all-or-nothing" and
the token-fetching bullet) that echoed body sentences nearly verbatim —
takeaways now preview the body instead of repeating it.
No changes to metadata, links, or the substance of the article.
@headmin can you give this a review? The intention was not to rewrite
your article but to have it follow the new article format.
---
Reframes the opening to focus on operational cost and team dependency
tradeoffs, adds a new "Key takeaways" section, and inserts an early CTA
link. Also normalizes capitalization in the closing demo bullet for
consistency.
Fixes:
https://github.com/fleetdm/fleet/actions/runs/28775213423/job/85317520154.
New run: https://github.com/fleetdm/fleet/actions/runs/28779783653.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Added vulnerability status information for a known issue affecting
`fleetctl`, clarifying that it is not impacted by the reported problem.
* Included a note explaining why the affected code path is not reached
in typical use.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#45710
# Checklist for submitter
- [x] Changes file added (`changes/45710-zorin-os-support`).
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops.
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes.
## Testing
- [x] Added/updated automated tests —
`server/vulnerabilities/oval/oval_platform_test.go` extended with Zorin
→ Ubuntu LTS mapping cases (16/17/18) plus an unknown-version case
(`Zorin OS 99` → `zorin_99`, which `IsSupported()` rejects).
- [x] QA'd all new/changed functionality manually — Zorin OS 17.0 and
18.1 hosts enrolled against a patched Fleet server, host details show
`platform=zorin`, software inventory populates, and OVAL CVE matching
produces results against the corresponding `ubuntu_2204` / `ubuntu_2404`
feeds.
## Database migrations
- N/A. No schema changes.
## New Fleet configuration settings
- N/A. No new settings.
## fleetd/orbit/Fleet Desktop
- N/A. Server + frontend only; no fleetd/orbit changes.
---
## Summary
Fleet previously logged `unrecognized platform` for Zorin OS hosts
(osquery reports `platform=zorin` from `/etc/os-release` `ID=zorin`).
The common workaround was running osquery with
`--force_platform=ubuntu`, which masquerades the host. This change adds
`zorin` as a first-class Linux platform alongside Ubuntu:
- **`server/fleet/hosts.go`** — register `zorin` in `HostLinuxOSs` and
`HostDebPackageOSs`
- **`server/datastore/mysql/linux_mdm.go`** — include Zorin in the Linux
disk-encryption summary query
- **`server/vulnerabilities/oval/oval_platform.go`** — map Zorin major
version to the underlying Ubuntu LTS OVAL feed (16 → 20.04, 17 → 22.04,
18 → 24.04). Unknown future versions fall through to an unsupported
`zorin_<major>` identifier so vulnerability scanning is skipped rather
than served stale data from an aging LTS feed.
- **frontend** — add `zorin` to `HOST_LINUX_PLATFORMS`, the
disk-encryption support list and type guard, the label platform
dropdown, and the icon mapping (Ubuntu icon, since no Zorin-specific
asset exists in the repo).
No new dependency, schema migration, or config setting. Reuses existing
Ubuntu OVAL feeds and the existing Ubuntu icon.
Diff is ~30 lines net across 9 files (8 patched + 1 `changes/` file).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added Zorin OS as a supported Linux platform.
* Zorin hosts included in Linux disk-encryption summaries and treated as
disk-encryption capable.
* Zorin OS available as a selectable/filterable platform label and
considered DEB-install compatible.
* Vulnerability scanning enabled for Zorin 16→Ubuntu 20.04, 17→22.04,
18→24.04; unknown/future Zorin versions are marked unsupported and
skipped for CVE matching.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45712?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Reworks the article opening with a stronger headline, intro framing, and
a new key takeaways section plus CTA. It also tightens product language
(agent/query-based verification, Premium benchmark availability),
simplifies the “what changes” section into a clearer narrative, and
updates metadata by moving the category from `security` to `articles`.
**Related issue:** N/A
New article: "Take control of Apple beta programs with declarative
device management."
Covers the `com.apple.configuration.softwareupdate.settings` DDM
declaration for controlling Apple beta program enrollment, automating
AppleSeed for IT token retrieval from Apple Business Manager using the
Microsoft/HCS `betaTokens.sh` script, and deploying the declaration
through Fleet's custom OS settings (UI upload or GitOps) with
label-based scoping.
Follows the Fleet article format: dek, key takeaways above the fold,
post-takeaways CTA button, and closing "See it live" block.
Notes for reviewers:
- Verified `com.apple.configuration.softwareupdate.settings` is not in
Fleet's `ForbiddenDeclTypes` (`server/fleet/apple_mdm.go`), so the
"deploy with Fleet" section is accurate.
- CTA routes (`/try-fleet`, `/gitops-workshop`,
`/guides/custom-os-settings`) verified against
`website/config/routes.js`.
- Please click-check the Apple developer docs link for the
`SoftwareUpdateSettings` schema.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Article-only change; no changes file, tests, migrations, or config
changes needed.
---------
Co-authored-by: Harrison Ravazzolo <38767391+harrisonravazzolo@users.noreply.github.com>
Adds WinDirStat to maintained apps for Windows by introducing the winget
input metadata, generated app output entries, and installer/uninstaller
version data. Also adds the WinDirStat icon asset and wires it into the
SoftwarePage icon map so the app displays with the correct branding in
the UI.
- Clarified the absence of a Refetch button for Android hosts
- Removed "Sync policies" header because the term policies could be
confusing. After that, just decided to remove the headers because the
article is short.
- Piece of the following request:
https://github.com/fleetdm/fleet/issues/42755
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Updated the Windows release metadata for Cursor to version 3.10.11,
including the download link and checksum.
* Updated the Windows release metadata for DBeaver Community to version
26.1.2, including the download link and checksum.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
**Related issue:** Resolves#46235
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
## Testing
- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually: Pending if possible
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed DEP sync so progress is only saved after device data is written
successfully, preventing missed enrollment events during interrupted
syncs.
* Improved handling of sync errors so the next run can safely replay
affected devices instead of skipping them.
* Added end-to-end and scenario coverage to verify cursor behavior after
successful syncs, errors, and expired cursors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#48277
## What & why
Running a live report/query (or live policy) on some machines throws a
full error page once results start streaming in:
```
NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
```
**Root cause:** Chrome's built-in auto-translate (Google Translate)
wraps text nodes in `<font>` elements, detaching the original DOM nodes
React holds references to. As live results stream in over the websocket,
table cells (and the responded-count heading) unmount rapidly; React
then calls `parentNode.removeChild(node)` on a node Translate has
already moved, throwing `NotFoundError`. The app's error boundary
catches it and renders the error page (facebook/react#11538).
This is why it's machine-dependent: it only reproduces when Chrome is
translating the page. It surfaces on live results specifically because
that's one of the few surfaces that unmounts DOM rapidly while
displaying translatable text.
**Fix:** Exclude the live-results subtrees from translation via the
`notranslate` class on the top-level containers of `QueryResults` (live
report/query) and `PolicyResults` (live policy). These wrappers cover
the `LiveResultsHeading` counts, the results table, and (policy) the
errors table. Translation stays enabled everywhere else in the app.
## How to reproduce / QA
1. In Chrome, enable translation of the live results page (right-click →
Translate to English, or set a non-English preferred language so the
Translate banner activates and choose "Always translate").
2. Run a live query/report or live policy targeting several hosts so
results stream in.
3. Before the fix: error page appears after a few results. After the
fix: results render normally.
# Checklist for submitter
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [x] QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
- [x] Confirmed that the fix is not expected to adversely impact load
test results
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Prevented browser auto-translation from modifying live results areas
in the policy and query editors.
* Reduces the chance of display errors during live streaming/remounting
of results.
* Addressed a related Google Translate browser extension issue that
could lead to incorrect live-run behavior.
* **Chores**
* Added a changelog entry for the live results translation fix.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Fully resolves
https://github.com/fleetdm/fleet/issues/34591.
## Testing
- [X] QA'd all new/changed functionality manually.
<img width="533" height="454" alt="Screenshot 2026-07-03 at 10 40 37 AM"
src="https://github.com/user-attachments/assets/892fb548-21c6-467c-b270-65f1c9338fdc"
/>
<img width="1287" height="259" alt="Screenshot 2026-07-03 at 10 41
55 AM"
src="https://github.com/user-attachments/assets/d3528b0c-0d05-4ace-8512-ab363241b97c"
/>
<img width="1077" height="123" alt="Screenshot 2026-07-03 at 10 41
46 AM"
src="https://github.com/user-attachments/assets/249e80de-320c-48f3-962a-59c98c736c54"
/>
<img width="725" height="208" alt="Screenshot 2026-07-03 at 10 41 32 AM"
src="https://github.com/user-attachments/assets/361764cf-26fc-4a44-b5d6-489d883a392b"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added CachyOS Linux to rolling-release OS detection and reporting.
* Added a CachyOS fleetd package/image variant and a new CachyOS fleetd
service for local testing.
* **Bug Fixes**
* Improved rolling-release OS version labeling for host “Vitals”
display.
* Updated OS inventory normalization so CachyOS is aggregated with Arch
Linux, including correct “rolling” version handling.
* **Tests**
* Expanded OS version ingest test coverage for rolling-release and
CachyOS scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#39323
Missed updating [the case of a
word](https://github.com/fleetdm/fleet/issues/39323#issuecomment-4890263095)
in #47603.
# Checklist for submitter
## Testing
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Updated the query description field help text to use consistent
capitalization (“optional” instead of “Optional”).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Related to: #39962
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added pre-install checks to prevent several fleet-managed app installs
from starting while the apps are already running (including Slack and
Zoom, plus others).
* Enabled automatic update installation with continuous automations for
a broad set of macOS and Windows apps, including Chrome, 1Password,
Slack, Zoom, and Visual Studio Code.
* **Bug Fixes**
* Updated update guidance to direct users to Self-service for the latest
versions, reducing conflicting “built-in update” instructions when apps
are already running.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Allen Houchins <allenhouchins@mac.com>
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Updated macOS release metadata for several apps to their latest
versions, including DBeaver Community, DockDoor, Marked, OnlySwitch,
OpenRCT2, Spokenly, and Tunnelblick.
* Refreshes download links and checksums so installers point to the
newest available releases.
* **Bug Fixes**
* Improved patch detection logic to match the updated app versions,
helping keep upgrade checks accurate.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Updated the macOS app entries for Clop and Stats to the latest
available versions.
* Refreshed download links and checksums so installs point to the
correct release artifacts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Updated release metadata for several maintained apps to newer
versions, including BetterMouse, Beyond Compare, Clop, CodexBar,
DaisyDisk, and Kiro.
* Refreshed installer links and checksums so the latest available
packages are detected and installed correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
Replace the hardcoded FLEET_MYSQL_MAX_OPEN_CONNS=10 in the loadtest
terraform (both the root ecs.tf stack and the infra/ stack) with a
mysql_max_open_conns variable, applied to both the writer and
read-replica pools.
Also document per-tier connection pool sizing in the AWS reference
architecture: 20 for R-class instances (25k+ hosts), 10 for the smaller
T-class instances (5k/10k hosts) whose low default max_connections
(~90/135) leaves little headroom.
<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves#44802
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a configurable setting for MySQL connection limits in the load
testing environment.
* Connection settings now use a shared input value instead of fixed
defaults, making it easier to tune database load behavior across
container types.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Refreshed release metadata for multiple maintained apps across macOS
and Windows by updating versions, installer download links, and
integrity checks so the latest installers are recognized.
* **New Features**
* Added an Egnyte Windows installer script that runs the MSI with
detailed logging and treats reboot-required outcomes as successful,
ensuring required follow-up behavior occurs on the next reboot.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
Co-authored-by: Allen Houchins <allenhouchins@mac.com>
Related to: https://github.com/fleetdm/fleet/issues/48052
Changes:
- Added a JSON `pageFaqForSeo` to the device management page's view
action
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added schema.org FAQ structured data to the device management page to
improve search visibility and enable richer search results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#48697
## Summary
The hourly `UpdateQueryAggregatedStats` cron job currently walks **every
query ID** in the `queries` table and runs 5 expensive
percentile-calculation queries per query against
`scheduled_query_stats`, plus 1 INSERT/UPDATE to store results. Most
queries have no execution data at all (they are saved queries,
live-only, or de-scheduled), so this work is pure waste.
This PR changes the cron to only process queries that actually have
execution data, by querying `scheduled_query_stats` directly instead of
the `queries` table. The now-unused `walkIdsInTable` helper function is
also removed.
### How the calculations work
`CalculateAggregatedPerfStatsPercentiles` computes performance
statistics for each query that has been scheduled and executed by hosts.
For each qualifying query ID, it runs these operations against the read
replica:
1. **P50 user_time** -- Calculates the median (50th percentile) of
per-host average user-mode CPU time. The query groups
`scheduled_query_stats` rows by `host_id`, computes `SUM(user_time) /
SUM(executions)` per host, sorts them, then picks the row at position
`FLOOR(total_rows * 0.5) + 1` using a `@rownum` session variable.
2. **P95 user_time** -- Same calculation but picks the 95th percentile
row (`FLOOR(total_rows * 0.95) + 1`).
3. **P50 system_time** -- Same percentile calculation for kernel/system
CPU time.
4. **P95 system_time** -- 95th percentile of system CPU time.
5. **Total executions** -- `SELECT COALESCE(SUM(executions), 0) FROM
scheduled_query_stats WHERE scheduled_query_id = ?`
6. **INSERT/UPDATE** -- Writes the JSON result (`user_time_p50`,
`user_time_p95`, `system_time_p50`, `system_time_p95`,
`total_executions`) into the `aggregated_stats` table via `INSERT ... ON
DUPLICATE KEY UPDATE`.
### What changed
**Before:** `SELECT id FROM queries` -- walks every query (200-400+ in a
typical deployment).
**After:** `SELECT DISTINCT scheduled_query_id FROM
scheduled_query_stats WHERE executions > 0` -- walks only queries that
have actual execution data (typically 10-20).
### Benchmark results (MySQL 8.0, 300 queries seeded, only 15 with
stats)
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Avg time per cron run | 3.36s | 0.28s | **12.2x faster** |
| DB operations per run | 1,800 | 90 | **95% fewer** |
| DB operations per day | 43,200 | 2,160 | **41,040 eliminated** |
| `aggregated_stats` rows written | 300 (285 empty) | 15 (all
meaningful) | Less table bloat |
| Correctness | baseline | byte-identical JSON | **Zero regression** |
At 500+ queries the current approach **drops MySQL connections**
(`unexpected EOF` / `invalid connection`) because the cursor is held
open across thousands of heavy serial queries. The optimized version
handles any scale trivially.
### Impact analysis
Verified safe across all consumers: all query endpoints use `LEFT JOIN
aggregated_stats` (NULL-safe for missing rows), the frontend explicitly
handles null stats as "Undetermined", live query stats
(`service_campaigns.go`) call `CalculateAggregatedPerfStatsPercentiles`
directly and are unaffected, and query deletion already cleans up both
`scheduled_query_stats` and `aggregated_stats` rows.
# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/`
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] Added/updated automated tests
- [x] Confirmed that the fix is not expected to adversely impact load
test results
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolves#47241.
- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [X] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Better host policy results by automatically cleaning up out-of-scope
`policy_membership` records.
* Refreshes host failing-policy counts after cleanup, including when
distributed writes report “no policies in scope.”
* Preserves existing safeguards by skipping this cleanup during
setup/initial configuration to prevent premature updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fixes the `device_mapping` ordering flake in
`TestIntegrations/TestListHostsByLabel` and
`TestIntegrations/TestHostsReportDownload`, seen across integration-core
jobs on `main` since July 1 (e.g. [this
run](https://github.com/fleetdm/fleet/actions/runs/28567132474)).
#48488 replaced the derived-table `GROUP_CONCAT` join in `ListHosts`
with a correlated subquery, but the `GROUP_CONCAT` has no `ORDER BY`, so
MySQL returns `device_mapping` entries in arbitrary order. The old plan
happened to read `idx_host_emails_host_id_email` in index order, which
masked this; the new access path doesn't, so the order now varies
between endpoints and runs — the tests compare `GET /hosts` output
against `GET /labels/{id}/hosts` (and CSV report) output for the same
host and intermittently see `[b@b.c, a@b.c]` vs `[a@b.c, b@b.c]`.
This adds `ORDER BY he.email, he.source` inside the `GROUP_CONCAT`,
matching the ordering of the single-host `listHostDeviceMappingDB`
query. The sort applies only within each host's few email rows, so it
doesn't affect the perf improvement from #48488.
No changes file: #48488 is unreleased (not in any RC branch), so this is
a fix to an unreleased change.
# Checklist for submitter
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
Covered by existing tests: `TestIntegrations/TestListHostsByLabel` and
`TestIntegrations/TestHostsReportDownload` assert the (now
deterministic) ordering. Ran both 4× locally with `MYSQL_TEST=1
REDIS_TEST=1`, plus the `TestHosts` device-mapping/ListHosts datastore
tests — all green.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved consistency of host device mapping results by making the
ordering deterministic.
* Fixed an issue where device mapping entries could appear in different
orders between requests.
* **Tests**
* Added coverage to verify the device mapping order remains stable and
predictable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Related issue:** Resolves#25574
# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/`
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
---
## Summary
- Adds a new `splunk` log plugin that sends osquery logs directly to
Splunk's HTTP Event Collector (HEC) endpoint
- Eliminates the need for middleware like AWS Firehose when using Splunk
as a log destination
- Follows the same pattern as existing log destinations (Firehose, Kafka
REST, NATS, etc.)
- Includes `insecure_skip_verify` option for environments with
self-signed TLS certs
## UI changes
Follows the same pattern as the NATS log destination PR (#36527) --
adding "Splunk" to the display name, tooltip, and TypeScript type union.
No new components, pages, or styles.
### Manage automations modal -- "Log destination: Splunk"
<img width="822" height="527" alt="image"
src="https://github.com/user-attachments/assets/2533207f-fa95-4364-8ee0-3c39cd3e8e4d"
/>
### Query details page -- "Log destination: Splunk"
<img width="1905" height="662" alt="image"
src="https://github.com/user-attachments/assets/069a5005-f95c-4562-a819-fd8bdcc349f7"
/>
### Tooltip on hover
<img width="639" height="348" alt="image"
src="https://github.com/user-attachments/assets/809a47a6-b82a-4f45-b731-77b2d2c87947"
/>
### Edit query form -- "sent to your log destination: Splunk"
<img width="451" height="814" alt="image"
src="https://github.com/user-attachments/assets/b78b9a57-1f0c-4413-8b7c-654de1fd40a2"
/>
### Save new query modal -- "sent to your log destination: Splunk"
<img width="536" height="698" alt="image"
src="https://github.com/user-attachments/assets/d0a0ab01-66fe-4d63-9190-9c5e840e456d"
/>
---
### How it works
The Splunk writer (`server/logging/splunk.go`) implements the
`fleet.JSONLogger` interface. On startup it performs a health check
against the HEC `/services/collector/health` endpoint. On each `Write()`
call, it wraps each log entry in Splunk's HEC event format (adding
`time`, `index`, `source`, `sourcetype`), batches them up to 1 MB, and
POSTs to `/services/collector/event` with the `Authorization: Splunk
<token>` header. If a batch exceeds 1 MB it flushes and starts a new
one. Events over 1 MB are dropped with a log warning. Transient errors
(HTTP 503) are retried with exponential backoff (up to 8 retries).
### Configuration
```yaml
osquery:
status_log_plugin: splunk
result_log_plugin: splunk
splunk:
url: https://splunk.example.com:8088
token: <HEC token>
index: main
source: fleet
source_type: fleet:json
insecure_skip_verify: false # set true for self-signed certs
```
Or via environment variables:
```
FLEET_OSQUERY_STATUS_LOG_PLUGIN=splunk
FLEET_OSQUERY_RESULT_LOG_PLUGIN=splunk
FLEET_SPLUNK_URL=https://splunk.example.com:8088
FLEET_SPLUNK_TOKEN=<HEC token>
FLEET_SPLUNK_INDEX=main
FLEET_SPLUNK_SOURCE=fleet
FLEET_SPLUNK_SOURCE_TYPE=fleet:json
```
### Files changed
- `server/logging/splunk.go` -- Splunk HEC log writer with batching,
retry, and health check
- `server/logging/splunk_test.go` -- 9 unit tests
- `server/logging/splunk_integration_test.go` -- 3 integration tests
against real Splunk (gated by env var)
- `server/logging/logging.go` -- Added `SplunkConfig` and `case
"splunk"` to factory
- `server/config/config.go` -- Added `SplunkConfig` struct and config
flags
- `cmd/fleet/logging.go` -- Wired Splunk config into logging builder
- `server/fleet/app.go` -- Added `SplunkConfig` type for API responses
(excludes token)
- `server/service/service_appconfig.go` -- Added `case "splunk"` to
logging plugin validation
- `frontend/interfaces/config.ts` -- Added `"splunk"` to LogDestination
type
-
`frontend/components/LogDestinationIndicator/LogDestinationIndicator.tsx`
-- Added Splunk display name and tooltip
- `docs/Configuration/fleet-server-configuration.md` -- Splunk config
documentation
- `docs/Get started/FAQ.md` -- Updated plugin list
- `articles/log-destinations.md` -- Updated Splunk section with native
HEC docs
- `changes/25574-splunk-log-destination` -- Change file
## Test plan
### Unit tests (9 tests)
- [x] `TestSplunkWrite` -- sends 3 events, verifies HEC format, auth
header, index/source/sourcetype
- [x] `TestSplunkWriteEmpty` -- empty logs don't trigger HTTP request
- [x] `TestSplunkServerError` -- HEC 403 propagates as error
- [x] `TestSplunkHealthCheckFailure` -- constructor fails on bad health
- [x] `TestSplunkRecordTooBig` -- oversized events (>1MB) are dropped,
normal events still sent
- [x] `TestSplunkSplitBatchBySize` -- logs exceeding 1MB batch limit are
split into multiple requests
- [x] `TestSplunkRetryOnServiceUnavailable` -- 503 retried with backoff,
succeeds on 3rd attempt
- [x] `TestSplunkRetryExhausted` -- after 9 attempts (1 + 8 retries)
returns error
- [x] `TestSplunkMissingConfig` -- empty URL/token returns descriptive
error
### Integration tests (3 tests, gated by `SPLUNK_INTEGRATION_TEST=1`)
- [x] `TestSplunkIntegration` -- 3 events sent via writer, queried back
from Splunk REST API
- [x] `TestSplunkIntegrationBatch` -- 100 events in one Write(), all
confirmed indexed
- [x] `TestSplunkIntegrationBadToken` -- bad token Write() returns 403
### End-to-end test (macOS ARM64, real osquery agent)
1. Started Splunk Enterprise, MySQL, Redis via Docker
2. Built Fleet server from this branch with
`--osquery_status_log_plugin=splunk`
3. Set up Fleet, enrolled a real osquery 5.23.0 agent on this MacBook
4. **83 real osquery status log events indexed in Splunk** with correct
source/sourcetype/index
5. Each event contained full osquery data (`hostIdentifier`,
`host_uuid`, `calendarTime`, `severity`, `message`, `decorations`)
### Splunk showing real osquery events from Fleet
<img width="1910" height="861" alt="image"
src="https://github.com/user-attachments/assets/192490bf-d594-4424-a3e3-a18306892873"
/>
Generated with [Claude Code](https://claude.ai/code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added native Splunk HEC logging destination for status, result, and
audit logs.
* Updated the log destination UI to display **Splunk** with a dedicated
tooltip.
* Added Splunk HEC configuration (URL/token/index/source/source type)
including TLS verification control.
* **Bug Fixes**
* Improved log delivery with batching, retries for temporary HTTP
failures, and safeguards for oversized events.
* **Tests**
* Added unit tests and optional integration tests covering routing,
batching, retries, and error scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Changes
- Deleted 52 anonymous case study article files from the `articles/`
directory
- Removed all anonymous case study sections (financial services,
technology & SaaS, security/IT/healthcare/other) from
`handbook/marketing/fleet-social-proof.md`
- Removed 21 redirect entries from `website/config/routes.js` that
pointed to these removed case studies
The named customer stories (Stripe, Foursquare, Faire, Thumbtack,
Deputy, Fastly) and customer testimonials are preserved.
---
Built for [Irena
Reedy](https://fleetdm.slack.com/archives/D0APYC9R9SL/p1783033337222849?thread_ts=1775761161.561979&cid=D0APYC9R9SL)
by [Kilo for Slack](https://kilo.ai/slack)
---------
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Co-authored-by: Irena Reedy <irena@fleetdm.com>
Co-authored-by: Eric <eashaw@sailsjs.com>
Fixes `TestGitOpsFullGlobal`, which has been failing the `fleetctl` test
bundle on every `main` run since #48467 merged (bisected to a90eab6f62,
e.g. [this
run](https://github.com/fleetdm/fleet/actions/runs/28665588825)).
#48467 changed `BatchSetMDMProfiles` so that `assume_enabled` is only
honored on dry runs; real runs now validate Windows profiles against the
app config persisted in the datastore. That's correct in production,
because the GitOps run persists `windows_enabled_and_configured: true`
via `ModifyAppConfig` before the profiles batch call. But this test's
`AppConfigFunc` mock always returned a fixed app config with Windows MDM
disabled, never reflecting what `SaveAppConfigFunc` stored — so the
real-run profiles batch now fails with 422 "Windows MDM isn't turned
on".
The fix makes the mock behave like the real datastore: once
`SaveAppConfig` is called, `AppConfigFunc` returns the saved config.
Test-only change, no product code touched.
# Checklist for submitter
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
## Testing
- [x] Added/updated automated tests
`go test -run TestGitOps ./cmd/fleetctl/fleetctl/` passes locally (it
fails on `main` without this change).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Improved GitOps test coverage to better match real persistence
behavior during an apply run.
* Updated test setup so saved app configuration is read back correctly
after changes are applied.
* Reset test state between subtests to keep each scenario isolated and
reliable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Settings-page adjustments to the Google Workspace integration (per
[product
feedback](https://github.com/fleetdm/fleet/issues/42915#issuecomment-4867743312)):
1. Removed the wrapper card (`InfoBanner`) around the instructions —
replaced it with `PageDescription` (`variant="right-panel"`), so the
text styling matches the "Connect Fleet to your IdP…" description in the
sibling IdP section.
2. Updated the copy to:
> Configure these settings to populate IdP host vitals from Google
Workspace. When Google Workspace is connected, Fleet ignores SCIM
provisioning from other IdPs (e.g Okta, Entra ID).
Also updated a stale SCSS comment that referenced the removed
`InfoBanner` (the `box-sizing: border-box` fix on the form card stays —
it's needed regardless of the banner).
**Related issue:** #42915
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Updated the Google Workspace settings page to show connection and
configuration guidance in a cleaner description block.
* Preserved all existing form fields, validation, and submission
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Resolves#47894
- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
## Testing
- [X] QA'd all new/changed functionality manually
---
Performance results on my macOS host (between the old an new query):
Clean, dramatic result. Subtracting the ~0.23 s / ~27.5 MB osqueryd
startup baseline to isolate the query cost:
```
┌─────────────────────┬───────────┬──────────┬──────────────────────────┐
│ │ Wall time │ Peak RSS │ Query-attributable work¹ │
├─────────────────────┼───────────┼──────────┼──────────────────────────┤
│ Baseline (SELECT 1) │ 0.23 s │ 27.5 MB │ — │
├─────────────────────┼───────────┼──────────┼──────────────────────────┤
│ OLD (recursive %%) │ ~1.46 s │ 128 MB │ +1.23 s, +100 MB │
├─────────────────────┼───────────┼──────────┼──────────────────────────┤
│ NEW (bounded 2+3) │ 0.24 s │ 27.8 MB │ +0.01 s, +0.3 MB │
└─────────────────────┴───────────┴──────────┴──────────────────────────┘
¹ over baseline
```
Takeaways:
- Memory: ~128 MB → ~28 MB peak (–100 MB). The recursive walk alone
added ~100 MB; the bounded version adds essentially nothing.
- Time: ~1.46 s → ~0.24 s (~6× faster wall clock; the query-attributable
work dropped ~1.23 s → ~0.01 s, effectively free).
- System time tells the story: OLD spends 0.88–0.97 s in sys (the
readdir/stat syscalls from walking the tree); NEW spends ~0.00 s.
And this is with only 6 casks, dominated by gcloud-cli's ~98k-entry SDK
tree (walked twice via the latest → version symlink, plus following the
app back-symlinks into /Applications bundles). The recursive query hit
128 MB peak from a single well-stocked host — already within striking
distance of osquery's 200 MB watchdog limit. On hosts with more or
larger casks (or the /Library//Applications patterns from the issue),
that's exactly what tips it over and kills the worker. The bounded
version is flat regardless.
<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Should Resolve#34591
# Checklist for submitter
- [x] Changes file added
## User Story
CachyOS lacks from vitals information such as :
* disk encryption status
* disk space
* IP & MAC Addresses
* Installed packages
## Summary
- Add CachyOS as a recognized Linux platform
## Tests
- [x] Enroll a CachyOS host and verify it appears as Linux in Fleet
- [x] Verify disk encryption status displays correctly
- [x] Verify pacman packages are queryable via `fleetd_pacman_packages`
table
- [x] Disk space, mac address, Public/Private IP are well reported
- [x] Script are well executed
- [x] No more errors in fleet service logs (level=error
msg="unrecognized platform" hostID=169 platform=cachyos)
- [ ] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
* **New Features**
* CachyOS (Arch-based Linux distribution) is now recognized as a
supported platform, including disk encryption detection and LUKS
support.
* **Bug Fixes**
* Updated host vitals disk-encryption tooltip messaging so CachyOS uses
the correct Linux-specific copy.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: plop28 <plop28@noreply.com>
Automated ingestion of latest Fleet-maintained app data.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Updated download links, version checks, and checksums for multiple
maintained apps on macOS and Windows, including browsers, developer
tools, and productivity apps.
* Bumped several apps to newer releases so install/upgrade detection
stays accurate.
* Refreshed the 7-Zip Windows download source to a newer release asset.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: allenhouchins <32207388+allenhouchins@users.noreply.github.com>
## Summary
The `get_policies` MCP tool wasn't returning each policy's osquery SQL,
making it impossible to diagnose policy behavior via MCP — you had to
fall back to `curl` against `GET /api/v1/fleet/global/policies/{id}` to
read the `query` field.
Root cause: the `Policy` struct in
`tools/fleet-mcp/fleet_integration.go` didn't declare a `Query` field,
so Go's JSON decoder silently dropped the key from the Fleet API
response. One-line fix on the struct; the same struct backs both the
global path and the per-team fan-out, so both are covered.
Purely additive — no existing field changes shape, name, or type.
# Checklist for submitter
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Fleet policies now include a query field so the actual policy
query/SQL content is available with policy metadata.
* **Tests**
* Added integration-style test coverage to confirm the query field is
parsed and returned (including empty queries) and that policy responses
contain the expected entries.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45513)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: nulmete <nicoulmete1@gmail.com>