Skip to content

44954 custom host vitals - #49334

Merged
nulmete merged 13 commits into
mainfrom
44954-custom-host-vitals
Jul 15, 2026
Merged

44954 custom host vitals#49334
nulmete merged 13 commits into
mainfrom
44954-custom-host-vitals

Conversation

@nulmete

@nulmete nulmete commented Jul 15, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #44954

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • New Features

    • Added Custom Host Vitals management (create/edit/delete, search) with copyable variable tokens and per-host values.
    • Show Custom Host Vitals on host details, including role-based editing.
    • Enabled Custom Host Vitals in host-vitals labels and the activity feed.
    • Extended GitOps to manage global Custom Host Vitals declaratively (including dry-run behavior).
    • Split Controls → Variables into Global Variables and Custom Host Vitals, including routing updates.
  • Bug Fixes

    • Improved validation and expansion of Custom Host Vital references across scripts, profiles, installers, and deployments, with clearer failures when values are missing or invalid.

nulmete and others added 11 commits July 3, 2026 11:20
…d_at (#48555) (#48634)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48555

Custom host vitals (#44954) let admins define custom fields that surface
on the host details page and can be referenced in scripts and
configuration profiles. This PR builds the Controls > Variables
frontend: a Global variables / Custom host vitals sub-nav, and the
Custom host vitals CRUD tab (add/edit/delete, mock-backed until the API
lands). It also exposes `created_at` on the custom variables list
endpoint so the table's Created column renders.

# Checklist for submitter

- [ ] 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.

Will add changes file in the feature branch directly.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/fb3c67ee-373f-4971-a071-55939b6d8ea5




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a dedicated “Global variables” and “Custom host vitals”
experience under Controls, with navigation between sections.
* Users can now add, edit, delete, search, and copy values for custom
host vitals.
* Variable values now display clearer tokens and updated-time
information.

* **Bug Fixes**
  * Improved routing so section-specific Controls URLs load correctly.
* Added creation timestamps to variable listings for more complete
record details.

<!-- 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 #48559

Adds the schema (global definitions + per-host values) and the CRUD API:
create/list/rename/delete definitions, set a per-host value, the
host-details embed, and the related activities.

# Checklist for submitter

- [ ] 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.

Will be added in the feature branch directly.

## 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**
* Added support for custom host vitals, including creating, renaming,
deleting, listing, and setting values per host.
  * Host details now show any custom vitals assigned to that machine.
  * Activity history now records custom host vital changes.

* **Bug Fixes**
* Improved access controls so only appropriately authorized users can
view or manage custom host vitals.
* Added search and pagination support when browsing custom host vitals.

<!-- 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 #48557

Adds a "Custom host vitals" header button that navigates there to the
new Custom host vitals tab, and restyles the adjacent enroll-secret
button per design (gear icon + "Enroll secrets" label).

# Checklist for submitter

- [ ] 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.

Will be added in feature branch.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/13db084c-cc35-4609-a262-d20ed2607a6f
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48644

Renders global activities for custom host vitals (create, edit, delete).

# Checklist for submitter

- [ ] 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.

Will be added in feature branch.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

<img width="711" height="406" alt="Screenshot 2026-07-07 at 5 19 19 PM"
src="https://github.com/user-attachments/assets/e0b16ff5-0ce8-4514-a171-d3139bcfcda9"
/>
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48686

The Controls > Variables sub-nav (shipped in #48555) added two tabs:
Global variables and Custom host vitals.This adds both as navigable
palette entries under the existing Variables entry.

# Checklist for submitter

- [ ] 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.

Will be added in feature branch.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually
…#48901)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48556

Introduces rendering of the custom vitals on the Vitals card, adds an
inline edit modal (with permission gating), and adds the
`edited_custom_host_vital_value` host activity item.

# Checklist for submitter

- [ ] 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.

Will be added in feature branch.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/6bd13846-1d05-4ca2-a0ff-987110283088



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Hosts can now display custom vital metrics on the host details page.
* Users with sufficient permissions can edit custom vital values from
the host details view.
* Activity history now includes entries for edited custom vital values,
including the custom vital name.
* **Bug Fixes**
* Custom vitals are now shown even before any host-specific value is
set, with empty values handled consistently.
* **Tests**
* Added coverage for custom vital rendering, editing, and the new
activity item behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…#48821)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48761

This PR adds the backend for custom host vitals: validating
`$FLEET_HOST_VITAL_<id>` on upload and expanding it per-host at delivery
(scripts, Apple/Windows/DDM configuration profiles, software-installer +
setup-experience scripts), plus delete-protection.
Stacked on the CRUD PR which has already been merged to the feature
branch.

# Checklist for submitter

- [ ] 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.

Will be added in feature branch.

- [ ] 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
- [ ] 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


Env:
- dev server
- one enrolled macOS 26.5.1 VM (`host_id=1`)
- fleet "💻 Workstations".
- The Controls → Variables management UI is still on mocks, so vital
definitions/values were created via the REST API; everything else via
the UI.

### 1. Scripts: expansion at fetch time
- Created vital `asset_tag` (`$FLEET_HOST_VITAL_1`), **no value** on the
host. Ran `echo "asset tag: $FLEET_HOST_VITAL_1"` → run **failed to
fetch**; server log:
`err="…Couldn't populate custom host vital \"$FLEET_HOST_VITAL_1\": no
value set for this host"`. Run stayed **Upcoming** (never executed;
re-attempts each poll).
- Set the value to `LAPTOP-042` → the pending run fetched successfully
on the next poll and completed (exit 0) with `asset tag: LAPTOP-042`.

<img width="825" height="328" alt="laptop042_script"
src="https://github.com/user-attachments/assets/28eb057c-94b7-4072-996b-9db40db330b2"
/>

### 2. Apple configuration profile: re-delivery

Profile writes `AssetTag = $FLEET_HOST_VITAL_1` into a managed
preference domain.

- **Delivered with value:** VM shows `AssetTag = LAPTOP-042`.

<img width="488" height="452"
alt="laptop042_mobileconfig_value_already_set"
src="https://github.com/user-attachments/assets/48ace314-75d8-4981-a999-23d1c0f55e74"
/>


- **Re-push on change:** changed the value to `LAPTOP-999` via API (no
re-upload/re-scope) → profile re-delivered → VM shows `AssetTag =
LAPTOP-999`.

<img width="505" height="476" alt="laptop042_change_to_laptop999_repush"
src="https://github.com/user-attachments/assets/22e61208-1600-48c2-85f8-84ca330734c3"
/>


- **No-value → Failed:** a second profile references
`$FLEET_HOST_VITAL_2` (`department`, no value) → profile lands
**Failed** and is not applied on the VM. Reason is stored as the
profile's per-host `detail`: `Couldn't populate custom host vital
"$FLEET_HOST_VITAL_2": no value set for this host`.

<img width="672" height="445" alt="step3_novalue_fails"
src="https://github.com/user-attachments/assets/d7eb7da6-ddf4-4ceb-84d1-c6096d27ae2e"
/>


- **Failed → set value → recovered:** set `department = Engineering` →
the Failed profile reset (`failed → pending`, detail cleared) and
re-delivered → VM shows `Department = Engineering`.

<img width="499" height="457" alt="step3_no_value_set_value"
src="https://github.com/user-attachments/assets/bd52bf43-01f8-4524-8f81-827f45cad2be"
/>


### 3. DDM declaration
Declaration
`com.apple.configuration.softwareupdate.enforcement.specific` with
`TargetOSVersion = $FLEET_HOST_VITAL_3` (`min_os`).

- **No-value → Failed:** with no value, the declaration lands **Failed**
on the host; detail `Couldn't populate custom host vital
"$FLEET_HOST_VITAL_3": no value set for this host`, the reconciler
flagged it and the manifest builder dropped it (not a device-side
rejection).

<img width="679" height="442" alt="ddm_failed_no_value"
src="https://github.com/user-attachments/assets/cd866c28-c180-4453-8e81-8740808888c4"
/>


- **Set value → Verified:** set `min_os = 26.5.1` (VM's current OS →
enforcement is a no-op) → declaration reset `failed → pending`,
re-synced → **Verified**. Verified ⇒ the token expanded to a valid
version (a literal token would be an invalid OS version and get
rejected).

<img width="663" height="433" alt="ddm_set_value_verified"
src="https://github.com/user-attachments/assets/01cee1c7-fb65-48b3-9597-cbc068f1ef4b"
/>


- DB confirmation of the fix:
`host_mdm_apple_declarations.variables_updated_at` for this declaration
is **non-NULL** (`2026-07-10 00:11:34`), i.e. the vital-referencing
declaration was flagged `HasFleetVariables` and stamped.

### 4. Delete-protection
- **Referenced → 409:** `DELETE` on each of the three vitals returned
**409 Conflict** naming the referencing entity: across a **script**
(`helloworld.sh`), an **Apple profile**, and a **DDM declaration**,
proving the reference scan covers all types.
- **Unreferenced → deletes + cascade:** after removing the references,
`DELETE` returned **200**; DB confirmed `custom_host_vitals` and
`host_custom_host_vitals` both empty (per-host values cascaded via FK
`ON DELETE CASCADE`); `deleted_custom_host_vital` activity fired for
each.

### Not covered here (would need QA assistance)
- **Windows** configuration profile (needs an enrolled Windows host)
re-delivery path.
- **Software-installer** + **setup-experience** scripts
(validation-on-save + expansion-at-run mirror).
- **Android** negative (rejection on upload): needs an Android
enrollment.

## Note about scope & parity with `$FLEET_SECRET_*`

Custom host vitals are **user-defined** (like `$FLEET_SECRET_*`), not
built-in (like `$FLEET_VAR_*`), so they were scoped to the **same
entities as embedded secrets**. Confirmed with product at standup after
re-asking ([Slack
thread](https://fleetdm.slack.com/archives/C084F4MKYSJ/p1783349366719079);
screenshot below).
The table double-checks parity holds across every surface (line numbers
= the validate/expand wiring, non-test).

<img width="925" height="308" alt="Screenshot 2026-07-09 at 10 14 03 PM"
src="https://github.com/user-attachments/assets/307ea1de-0b3b-4f3e-8ab0-704d387deda6"
/>


| Delivery surface | `$FLEET_SECRET_*` | `$FLEET_HOST_VITAL_*` | Parity
|
|---|---|---|---|
| **Scripts** (ad-hoc + saved) | validate `scripts.go:131/413/616/769`;
expand `orbit.go:1092` | same validate (combined helper); expand
`orbit.go:1098` | ✅ |
| **Apple config profiles** | upload `apple_mdm.go:411`; nano re-expand
`nanomdm_storage.go:360` | validate `apple_mdm.go:426`; expand
`profile_processor.go:650` | ✅ |
| **Apple DDM declarations** | upload `apple_mdm.go:911`; delivery
`apple_mdm.go:6344` | validate `apple_mdm.go:927` / `mdm.go:2257`;
expand `apple_mdm.go:1237` | ✅ |
| **Windows config profiles** | validate `windows_mdm_profiles.go:95`;
expand `microsoft_mdm.go:2036/1051` | same validate (combined helper);
expand `profile_variables.go:202` | ✅ |
| **Software-installer scripts** | validate
`software_installers.go:149/377/2461`; expand `:134` | validate
`:162/390/2464`; expand `:138` | ✅ |
| **Setup-experience scripts** | validate `setup_experience.go:140`;
expand via script path | validate `setup_experience.go:143`; expand via
script path | ✅ |
| **Fleet-maintained-app scripts** | validate `maintained_apps.go:47` |
validate `maintained_apps.go:60` | ✅ |
| **Android app config** | ✗ no secret expansion in Android path | ✗
rejected at upload | ✅ (both unsupported) |

In several files the vital validate/expand call sits 1–13 lines after
the secret one (e.g. `setup_experience.go:140→143`,
`software_installers.go:149→162`, `apple_mdm.go:411→426`). One
deliberate architectural difference (not a gap): secrets are **global**
(expanded at upload), vitals are **per-host** (validated at upload,
expanded at delivery) — which is why the re-delivery fixes were needed
for vitals but not secrets. **Android** is the single surface where
neither is supported, so rejecting vitals there matches secret behavior.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Script, software installer, and setup-experience content can now
reference custom host vitals and will be expanded with per-host values
during delivery.
* MDM Apple and Windows profile processing now expands custom host
vitals; Android configuration profiles still reject custom host vitals.
* Batch MDM profile submission now rejects payloads that reference
unknown custom host vitals.

* **Bug Fixes**
  * Prevent saving content that references unknown custom host vitals.
* Prevent deleting a custom host vital while it’s still referenced (with
a clearer conflict message).
* Improved delivery-time errors when a custom host vital has no value
for the target host.

* **Refactor**
* Unified embedded secret validation with referenced custom-host-vital
validation across script and profile upload flows.

* **Tests**
* Added coverage for unknown vitals rejection, delivery expansion
failures, and installer/profile expansion behavior.
<!-- 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 #48582

Host-vitals labels can currently only be keyed on IdP group/department,
so admins can't build a label from a custom host vital. This adds a
`custom_host_vital` label criterion — a host is a member when its stored
value for the selected vital equals the criterion value — wired through
label create, batch/GitOps validation, membership evaluation, and
delete-protection, plus the New label UI. Targets the
`44954-custom-host-vitals` feature branch.

# Checklist for submitter


- [ ] 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.

Will be added in feature branch.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

Created `Test label 1` label with criteria: "Department = Engineering"
(`Department` is a custom host vital).

<img width="1396" height="658" alt="Screenshot 2026-07-13 at 11 15
17 PM"
src="https://github.com/user-attachments/assets/38434ee8-302b-4293-af5e-f59e62189fd7"
/>

Set the host vital value to Engineering in two hosts:


<img width="1458" height="634" alt="Screenshot 2026-07-13 at 11 14
42 PM"
src="https://github.com/user-attachments/assets/48a28794-22b3-4d99-b3f5-93b86dc930d9"
/>
<img width="1459" height="631" alt="Screenshot 2026-07-13 at 11 14
47 PM"
src="https://github.com/user-attachments/assets/50653f8c-2d83-4827-88b9-d18b2e2e75e4"
/>

Verified label membership contains those two hosts:

<img width="1481" height="336" alt="Screenshot 2026-07-13 at 11 14
28 PM"
src="https://github.com/user-attachments/assets/7a4a0124-c3d2-4a46-a1d3-e7273f581714"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for creating host-vitals labels using custom host
vitals.
* Custom host vital criteria now support filtering, label membership
updates, and label specification application.
* Added clearer value prompts and validation for selected custom vitals.

* **Bug Fixes**
  * Prevented deletion of custom host vitals referenced by labels.
* Invalid or unknown custom vital references are now rejected during
label creation and application.

<!-- 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 #48558

- Adds a `custom_host_vitals` GitOps key, letting `fleetctl gitops`
create, update, and remove custom host vital definitions.
- Introduces a new `PUT /spec/custom_host_vitals` server endpoint that
reconciles the full set of definitions in one call
- Extends `fleetctl generate-gitops` to emit existing vitals so a GitOps
repo can capture and round-trip this configuration.

# Checklist for submitter

- [ ] 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.

Will be added in the feature branch.

## 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**
* Added GitOps support for a `custom_host_vitals` section in global YAML
to declare, reconcile, and report custom host vitals.
* Added a custom host vitals API that validates names, supports dry-run,
and provides create/delete feedback.
* Supports clearing vitals by explicitly providing an empty list (or
`null`), while omitting the key leaves current vitals unchanged.
* **Bug Fixes**
* Prevents deleting vitals that are still referenced by scripts or other
resources.
* Preserves existing vital IDs when vital definitions are unchanged and
rejects invalid/duplicate vital names.
<!-- 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:** Relates to #48555

De-mocks the Custom host vitals tab (Controls → Variables): the tab and
its add/edit/delete modals now call the real `custom_host_vitals` API
service instead of the temporary in-memory mock, which is removed.

# Checklist for submitter

- [ ] 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.

Will be added in the feature branch.


## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/87d2bc36-db86-4a80-8b04-bab8bf9cd361
# Conflicts:
#	frontend/components/CommandPalette/groups/controls.ts
#	frontend/components/CommandPalette/helpers.tests.ts
#	frontend/interfaces/activity.ts
#	frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx
#	server/datastore/mysql/apple_mdm_batched.go
#	server/datastore/mysql/schema.sql
@nulmete
nulmete marked this pull request as ready for review July 15, 2026 15:06
@nulmete
nulmete requested review from a team and rachaelshaw as code owners July 15, 2026 15:06
Copilot AI review requested due to automatic review settings July 15, 2026 15:06
@nulmete
nulmete requested a review from a team as a code owner July 15, 2026 15:06
juan-fdz-hawa
juan-fdz-hawa previously approved these changes Jul 15, 2026
Comment thread server/datastore/mysql/custom_host_vitals.go Fixed
Comment thread server/fleet/custom_host_vitals.go Fixed
Comment thread server/fleet/custom_host_vitals.go Fixed
Comment thread server/fleet/custom_host_vitals.go Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Adds “custom host vitals” (global definitions + per-host values) across Fleet’s API, GitOps, backend expansion/validation paths, and UI surfaces (Controls > Variables and Host details), including activity logging and authz.

Changes:

  • Introduces DB schema + datastore/service/API plumbing for custom host vital definitions and per-host values, including validation (upload-time) and expansion (delivery-time).
  • Updates MDM/script/software installer pipelines to validate referenced $FLEET_HOST_VITAL_<id> tokens and expand them per-host where supported (Apple/Windows/profiles/scripts/installers), with Android rejecting usage.
  • Adds UI for managing definitions and setting per-host values, plus activities and GitOps (apply + generate) support.

Reviewed changes

Copilot reviewed 121 out of 123 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/service/windows_mdm_profiles.go Validates embedded secrets + custom host vitals for Windows MDM profile uploads.
server/service/testing_utils_test.go Sets default mock behavior for custom host vital reference validation in service tests.
server/service/scripts.go Validates scripts for embedded secrets + custom host vitals on create/update/run/batch set.
server/service/orbit.go Expands custom host vitals when serving host scripts (delivery-time).
server/service/microsoft_mdm.go Ensures Windows profile reconcile treats profiles with vitals as per-host processed.
server/service/mdm.go Validates referenced custom host vitals for (non-Android) MDM profiles at batch set.
server/service/labels.go Validates custom host vital criteria IDs exist for labels/spec apply paths.
server/service/labels_test.go Adds label tests covering custom host vital criteria validation and query generation.
server/service/integration_custom_host_vitals_test.go New integration test covering custom host vitals CRUD + activities + host detail projection.
server/service/hosts.go Adds custom host vitals to host detail response (via datastore lookup).
server/service/hosts_test.go Updates host detail tests for new datastore dependency.
server/service/handler.go Registers REST endpoints for custom host vitals and per-host value updates + spec upsert.
server/service/devices_endpoint_test.go Stubs new host detail dependency in device endpoint tests.
server/service/custom_host_vitals_resolution_test.go Tests service behavior for expansion + validation in scripts.
server/service/client.go Adds GitOps reconciliation step for custom host vitals.
server/service/client_custom_host_vitals.go Adds fleetctl client methods to list/save custom host vitals for GitOps and generation.
server/service/apple_mdm.go Validates referenced custom host vitals on Apple profile/declaration upload and expands at delivery (DDM).
server/service/apple_mdm_test.go Updates test setup for new datastore dependency.
server/service/apple_mdm_ddm_test.go Verifies custom host vitals expand after Fleet vars in DDM to avoid unintended replacements.
server/mock/service/service_mock.go Extends service mock interface with custom host vitals operations.
server/mock/datastore_mock.go Extends datastore mock interface with custom host vitals operations.
server/mdm/microsoft/profile_variables.go Expands custom host vitals in Windows profile preprocessing and maps missing values to profile failure detail.
server/mdm/microsoft/custom_host_vitals_test.go Tests Windows profile preprocessing custom host vitals expansion and missing-value behavior.
server/mdm/apple/profile_processor.go Expands custom host vitals per-host during Apple profile processing and marks profiles failed when missing.
server/fleet/service.go Adds service interface methods for custom host vitals CRUD/value set/spec upsert.
server/fleet/secret_variables.go Adds created_at to secret variable identifier payload.
server/fleet/labels.go Extends HostVitalCriteria with custom_host_vital_id and builds membership join for custom vital criteria.
server/fleet/hosts.go Adds host vital type + HostDetail.custom_host_vitals field.
server/fleet/embedded_variables.go Adds shared validator for secrets + custom host vitals references.
server/fleet/datastore.go Adds datastore interface for custom host vitals CRUD/value set/validate/expand/upsert.
server/fleet/custom_host_vitals.go Adds core types, token parsing helpers, and error types for custom host vitals.
server/fleet/custom_host_vitals_test.go Unit tests for token parsing and error messages.
server/fleet/apple_mdm.go Clarifies HasFleetVariables semantics to include custom host vitals for declarations.
server/fleet/api_custom_host_vitals.go Adds API request/response structs for custom host vitals endpoints + spec upsert.
server/fleet/android.go Rejects custom host vitals tokens in Android profiles (unsupported expansion).
server/fleet/android_test.go Tests Android rejection of custom host vitals usage.
server/fleet/activities.go Adds activity types for custom host vital CRUD and per-host value edits.
server/datastore/mysql/software_installers.go Expands custom host vitals for per-host software installer scripts at execution-time.
server/datastore/mysql/software_installers_test.go Tests installer script expansion/missing-value failure for custom host vitals.
server/datastore/mysql/secret_variables.go Adds created_at to list query and refactors shared document var expansion/escaping helper.
server/datastore/mysql/secret_variables_test.go Asserts created_at is returned when listing secret variables.
server/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals.go Migration adding custom_host_vitals + host_custom_host_vitals tables.
server/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals_test.go Migration test for uniqueness, NOT NULL, and cascade delete behavior.
server/datastore/mysql/labels_test.go Tests MySQL label membership evaluation for custom host vital criteria (real join path).
server/datastore/mysql/hosts.go Adds host_custom_host_vitals to hostRefs cleanup list.
server/datastore/mysql/hosts_test.go Ensures host deletion cleanup covers host_custom_host_vitals rows.
server/datastore/mysql/apple_mdm_batched.go Marks declarations with custom host vitals as variable-bearing so delivery updates track changes.
server/authz/policy.rego Adds authz rules for managing definitions and setting per-host values.
server/authz/policy_test.go Tests new authz rules for custom host vitals.
pkg/spec/gitops.go Adds GitOps parsing/presence tracking for custom_host_vitals (global-only, declarative).
pkg/spec/gitops_test.go Tests parsing rules and invalid configurations for custom host vitals in GitOps YAML.
frontend/utilities/endpoints.ts Adds endpoints for custom host vitals and clarifies “global variables” endpoint naming.
frontend/services/entities/variables.ts Updates variables API client to use GLOBAL_VARIABLES endpoint constant.
frontend/services/entities/labels.ts Sends custom_host_vital_id for host-vitals label creation when applicable.
frontend/services/entities/custom_host_vitals.ts New FE API client for custom host vitals CRUD + per-host value set.
frontend/router/paths.ts Adds routes for global variables and custom host vitals tabs.
frontend/router/index.tsx Supports optional variables section route param.
frontend/pages/ManageControlsPage/Variables/VariablesNavItems.tsx Adds left-nav items for Variables sub-tabs (Global variables, Custom host vitals).
frontend/pages/ManageControlsPage/Variables/helpers.ts Adds validation helpers/constants for custom host vital name.
frontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/index.ts Barrel export for edit vital modal.
frontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/EditCustomHostVitalModal.tsx UI modal to rename a custom host vital.
frontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/index.ts Barrel export for delete vital modal.
frontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/DeleteCustomHostVitalModal.tsx UI modal to delete a custom host vital with conflict handling.
frontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/index.ts Barrel export for add vital modal.
frontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/AddCustomHostVitalModal.tsx UI modal to create a custom host vital.
frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/index.ts Barrel export for global variables tab/card.
frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariablesTableConfig.tsx Table config for global variables (token, created time, delete).
frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tsx New global variables tab UI with pagination + deep-link add modal.
frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tests.tsx Updates tests for renamed global variables component and UI labels.
frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/index.ts Barrel export for custom host vitals tab.
frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTableConfig.tsx Table config for custom host vitals (token copy, updated time, edit/delete actions).
frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tsx Custom host vitals tab UI with URL-persistent search and CRUD modals.
frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tests.tsx Tests URL-persistent search behavior for custom host vitals tab.
frontend/pages/ManageControlsPage/Variables/_styles.scss Refactors variables page styling into global variables + custom host vitals layouts.
frontend/pages/labels/NewLabelPage/helpers.ts Adds helpers for custom-host-vital criterion encoding/decoding + validation rules.
frontend/pages/labels/NewLabelPage/helpers.tests.ts Tests new label helper encoding/decoding and helper text/placeholder behavior.
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx Adds “Custom host vitals” shortcut button and updates enroll-secret button UI.
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx Updates test expectation for renamed enroll secrets button text.
frontend/pages/hosts/details/modals/EditHostVitalModal/index.ts Barrel export for edit host vital modal.
frontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tsx Modal to set/clear a host’s custom vital value.
frontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tests.tsx Tests save/cancel behavior for the edit host vital modal.
frontend/pages/hosts/details/modals/EditHostVitalModal/_styles.scss Styles for edit host vital modal layout.
frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx Wires custom host vitals into host vitals card and edit modal based on permissions.
frontend/pages/hosts/details/cards/Vitals/Vitals.tsx Renders custom host vitals within Vitals card, with optional edit affordance.
frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx Tests rendering and edit behavior for custom host vitals in Vitals card.
frontend/pages/hosts/details/cards/Vitals/_styles.scss Adds styling for custom vital title/edit button layout.
frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/index.ts Barrel export for host activity item.
frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tsx Host activity UI item for edited custom host vital value.
frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tests.tsx Tests rendering for edited custom host vital value activity item.
frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx Registers new host activity item component mapping.
frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx Adds global activity feed rendering for created/edited/deleted custom host vitals.
frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx Tests global activity feed rendering for custom host vital activities.
frontend/interfaces/label.ts Extends label criterion typing to support custom host vital id requirement.
frontend/interfaces/host.ts Adds custom_host_vitals to host interface.
frontend/interfaces/custom_host_vitals.ts Adds interfaces for definitions and per-host custom vital projection.
frontend/interfaces/activity.ts Adds activity types/details/filter labels for custom host vitals.
frontend/components/CommandPalette/helpers.ts Adds canAccessVariables flag to palette context.
frontend/components/CommandPalette/helpers.tests.ts Tests palette items include/hide Variables section based on canAccessVariables.
frontend/components/CommandPalette/groups/controls.ts Adds Variables item + sub-items for global variables/custom host vitals (gated).
frontend/components/CommandPalette/CommandPalette.tsx Computes canAccessVariables and passes it into palette item building.
ee/server/service/software_installers.go Validates referenced custom host vitals in installer scripts on upload/update/batch set.
ee/server/service/software_installers_test.go Sets default mock validation + keeps tests working with new validation.
ee/server/service/setup_experience.go Validates referenced custom host vitals in setup experience script uploads.
ee/server/service/setup_experience_test.go Adds test ensuring unknown custom host vital tokens are rejected for setup scripts.
ee/server/service/maintained_apps.go Validates referenced custom host vitals in maintained app scripts.
cmd/fleetctl/integrationtest/gitops/gitops_integration_test.go Adds gitops integration tests for custom host vitals (dry-run, apply, clear-all, reference blocking).
cmd/fleetctl/fleetctl/testing_utils/testing_utils.go Stubs datastore methods needed by gitops custom host vitals in mocked server tests.
cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml Updates generate-gitops fixture to include custom_host_vitals.
cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml Updates generate-gitops fixture to include custom_host_vitals.
cmd/fleetctl/fleetctl/scripts_test.go Adds host detail stub dependency for scripts tests.
cmd/fleetctl/fleetctl/mdm_test.go Adds host detail stub dependency for MDM tests.
cmd/fleetctl/fleetctl/get_test.go Adds host detail stub dependency for get tests.
cmd/fleetctl/fleetctl/generate_gitops.go Adds generation of custom_host_vitals into output YAML (round-trip support).
cmd/fleetctl/fleetctl/generate_gitops_test.go Extends mock client for custom host vitals generation.
docs/REST API/rest-api.md Content excluded (REST API docs updated).
changes/44954-custom-host-vitals.md Content excluded (user-visible change note).
Files excluded by content exclusion policy (2)
  • changes/44954-custom-host-vitals.md
  • docs/REST API/rest-api.md

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/pages/labels/NewLabelPage/helpers.ts
Comment thread frontend/services/entities/labels.ts
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.93704% with 397 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.96%. Comparing base (2262551) to head (18b45a4).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/custom_host_vitals.go 81.51% 37 Missing and 29 partials ⚠️
server/service/custom_host_vitals.go 77.71% 22 Missing and 17 partials ⚠️
ee/server/service/software_installers.go 0.00% 23 Missing and 3 partials ⚠️
...ddCustomHostVitalModal/AddCustomHostVitalModal.tsx 7.40% 25 Missing ⚠️
...tCustomHostVitalModal/EditCustomHostVitalModal.tsx 7.40% 25 Missing ⚠️
server/mdm/apple/profile_processor.go 7.40% 24 Missing and 1 partial ⚠️
...tend/pages/ManageControlsPage/Variables/helpers.ts 17.39% 19 Missing ⚠️
server/fleet/custom_host_vitals.go 75.32% 18 Missing and 1 partial ⚠️
.../cards/CustomHostVitalsTab/CustomHostVitalsTab.tsx 78.04% 18 Missing ⚠️
...ustomHostVitalModal/DeleteCustomHostVitalModal.tsx 11.76% 15 Missing ⚠️
... and 23 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49334      +/-   ##
==========================================
- Coverage   67.96%   67.96%   -0.01%     
==========================================
  Files        3823     3841      +18     
  Lines      241004   242362    +1358     
  Branches    12702    12967     +265     
==========================================
+ Hits       163806   164728     +922     
- Misses      62323    62685     +362     
- Partials    14875    14949      +74     
Flag Coverage Δ
backend 69.56% <74.50%> (+0.02%) ⬆️
frontend 59.37% <62.34%> (-0.12%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a6a44f87-9b7f-4ae4-985f-2bce20a59f8c

📥 Commits

Reviewing files that changed from the base of the PR and between 24e4293 and 18b45a4.

📒 Files selected for processing (1)
  • server/service/hosts_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/service/hosts_test.go

Walkthrough

This PR adds custom host vital definitions, per-host values, token validation and expansion, persistence, authorization, REST APIs, GitOps parsing and reconciliation, MDM integration, label criteria, activity records, and frontend management and editing flows. It also adds database migrations, datastore and service tests, integration coverage, and supporting mock implementations.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning It covers the issue link, change file, tests, and QA, but omits several relevant template sections like migrations, GitOps, and permissions. Add the missing relevant checklist items and briefly note the DB migration, GitOps, permissions, and any other applicable sections.
Out of Scope Changes check ⚠️ Warning The custom-host-vital label UI and criteria work appears outside the issue scope, which focused on scripts, profiles, GitOps, and host details. Move the label-specific UI, criteria, and datastore changes to the follow-on labels issue unless they are required for this story.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately names the main change: custom host vitals.
Linked Issues check ✅ Passed The PR implements the core feature set: CRUD, host values, scripts/profiles expansion, GitOps, activities, permissions, and schema changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 44954-custom-host-vitals

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ee/server/service/software_installers.go`:
- Around line 880-894: Update validateReferencedCustomHostVitalsOnScript to
aggregate only typed malformed or missing-reference validation errors into
argErr; return datastore and other non-validation errors unchanged so callers
can use their transient-error fallback. Preserve the existing per-script
aggregation behavior for expected invalid-input errors.
- Around line 400-409: The ValidateReferencedCustomHostVitals block must
validate the effective scripts after package extraction and replacement has
populated payload.InstallScript, payload.PostInstallScript, and
payload.UninstallScript. Move this validation from its current position to
immediately after script/file preprocessing and before persistence, preserving
the existing InvalidArgumentError handling and transient-error wrapping.

In
`@frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tsx`:
- Around line 119-122: Update the TableCount invocation in renderCount to use
the noun "variables" instead of "secrets", so the displayed count matches the
Global Variables page context.

In `@server/datastore/mysql/custom_host_vitals.go`:
- Around line 280-299: The SetHostCustomHostVitalValue flow must reject
nonexistent hosts before committing the custom vital upsert. Verify and lock
hostID within the same transaction before the INSERT, or otherwise return a
NotFound error for sql.ErrNoRows; ensure that lookup errors propagate and the
value write is rolled back rather than creating an orphan row.
- Around line 95-105: Update the existence check in the affected == 0 branch of
the custom host vital update flow to query through the writer connection rather
than ds.reader(ctx). Preserve the existing no-op rename handling and error
mapping while ensuring replica lag cannot produce a false NotFound result.

In `@server/datastore/mysql/secret_variables.go`:
- Around line 360-400: Replace the content-based format detection in
expandDocumentVars with an explicit document-format argument, using the known
format from ExpandEmbeddedSecrets and ExpandCustomHostVitals. Apply JSON or XML
escaping only for FormatJSON or FormatXML, and leave script/unknown documents
unescaped via FormatNone.

In `@server/service/custom_host_vitals.go`:
- Around line 267-269: Update the dryRun branch in the surrounding
reconciliation method so it performs the same deletion-protection validation for
omitted vitals before returning success. Use a rollback-only transaction or
dedicated datastore validation to execute the full reconciliation checks without
committing changes, while preserving normal commit behavior when dryRun is
false.

In `@server/service/labels.go`:
- Around line 155-180: Update validateCustomHostVitalCriteria to recursively
traverse fleet.HostVitalCriteria And and Or sub-criteria, collect every
referenced CustomHostVitalID, and validate all collected IDs with
GetCustomHostVitals. Preserve the existing invalid-criteria and datastore error
handling, and return an invalid-argument error when any referenced custom host
vital does not exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bc9b9b03-ded9-4414-831f-56b4e73bc6a3

📥 Commits

Reviewing files that changed from the base of the PR and between 2262551 and a5aafab.

⛔ Files ignored due to path filters (2)
  • changes/44954-custom-host-vitals.md is excluded by !**/*.md
  • docs/REST API/rest-api.md is excluded by !**/*.md
📒 Files selected for processing (121)
  • cmd/fleetctl/fleetctl/generate_gitops.go
  • cmd/fleetctl/fleetctl/generate_gitops_test.go
  • cmd/fleetctl/fleetctl/get_test.go
  • cmd/fleetctl/fleetctl/mdm_test.go
  • cmd/fleetctl/fleetctl/scripts_test.go
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml
  • cmd/fleetctl/fleetctl/testing_utils/testing_utils.go
  • cmd/fleetctl/integrationtest/gitops/gitops_integration_test.go
  • ee/server/service/maintained_apps.go
  • ee/server/service/setup_experience.go
  • ee/server/service/setup_experience_test.go
  • ee/server/service/software_installers.go
  • ee/server/service/software_installers_test.go
  • frontend/components/CommandPalette/CommandPalette.tsx
  • frontend/components/CommandPalette/groups/controls.ts
  • frontend/components/CommandPalette/helpers.tests.ts
  • frontend/components/CommandPalette/helpers.ts
  • frontend/interfaces/activity.ts
  • frontend/interfaces/custom_host_vitals.ts
  • frontend/interfaces/host.ts
  • frontend/interfaces/label.ts
  • frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx
  • frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx
  • frontend/pages/ManageControlsPage/Variables/Variables.tsx
  • frontend/pages/ManageControlsPage/Variables/VariablesNavItems.tsx
  • frontend/pages/ManageControlsPage/Variables/_styles.scss
  • frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tests.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTableConfig.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/index.ts
  • frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tests.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariablesTableConfig.tsx
  • frontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/index.ts
  • frontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/AddCustomHostVitalModal.tsx
  • frontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/index.ts
  • frontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/DeleteCustomHostVitalModal.tsx
  • frontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/index.ts
  • frontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/EditCustomHostVitalModal.tsx
  • frontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/index.ts
  • frontend/pages/ManageControlsPage/Variables/helpers.ts
  • frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx
  • frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
  • frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
  • frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx
  • frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tests.tsx
  • frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tsx
  • frontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/index.ts
  • frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx
  • frontend/pages/hosts/details/cards/Vitals/Vitals.tsx
  • frontend/pages/hosts/details/cards/Vitals/_styles.scss
  • frontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tests.tsx
  • frontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tsx
  • frontend/pages/hosts/details/modals/EditHostVitalModal/_styles.scss
  • frontend/pages/hosts/details/modals/EditHostVitalModal/index.ts
  • frontend/pages/labels/NewLabelPage/NewLabelPage.tsx
  • frontend/pages/labels/NewLabelPage/helpers.tests.ts
  • frontend/pages/labels/NewLabelPage/helpers.ts
  • frontend/router/index.tsx
  • frontend/router/paths.ts
  • frontend/services/entities/custom_host_vitals.ts
  • frontend/services/entities/labels.ts
  • frontend/services/entities/variables.ts
  • frontend/utilities/endpoints.ts
  • pkg/spec/gitops.go
  • pkg/spec/gitops_test.go
  • server/authz/policy.rego
  • server/authz/policy_test.go
  • server/datastore/mysql/apple_mdm_batched.go
  • server/datastore/mysql/custom_host_vitals.go
  • server/datastore/mysql/custom_host_vitals_test.go
  • server/datastore/mysql/hosts.go
  • server/datastore/mysql/hosts_test.go
  • server/datastore/mysql/labels_test.go
  • server/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals.go
  • server/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals_test.go
  • server/datastore/mysql/schema.sql
  • server/datastore/mysql/secret_variables.go
  • server/datastore/mysql/secret_variables_test.go
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_installers_test.go
  • server/fleet/activities.go
  • server/fleet/android.go
  • server/fleet/android_test.go
  • server/fleet/api_custom_host_vitals.go
  • server/fleet/apple_mdm.go
  • server/fleet/custom_host_vitals.go
  • server/fleet/custom_host_vitals_test.go
  • server/fleet/datastore.go
  • server/fleet/embedded_variables.go
  • server/fleet/hosts.go
  • server/fleet/labels.go
  • server/fleet/secret_variables.go
  • server/fleet/service.go
  • server/mdm/apple/profile_processor.go
  • server/mdm/microsoft/custom_host_vitals_test.go
  • server/mdm/microsoft/profile_variables.go
  • server/mock/datastore_mock.go
  • server/mock/service/service_mock.go
  • server/service/apple_mdm.go
  • server/service/apple_mdm_ddm_test.go
  • server/service/apple_mdm_test.go
  • server/service/client.go
  • server/service/client_custom_host_vitals.go
  • server/service/custom_host_vitals.go
  • server/service/custom_host_vitals_resolution_test.go
  • server/service/custom_host_vitals_test.go
  • server/service/devices_endpoint_test.go
  • server/service/handler.go
  • server/service/hosts.go
  • server/service/hosts_test.go
  • server/service/integration_custom_host_vitals_test.go
  • server/service/labels.go
  • server/service/labels_test.go
  • server/service/mdm.go
  • server/service/microsoft_mdm.go
  • server/service/orbit.go
  • server/service/scripts.go
  • server/service/testing_utils_test.go
  • server/service/windows_mdm_profiles.go

Comment thread ee/server/service/software_installers.go
Comment thread ee/server/service/software_installers.go
Comment thread server/datastore/mysql/custom_host_vitals.go
Comment thread server/datastore/mysql/custom_host_vitals.go
Comment thread server/datastore/mysql/secret_variables.go
Comment thread server/service/custom_host_vitals.go
Comment thread server/service/labels.go
- Parse vital IDs with strconv.IntSize to avoid uint truncation (CodeQL)
- Check host-vital existence on the writer after a no-op update (CodeRabbit)
- Guard NaN custom_host_vital_id in label criteria (Copilot)
- Rename GlobalVariables count noun to "variables" (CodeRabbit)
- Make the Custom host vitals tab server-side paginated with page/sort
  persisted in the URL; add tab-scoped tests
getHostDetails now calls ds.GetHostCustomHostVitals, but this test (from
the host-names feature merged from main) set up its mock without
GetHostCustomHostVitalsFunc, panicking and taking down the whole
server/service test binary. Initialize the func like the other
getHostDetails tests.
@nulmete
nulmete merged commit b10642b into main Jul 15, 2026
50 checks passed
@nulmete
nulmete deleted the 44954-custom-host-vitals branch July 15, 2026 22:35
nulmete added a commit that referenced this pull request Jul 29, 2026
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49421

Custom host vitals (`$FLEET_HOST_VITAL_<id>`) already worked in scripts
and Apple/Windows configuration profiles, but Android configuration
profiles and managed app configuration explicitly rejected them at
upload to keep parity with `$FLEET_SECRET_*`. This left admins unable to
inject per-host vitals (e.g. an asset tag) into Android MDM
configuration the same way they can for every other platform.

For more context, prior PRs:
- #49334
- #49586

# Checklist for submitter

- [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

- [x] QA'd all new/changed functionality manually

- Created an "Asset tag" host vital.
- Enrolled an Android device.
- Initially the test profile showed as "Failed" because no value was set
for the vital.
- Set a value for the vital, saw that it went from Enforcing to
Verified.

<img width="1446" height="510" alt="Screenshot 2026-07-24 at 8 57 46 AM"
src="https://github.com/user-attachments/assets/c0e2348c-e521-48f3-85cd-6f884689b2cd"
/>
<img width="1520" height="936" alt="Screenshot 2026-07-24 at 8 56 56 AM"
src="https://github.com/user-attachments/assets/169b9545-ec7a-429b-8f45-0e2740f61c77"
/>
<img width="1607" height="1136" alt="Screenshot 2026-07-24 at 8 57
30 AM"
src="https://github.com/user-attachments/assets/a8213745-b224-4a36-a54d-32152a15c377"
/>

Also tested the rejection cases:
- trying to upload a profile with an invalid custom host vital id
(either a non-numeric value, a numeric but non-existent ID, and
referencing a vital as a JSON key instead of a value)
- deleting a vital referenced in a profile



https://github.com/user-attachments/assets/e8b4acde-ddf4-41c0-b00a-5ab4945d0bc2



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **New Features**
* Android app configurations and profiles now support custom host vital
placeholders (`$FLEET_HOST_VITAL_<id>`).
* Custom host vital values are expanded per device during Android
delivery.
* Managed Android profiles/configurations are automatically resent when
a referenced vital value changes.

* **Bug Fixes**
* Added validation for malformed, missing, or undefined vital references
during Android app association and profile/config uploads.
  * Prevented deletion of vitals referenced by Android profiles.
* Improved error handling and delivery failure details when a device
lacks a required vital value.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add/edit/delete custom host vitals

5 participants