Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .gitlab/windows/build/package_build/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,70 @@ windows_zip_ddot_x64:
extends: .windows_zip_base
variables:
OMNIBUS_TARGET: ddot

# Like .windows_zip_base, but for omnibus targets whose build invokes Bazel (e.g. the eudm
# extension's ai-usage Rust native host). Uses the Bazel-cache docker wrapper (as .windows_msi_base does) instead of a
# plain `docker run`, so `bazel run` has the cache/toolchain available inside the container.
.windows_zip_bazel_base:
stage: package_build
rules:
- !reference [.except_mergequeue]
- when: on_success
extends: [.windows_docker_default, .bazel:defs:cache:windows]
needs: ["go_deps"]
variables:
ARCH: "x64"
script:
- $ErrorActionPreference = "Stop"
- !reference [.sanitize_goproxy_windows]
- if (Test-Path omnibus\pkg) { remove-item -recurse -force omnibus\pkg }
- mkdir omnibus\pkg
- >
.\tools\ci\docker-run-with-bazel-cache.ps1
-m 24576M
-v "$(Get-Location):c:\mnt"
-e DDA_FEATURE_FLAGS_CI_SSM_KEY_WINDOWS="${DDA_FEATURE_FLAGS_CI_SSM_KEY_WINDOWS}"
-e GITLAB_CI
-e CI_JOB_ID
-e CI_PIPELINE_ID
-e CI_PROJECT_NAME
-e CI_COMMIT_BRANCH
-e CI_JOB_NAME_SLUG
-e CI_COMMIT_REF_NAME
-e OMNIBUS_TARGET
-e WINDOWS_BUILDER=true
-e GOMODCACHE="c:\modcache"
-e GOPROXY
-e GONOSUMDB
-e AWS_NETWORKING=true
-e SIGN_WINDOWS_DD_WCS=true
-e WINDOWS_SIGNING_CERT
-e WINDOWS_SIGNING_CONFIG
-e TARGET_ARCH="$ARCH"
-e BUCKET_BRANCH
-e S3_OMNIBUS_CACHE_BUCKET
-e S3_OMNIBUS_GIT_CACHE_BUCKET
-e BUNDLE_MIRROR__RUBYGEMS__ORG
-e PIP_INDEX_URL
-e AGENT_API_KEY_ORG2
-e OMNIBUS_GIT_CACHE_DIR=${Env:TEMP}/${CI_PIPELINE_ID}/omnibus-git-cache
-e AGENT_FLAVOR
-e OMNIBUS_RUBY_VERSION
-e PYTHONUTF8=1
-e DEPLOY_AGENT
-e CI_JOB_TOKEN
-e CI_IDENTITIES_GITLAB_ID_TOKEN
${WINBUILDIMAGE}
powershell -C "c:\mnt\tasks\winbuildscripts\Build-OmnibusTarget.ps1 -BuildOutOfSource 1 -InstallDeps 1 -CheckGoVersion 1"
- If ($lastExitCode -ne "0") { throw "Previous command returned $lastExitCode" }
- get-childitem omnibus\pkg\pipeline-$CI_PIPELINE_ID
- !reference [.upload_sbom_artifacts_windows]
artifacts:
expire_in: 2 weeks
paths:
- omnibus/pkg/pipeline-$CI_PIPELINE_ID

windows_zip_eudm_x64:
extends: .windows_zip_bazel_base
variables:
OMNIBUS_TARGET: eudm
15 changes: 15 additions & 0 deletions .gitlab/windows/build/packaging/oci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ agent_win_oci:
artifacts: true
- job: "windows_zip_ddot_x64"
artifacts: true
- job: "windows_zip_eudm_x64"
artifacts: true
variables:
OCI_PRODUCT: "datadog-agent"
WIN_SOURCE_TYPE: "msi"
Expand Down Expand Up @@ -95,6 +97,19 @@ agent_win_oci:

EXTRA_FLAGS="${EXTRA_FLAGS} --extension ddot=${DDOT_EXTENSION_DIR}"
fi
- |
EUDM_ARTIFACT=$(ls -1 "${PIPE_DIR}"/datadog-agent-eudm-*x86_64.zip 2>/dev/null || true)
if [ -n "$EUDM_ARTIFACT" ]; then
EUDM_INPUT_DIR="$(mktemp -d)"
EUDM_EXTENSION_DIR="$(mktemp -d)"
echo "Extracting EUDM extension to ${EUDM_INPUT_DIR}"
unzip -q "${EUDM_ARTIFACT}" -d "${EUDM_INPUT_DIR}"

