44954 custom host vitals - #49334
Conversation
…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
There was a problem hiding this comment.
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.
Codecov Report❌ Patch coverage is 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThis 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)
✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (2)
changes/44954-custom-host-vitals.mdis excluded by!**/*.mddocs/REST API/rest-api.mdis excluded by!**/*.md
📒 Files selected for processing (121)
cmd/fleetctl/fleetctl/generate_gitops.gocmd/fleetctl/fleetctl/generate_gitops_test.gocmd/fleetctl/fleetctl/get_test.gocmd/fleetctl/fleetctl/mdm_test.gocmd/fleetctl/fleetctl/scripts_test.gocmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.ymlcmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.ymlcmd/fleetctl/fleetctl/testing_utils/testing_utils.gocmd/fleetctl/integrationtest/gitops/gitops_integration_test.goee/server/service/maintained_apps.goee/server/service/setup_experience.goee/server/service/setup_experience_test.goee/server/service/software_installers.goee/server/service/software_installers_test.gofrontend/components/CommandPalette/CommandPalette.tsxfrontend/components/CommandPalette/groups/controls.tsfrontend/components/CommandPalette/helpers.tests.tsfrontend/components/CommandPalette/helpers.tsfrontend/interfaces/activity.tsfrontend/interfaces/custom_host_vitals.tsfrontend/interfaces/host.tsfrontend/interfaces/label.tsfrontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsxfrontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsxfrontend/pages/ManageControlsPage/Variables/Variables.tsxfrontend/pages/ManageControlsPage/Variables/VariablesNavItems.tsxfrontend/pages/ManageControlsPage/Variables/_styles.scssfrontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tests.tsxfrontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTab.tsxfrontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/CustomHostVitalsTableConfig.tsxfrontend/pages/ManageControlsPage/Variables/cards/CustomHostVitalsTab/index.tsfrontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tests.tsxfrontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariables.tsxfrontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/GlobalVariablesTableConfig.tsxfrontend/pages/ManageControlsPage/Variables/cards/GlobalVariables/index.tsfrontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/AddCustomHostVitalModal.tsxfrontend/pages/ManageControlsPage/Variables/components/AddCustomHostVitalModal/index.tsfrontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/DeleteCustomHostVitalModal.tsxfrontend/pages/ManageControlsPage/Variables/components/DeleteCustomHostVitalModal/index.tsfrontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/EditCustomHostVitalModal.tsxfrontend/pages/ManageControlsPage/Variables/components/EditCustomHostVitalModal/index.tsfrontend/pages/ManageControlsPage/Variables/helpers.tsfrontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsxfrontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsxfrontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsxfrontend/pages/hosts/details/cards/Activity/ActivityConfig.tsxfrontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tests.tsxfrontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/EditedCustomHostVitalValueActivityItem.tsxfrontend/pages/hosts/details/cards/Activity/ActivityItems/EditedCustomHostVitalValueActivityItem/index.tsfrontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsxfrontend/pages/hosts/details/cards/Vitals/Vitals.tsxfrontend/pages/hosts/details/cards/Vitals/_styles.scssfrontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tests.tsxfrontend/pages/hosts/details/modals/EditHostVitalModal/EditHostVitalModal.tsxfrontend/pages/hosts/details/modals/EditHostVitalModal/_styles.scssfrontend/pages/hosts/details/modals/EditHostVitalModal/index.tsfrontend/pages/labels/NewLabelPage/NewLabelPage.tsxfrontend/pages/labels/NewLabelPage/helpers.tests.tsfrontend/pages/labels/NewLabelPage/helpers.tsfrontend/router/index.tsxfrontend/router/paths.tsfrontend/services/entities/custom_host_vitals.tsfrontend/services/entities/labels.tsfrontend/services/entities/variables.tsfrontend/utilities/endpoints.tspkg/spec/gitops.gopkg/spec/gitops_test.goserver/authz/policy.regoserver/authz/policy_test.goserver/datastore/mysql/apple_mdm_batched.goserver/datastore/mysql/custom_host_vitals.goserver/datastore/mysql/custom_host_vitals_test.goserver/datastore/mysql/hosts.goserver/datastore/mysql/hosts_test.goserver/datastore/mysql/labels_test.goserver/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals.goserver/datastore/mysql/migrations/tables/20260715144547_CustomHostVitals_test.goserver/datastore/mysql/schema.sqlserver/datastore/mysql/secret_variables.goserver/datastore/mysql/secret_variables_test.goserver/datastore/mysql/software_installers.goserver/datastore/mysql/software_installers_test.goserver/fleet/activities.goserver/fleet/android.goserver/fleet/android_test.goserver/fleet/api_custom_host_vitals.goserver/fleet/apple_mdm.goserver/fleet/custom_host_vitals.goserver/fleet/custom_host_vitals_test.goserver/fleet/datastore.goserver/fleet/embedded_variables.goserver/fleet/hosts.goserver/fleet/labels.goserver/fleet/secret_variables.goserver/fleet/service.goserver/mdm/apple/profile_processor.goserver/mdm/microsoft/custom_host_vitals_test.goserver/mdm/microsoft/profile_variables.goserver/mock/datastore_mock.goserver/mock/service/service_mock.goserver/service/apple_mdm.goserver/service/apple_mdm_ddm_test.goserver/service/apple_mdm_test.goserver/service/client.goserver/service/client_custom_host_vitals.goserver/service/custom_host_vitals.goserver/service/custom_host_vitals_resolution_test.goserver/service/custom_host_vitals_test.goserver/service/devices_endpoint_test.goserver/service/handler.goserver/service/hosts.goserver/service/hosts_test.goserver/service/integration_custom_host_vitals_test.goserver/service/labels.goserver/service/labels_test.goserver/service/mdm.goserver/service/microsoft_mdm.goserver/service/orbit.goserver/service/scripts.goserver/service/testing_utils_test.goserver/service/windows_mdm_profiles.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.
<!-- 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 -->
Related issue: Resolves #44954
Checklist for submitter
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Testing
Summary by CodeRabbit
New Features
Bug Fixes