# Copy entire EUDM extension structure (flat: AI Usage host binary + example config) to extension directory
cp -r "${EUDM_INPUT_DIR}"/* "${EUDM_EXTENSION_DIR}"/

EXTRA_FLAGS="${EXTRA_FLAGS} --extension eudm=${EUDM_EXTENSION_DIR}"
fi
- |
INSTALLER_BIN=$(ls -1 "${PIPE_DIR}"/datadog-installer-*-x86_64.exe 2>/dev/null | head -1)
if [ -n "$INSTALLER_BIN" ]; then
Expand Down
30 changes: 30 additions & 0 deletions .gitlab/windows/test/e2e_install_packages/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,36 @@ new-e2e-windows-ddot-package-a7-x86_64:
- !reference [.manual]
timeout: 45m

new-e2e-windows-eudm-package-a7-x86_64:
stage: e2e_install_packages
extends:
- .new_e2e_template
- .new-e2e_agent_a7
variables:
TARGETS: ./tests/installer/windows
EXTRA_PARAMS: --run "TestEUDMExtensionViaMSI"
TEAM: windows-products
needs:
- !reference [.needs_new_e2e_template]
- deploy_windows_testing-a7
- deploy_installer_oci
- deploy_agent_oci
- qa_installer_script_windows
before_script:
- CURRENT_AGENT_ASSERT_VERSION=$(dda inv agent.version) || exit $?; export CURRENT_AGENT_ASSERT_VERSION
- CURRENT_AGENT_ASSERT_PACKAGE_VERSION=$(dda inv agent.version --url-safe)-1 || exit $?; export CURRENT_AGENT_ASSERT_PACKAGE_VERSION
- !reference [.new_e2e_template, before_script]
parallel:
matrix:
- EXTRA_PARAMS: --run "TestEUDMExtensionViaMSI$/TestInstallAndUninstallEUDMExtension$"
- EXTRA_PARAMS: --run "TestEUDMExtensionViaMSI$/TestUpgradeEnablesEUDMExtension$"
- EXTRA_PARAMS: --run "TestEUDMExtensionViaMSI$/TestInstallWithoutEUDMSkipsExtension$"
rules:
- !reference [.on_deploy]
- !reference [.on_e2e_or_windows_installer_changes]
- !reference [.manual]
timeout: 45m

new-e2e-installer-windows:
extends: .new_e2e_template
rules:
Expand Down
21 changes: 21 additions & 0 deletions cmd/ai_prompt_logger/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,24 @@ pkg_install(
}),
target_compatible_with = _MACOS_OR_WINDOWS,
)

# Windows-only flat layout for the "eudm" fleet installer extension layer. The binary and
# example config are placed at the layer root, matching the paths that the extension hook expects
# under ext/eudm/ (see pkg/fleet/installer/packages/datadog_agent_eudm_windows.go). The
# Chrome manifest and registry entries are created at install time by the extension hook, so only
# the binary and example config ship in the layer.
pkg_files(
name = "ai_usage_extension_files",
srcs = [
"ai_usage_native_host.yaml.example",
":ai-usage-agent-native-host",
],
target_compatible_with = ["@platforms//os:windows"],
visibility = ["//packages:__subpackages__"],
)

pkg_install(
name = "install-extension",
srcs = [":ai_usage_extension_files"],
target_compatible_with = ["@platforms//os:windows"],
)
22 changes: 12 additions & 10 deletions cmd/ai_prompt_logger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Chrome **Native Messaging** host that forwards **AI usage** events to the **local Datadog Agent** via the trace receiver **EVP proxy** and the dedicated AI usage track, not directly to Datadog with a customer API key.

Chrome registration for **macOS** is done by the **Datadog Agent** package (`postinst` + per-user manifests). On **Windows**, the MSI installs a machine-wide Chrome Native Messaging Host registration under HKLM — see the **Windows install** section below.
Chrome registration for **macOS** is done by the **Datadog Agent** package (`postinst` + per-user manifests). On **Windows**, registration is owned by the **ai-usage** fleet installer extension when End User Device Monitoring is enabled — see the **Windows install** section below.

The same binary can also run as a standalone desktop monitor with `--desktop-monitor`. In that mode it is a separate long-lived user-session process and does not use Chrome's native messaging protocol.

Expand Down Expand Up @@ -38,7 +38,7 @@ Settings live in **`ai_usage_native_host.yaml`** (see **`ai_usage_native_host.ya

On a **packaged macOS** agent, Chrome is pointed at **`embedded/bin/run_ai_usage_native_host.sh`**, which **`exec`s** the binary with **`--config=$install_root/etc/ai_usage_native_host.yaml`**. On first config creation, the installer generates `trace_agent_url` from the Agent's `apm_config.receiver_port` in `datadog.yaml`.

On a **packaged Windows** agent (MSI), Chrome is pointed directly at **`bin\agent\ai-usage-agent-native-host.exe`** through a machine-wide HKLM Native Messaging Host registration; no shell wrapper is used.
On a **packaged Windows** agent with End User Device Monitoring enabled, the **ai-usage** fleet installer extension points Chrome directly at the **`ai-usage-agent-native-host.exe`** shipped in the extension layer through a machine-wide HKLM Native Messaging Host registration; no shell wrapper is used.

EVP / Agent URL behaviour (defaults):

Expand Down Expand Up @@ -117,27 +117,29 @@ Response:
{ "type": "SEND_USAGE_EVENT_RESULT", "success": true }
```

## Windows install (MSI)
## Windows install (EUDM fleet extension)

The Datadog Agent Windows MSI ships the native host under the Agent install directory and registers it with Chrome machine-wide:
On Windows, the native host is delivered by the `ai-usage` fleet installer extension. The extension is installed when End User Device Monitoring is enabled (`DD_INFRASTRUCTURE_MODE=end_user_device`) and skipped otherwise. The host binary and its Chrome manifest ship inside the extension layer and run in place from there — nothing is copied into Program Files. The install hook generates the manifest, writes the active config, and registers the host with Chrome machine-wide:

| Path (default install) | Contents |
|---|---|
| `C:\Program Files\Datadog\Datadog Agent\bin\agent\ai-usage-agent-native-host.exe` | The host binary (signed). |
| `C:\Program Files\Datadog\Datadog Agent\bin\agent\dist\com.datadoghq.ai_usage_agent.native_host.json` | Chrome Native Messaging Host manifest generated by the MSI. |
| `C:\ProgramData\Datadog\Installer\packages\datadog-agent\<version>\ext\eudm\ai-usage-agent-native-host.exe` | The host binary (signed), shipped in the extension layer and run in place. |
| `C:\ProgramData\Datadog\Installer\packages\datadog-agent\<version>\ext\eudm\com.datadoghq.ai_usage_agent.native_host.json` | Chrome Native Messaging Host manifest generated by the extension install hook. |
| `C:\ProgramData\Datadog\ai_usage_native_host.yaml.example` | Config example. |
| `C:\ProgramData\Datadog\ai_usage_native_host.yaml` | Active config generated by the MSI if missing. |
| `C:\ProgramData\Datadog\ai_usage_native_host.yaml` | Active config generated by the extension install hook if missing. |

The MSI writes machine-wide registry keys so every Chrome user on the machine can discover the same native host:
The extension install hook writes machine-wide registry keys so every Chrome user on the machine can discover the same native host:

```
HKLM\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.datadoghq.ai_usage_agent.native_host
HKLM\SOFTWARE\WOW6432Node\Google\Chrome\NativeMessagingHosts\com.datadoghq.ai_usage_agent.native_host
```

The default value for both keys points to the manifest under `bin\agent\dist`. The manifest's `path` field points to the bundled host executable under `bin\agent`, and `allowed_origins` uses the installer default Chrome extension ID (`gkmbhgbippkmmmidcikijiblbagbjgjj`). The active config's `trace_agent_url` is generated from the Agent's `apm_config.receiver_port` in `datadog.yaml`.
The default value for both keys points to the manifest in the extension layer (the `ext\eudm` directory above). The manifest's `path` field points to the host executable in that same directory, and `allowed_origins` uses the installer default Chrome extension ID (`gkmbhgbippkmmmidcikijiblbagbjgjj`). The active config's `trace_agent_url` is generated from the Agent's `apm_config.receiver_port` in `datadog.yaml`.

The MSI registers a Task Scheduler logon task named `Datadog AI Usage Agent`. The task launches the same bundled host executable with `--desktop-monitor --config "C:\ProgramData\Datadog\ai_usage_native_host.yaml"` in the interactive user session. Chrome native messaging registration remains separate and continues to launch the host without `--desktop-monitor`. When the `DatadogAgent` service is stopped, the desktop monitor remains resident but skips desktop scans until SCM reports the service running again.
The extension install hook registers a Task Scheduler logon task named `Datadog AI Usage Agent`. The task launches the same host executable from the extension layer with `--desktop-monitor --config "C:\ProgramData\Datadog\ai_usage_native_host.yaml"` in the interactive user session. Chrome native messaging registration remains separate and continues to launch the host without `--desktop-monitor`. When the `DatadogAgent` service is stopped, the desktop monitor remains resident but skips desktop scans until SCM reports the service running again.

The extension remove hook deletes the scheduled task and Chrome registry keys and stops the running host. The host binary and manifest live in the extension layer and are removed with the extension itself. It preserves the user-editable `C:\ProgramData\Datadog\ai_usage_native_host.yaml`.

## macOS install (DMG)

Expand Down
2 changes: 0 additions & 2 deletions omnibus/config/projects/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@
windows_symbol_stripping_file "#{install_dir}\\bin\\agent\\dd-procmgrd.exe"
windows_symbol_stripping_file "#{install_dir}\\bin\\agent\\dd-procmgr.exe"
windows_symbol_stripping_file "#{install_dir}\\bin\\agent\\agent-data-plane.exe"
windows_symbol_stripping_file "#{install_dir}\\bin\\agent\\ai-usage-agent-native-host.exe"

if windows_signing_enabled?
# Sign additional binaries from here.
Expand Down Expand Up @@ -370,7 +369,6 @@
"#{install_dir}\\bin\\agent\\dd-procmgrd.exe",
"#{install_dir}\\bin\\agent\\dd-procmgr.exe",
"#{install_dir}\\bin\\agent\\agent-data-plane.exe",
"#{install_dir}\\bin\\agent\\ai-usage-agent-native-host.exe",
]

BINARIES_TO_SIGN.each do |bin|
Expand Down
80 changes: 80 additions & 0 deletions omnibus/config/projects/eudm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Unless explicitly stated otherwise all files in this repository are licensed
# under the Apache License Version 2.0.
# This product includes software developed at Datadog (https:#www.datadoghq.com/).
# Copyright 2016-present Datadog, Inc.
require "./lib/ostools.rb"
require "./lib/project_helpers.rb"

# The "eudm" (End User Device Monitoring) fleet installer extension layer for the datadog-agent
# OCI package (Windows only). It currently carries the AI Usage native host feature; this project
# builds + signs the native host binary and produces datadog-agent-eudm-<version>-1-x86_64.zip,
# which the agent_win_oci job passes to `datadog-package create --extension eudm=<dir>`.
# Mirrors omnibus/config/projects/ddot.rb.

name 'eudm'
package_name 'datadog-agent-eudm'

license "Apache-2.0"
license_file "../LICENSE"

third_party_licenses "../LICENSE-3rdparty.csv"

homepage 'http://www.datadoghq.com'

if ENV.has_key?("OMNIBUS_WORKERS_OVERRIDE")
COMPRESSION_THREADS = ENV["OMNIBUS_WORKERS_OVERRIDE"].to_i
else
COMPRESSION_THREADS = 1
end

if ENV.has_key?("OMNIBUS_GIT_CACHE_DIR")
Omnibus::Config.use_git_caching true
Omnibus::Config.git_cache_dir ENV["OMNIBUS_GIT_CACHE_DIR"]
end

# Dedicated install_dir so the produced ZIP unzips to the flat ext/eudm/ layout the extension
# hook expects (binary + example config at the root). Must not contain spaces (Omnibus does not
# quote the Git commands it launches). Matches EUDM_ARTIFACT_DIR in tasks/msi.py.
INSTALL_DIR = 'C:/opt/datadog-agent-eudm'
install_dir INSTALL_DIR

third_party_licenses_path "LICENSES-eudm"
license_file_path "LICENSE-eudm"
json_manifest_path File.join(install_dir, "version-manifest.eudm.json")
text_manifest_path File.join(install_dir, "version-manifest.eudm.txt")

# build_version is computed by an invoke command/function.
# We can't call it directly from there, we pass it through the environment instead.
build_version ENV['PACKAGE_VERSION']

build_iteration 1

description 'Datadog AI Usage native messaging host'

maintainer 'Datadog Packages <package@datadoghq.com>'

# ------------------------------------
# Dependencies
# ------------------------------------
dependency 'ai-usage-agent'

# the stripper will drop the symbols in a `.debug` folder in the installdir
# we want to make sure that directory is not in the main build, while present
# in the debug package.
strip_build true
debug_path ".debug" # the strip symbols will be in here

if windows_target?
windows_symbol_stripping_file "#{install_dir}\\ai-usage-agent-native-host.exe"
sign_file "#{install_dir}\\ai-usage-agent-native-host.exe"
end

# ------------------------------------
# Packaging
# ------------------------------------

# Windows only: skip the MSI packager and rely on the default Windows ZIP packager, which emits
# datadog-agent-eudm-<version>-1-x86_64.zip from the contents of install_dir.
package :msi do
skip_packager true
end
33 changes: 33 additions & 0 deletions omnibus/config/software/ai-usage-agent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Unless explicitly stated otherwise all files in this repository are licensed
# under the Apache License Version 2.0.
# This product includes software developed at Datadog (https:#www.datadoghq.com/).
# Copyright 2016-present Datadog, Inc.

require './lib/ostools.rb'

name 'ai-usage-agent'

source path: '..',
options: {
exclude: [
"**/.cache/**/*",
"**/testdata/**/*",
],
}
relative_path 'src/github.com/DataDog/datadog-agent'

always_build true

build do
license :project_license

# The AI Usage Chrome native messaging host is a Rust binary built by Bazel. It ships inside the
# "eudm" fleet installer extension layer (Windows only), so we stage the flat layout the
# extension hook expects — the binary and the example config at the install_dir root:
# <install_dir>/ai-usage-agent-native-host.exe
# <install_dir>/ai_usage_native_host.yaml.example
# (see pkg/fleet/installer/packages/datadog_agent_eudm_windows.go).
if windows_target?
command "bazel run -- //cmd/ai_prompt_logger:install-extension --destdir=#{install_dir}", :live_stream => Omnibus.logger.live_stream(:info)
end
end
9 changes: 0 additions & 9 deletions omnibus/config/software/datadog-agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -294,15 +294,6 @@
copy "#{systray_build_dir}/agent.png", "#{app_temp_dir}/MacOS/"
end

if windows_target?
# AI usage Chrome native messaging host (Rust). Mirrors the macOS osx_target? branch above:
# the Bazel target installs the .exe into bin/agent (Windows convention; see
# //pkg/procmgr/rust:install for the same Linux-vs-Windows prefix split). The final Chrome
# Native Messaging Host manifest is staged under bin/agent/dist so the MSI owns the file
# during rollback/uninstall. The MSI custom action rewrites it with the final installation path.
command "bazel run #{bazel_flags} -- //cmd/ai_prompt_logger:install --destdir=#{install_dir}", :env => env, :live_stream => Omnibus.logger.live_stream(:info)
end

# APM Hands Off config file
if linux_target?
copy 'pkg/config/example/application_monitoring.yaml.example', "#{conf_dir}/application_monitoring.yaml.example"
Expand Down
3 changes: 3 additions & 0 deletions pkg/fleet/installer/packages/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ go_library(
"apm_library_dotnet_windows.go",
"datadog_agent_ddot_linux.go",
"datadog_agent_ddot_windows.go",
"datadog_agent_eudm_windows.go",
"datadog_agent_extensions.go",
"datadog_agent_linux.go",
"datadog_agent_nix.go",
Expand Down Expand Up @@ -77,6 +78,7 @@ go_library(
"@org_golang_x_sys//windows/registry",
"@org_golang_x_sys//windows/svc",
"@org_golang_x_sys//windows/svc/mgr",
"@org_golang_x_text//encoding/unicode",
],
"//conditions:default": [],
}),
Expand All @@ -86,6 +88,7 @@ go_test(
name = "packages_test",
srcs = [
"apm_inject_windows_test.go",
"datadog_agent_eudm_windows_test.go",
"datadog_agent_extensions_test.go",
"otel_config_common_test.go",
],
Expand Down
Loading
Loading