Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4dc2e5f
Route Required telemetry events through optional component DLL
richiemsft Nov 3, 2026
c6fd92b
Verify telemetry component signature and pass host path at init
richiemsft Nov 3, 2026
050e279
Pin telemetry component signer instead of matching the host
richiemsft Nov 3, 2026
e63aa1e
Revert "Pin telemetry component signer instead of matching the host"
richiemsft Nov 4, 2026
e6e6c5a
Revert "Verify telemetry component signature and pass host path at init"
richiemsft Nov 4, 2026
bd2cade
Revert "Route Required telemetry events through optional component DLL"
richiemsft Nov 4, 2026
16b4dd5
Read telemetry routing metadata from a signed component at build time
richiemsft Nov 4, 2026
d652497
Remove the administrative policy check from Required telemetry
richiemsft Nov 4, 2026
aa93268
Remove real telemetry classification values from public repo
richiemsft Nov 4, 2026
61146c8
Bundle signed telemetry component DLLs for release source builds
richiemsft Nov 4, 2026
13dcd0e
Keep telemetry test executor unrouted; correct target dir guidance
richiemsft Nov 4, 2026
f634e06
Remove MXC administrative telemetry registry policy
richiemsft Nov 4, 2026
182b15c
Drop generated provider definition details from telemetry docs
richiemsft Nov 4, 2026
80e991b
Remove target directory note from telemetry docs
richiemsft Nov 4, 2026
55ee459
Note that ETW assertion keyword values are mock values
richiemsft Nov 4, 2026
170470b
Address telemetry component review feedback
richiemsft Nov 4, 2026
2401c42
Revert removal of MXC administrative telemetry registry policy
richiemsft Nov 4, 2026
caaeb89
Document that development Required events ignore administrative policy
richiemsft Nov 4, 2026
129b291
Fix telemetry component CI step under pwsh Stop error preference
richiemsft Nov 4, 2026
e9b9f35
Print tampered-build result and exit explicitly in telemetry componen…
richiemsft Nov 4, 2026
49d3a8b
Address review: local-only E2E build, build message, published policy…
richiemsft Nov 4, 2026
40ff4ec
Extract telemetry component checks into a separate CI job
richiemsft Nov 5, 2026
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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
* text=auto eol=lf

.github/workflows/*.lock.yml linguist-generated=true

*.dll binary
38 changes: 37 additions & 1 deletion .github/workflows/Build.Windows.Job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:

- name: Build debug executor for isolated telemetry smoke tests
env:
MXC_TELEMETRY_PROVIDER_GROUP_GUID: ''
MXC_TELEMETRY_COMPONENT_PATH: ''
MXC_TELEMETRY_TEST_KEYWORDS: '1'
run: cargo build --locked -p wxc --features "test-support isolation_session"

Expand Down Expand Up @@ -205,3 +205,39 @@ jobs:
src/target/${{ matrix.target }}/release/aci-edge-sandboxes/
if-no-files-found: error
retention-days: 1

telemetry-component-check:
name: Telemetry component (${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runner: windows-2025
- arch: arm64
runner: windows-11-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Surface toolchain file at repo root
shell: pwsh
run: Copy-Item src\rust-toolchain.toml rust-toolchain.toml

- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0
with:
override: false
rustflags: ''

- name: Point cargo at the MxcDependencies feed
uses: ./.github/actions/setup-cargo-feed

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: src -> target/telemetry-component-check
key: telemetry-component-${{ matrix.arch }}
cache-on-failure: false

- name: Verify telemetry component build checks
shell: pwsh
run: .\scripts\ci\check-telemetry-component.ps1
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,17 @@ Official Microsoft builds send Required diagnostic data to Microsoft for the
development `1.1.0-alpha` request contract. MXC is moving to Required telemetry
to keep MXC secure, reliable, and up to date: four events describe execution
start and end, categorized failures, and process exits. They need no user
consent or per-request opt-in, and an administrator can block them with
machine-wide policy.
consent or per-request opt-in, and administrative policy does not block them.

Published `0.9.0-alpha` and `1.0.0` requests keep optional telemetry. It is off
unless the run opts in, the Windows user has consented, and administrative
policy permits collection. An administrator can block that telemetry but cannot
grant consent for the user.

Local open-source builds are not configured to route telemetry to Microsoft,
and telemetry is a no-op on non-Windows platforms. See
Local open-source debug builds are not configured to route telemetry to
Microsoft. Windows release builds from source use the bundled signed telemetry
component and are routed; set an empty `MXC_TELEMETRY_COMPONENT_PATH` to build
without routing. Telemetry is a no-op on non-Windows platforms. See
[telemetry policy and consent](docs/telemetry.md) for controls and privacy
details.

Expand Down
141 changes: 81 additions & 60 deletions docs/development/architecture/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ provider metadata through Rust constants and `write_event!` struct fields.
| Feature | WIL (`wil/TraceLogging.h`) | Rust `tracelogging` crate | MXC approach |
|---|---|---|---|
| **Provider group GUID** | WinExt telemetry group | `group_id("...")` in `define_provider!` | One `Microsoft.MXC` registration uses the WinExt group for all events; see [Private GUID Substitution](#private-guid-substitution-internal-builds) |
| **Sampling keywords** | Measures / Critical Data constants | Raw `u64` in `keyword(...)` | Generated legacy and Critical Data constants; ordinary local builds use `0x1` for both. Grouped builds select metadata by event and source contract as described below |
| **Sampling keywords** | Measures / Critical Data constants | Raw `u64` in `keyword(...)` | Supplied at build time by the signed telemetry component (see [Telemetry component](#telemetry-component)); ordinary local builds use `0x1` for both. Grouped builds select metadata by event and source contract as described below |
| **Common event fields** | `_GENERIC_PARTB_FIELDS_ENABLED` pattern | `struct("Name", { ... })` in `write_event!` | `struct("COMMON_MXC_PARAMS", { Version, Channel, IsDebugging, UTCReplace_AppSessionGuid })` |
| **Provider lifecycle** | `IMPLEMENT_TRACELOGGING_CLASS` singleton | `define_provider!` static + `register()`/`unregister()` | `OnceLock<ProviderState>` for version/channel, manual lifecycle |
| **Privacy Product** | Common Schema Part A product | `u16(...)` field | `PartA_PrivacyProduct = 11` (`MXC (Microsoft Execution Containers)`) on all events |
| **Privacy Data Category** | Common Schema Part A data category | `u16(...)` field | `PartA_PrivacyDataCategory = 1` (`Client Diagnostic Data`) on all events |
| **Privacy Data Tags** | `TelemetryPrivacyDataTag(PDT_*)` | `u64("PartA_PrivTags", &val)` field | Generated Usage/Performance constants selected with the keyword. Ordinary local builds use `0`; the field and its position remain unchanged |
| **Privacy Product** | Common Schema Part A product | `u16(...)` field | `PartA_PrivacyProduct` (the privacy-approved MXC product, from the telemetry component) on all events |
| **Privacy Data Category** | Common Schema Part A data category | `u16(...)` field | `PartA_PrivacyDataCategory` (Client Diagnostic Data, from the telemetry component) on all events |
| **Privacy Data Tags** | `TelemetryPrivacyDataTag(PDT_*)` | `u64("PartA_PrivTags", &val)` field | Usage/Performance values from the telemetry component, selected with the keyword. Ordinary local builds use `0`; the field and its position remain unchanged |
| **Activity tracking** | `DEFINE_TELEMETRY_ACTIVITY` | Manual `Opcode` | Not needed for current events |

The remaining gap (activity tracking) is not needed for current events.
Expand All @@ -60,12 +60,11 @@ If needed later, it can be added incrementally.
### Event keyword and privacy-tag selection

For Windows builds with the WinExt group configured, `MXC.ExecutionStart`, `MXC.ExecutionEnd`,
`MXC.Error`, and `MXC.ProcessExited` use `MICROSOFT_KEYWORD_CRITICAL_DATA`
(`0x0000_8000_0000_0000`) and `PDT_ProductAndServicePerformance`
(`0x01000000`) when the request uses development contract
`1.1.0-alpha`. Published `0.9.0-alpha` and `1.0.0` requests, and requests
without source-contract attribution, retain `MICROSOFT_KEYWORD_MEASURES`
(`0x0000_4000_0000_0000`) and `PDT_ProductAndServiceUsage` (`0x02000000`).
`MXC.Error`, and `MXC.ProcessExited` use the Critical Data keyword and the Performance
privacy tag when the request uses development contract `1.1.0-alpha`.
Published `0.9.0-alpha` and `1.0.0` requests, and requests without source-contract
attribution, retain the Measures keyword and the Usage privacy tag.
The numeric values are not in this repository; they come from the telemetry component.
Published requests retain Measures and Usage for the other event families.
Development `1.1.0-alpha` requests emit only those four events, suppressing
VerboseDenials, ProcessTimedOut, ProcessKillFailed, PolicyHash,
Expand All @@ -84,13 +83,9 @@ local denial reports and verbose artifacts remain available.
Development `1.1.0-alpha` requests collect the four Required events by default.
The request's `telemetry` object (including `enabled: false`) and MXC consent
are not consulted. Initialization runs for every such request and collection
requires both:

- a build eligible for Required collection: routed through the internal group,
or the debug-only local test mode (`MXC_TELEMETRY_TEST_KEYWORDS=1`); and
- administrative policy that permits it: an absent value, `1` or `3`. `0`, an
unsupported value, a wrong registry type or a read failure blocks. Policy is
rechecked for each emission.
requires only a build eligible for Required collection: routed through the internal
group, or the debug-only local test mode (`MXC_TELEMETRY_TEST_KEYWORDS=1`).
Administrative policy is not consulted, so no registry setting disables it.

The gate is selected from the request-scoped contract, so published and
development requests running concurrently do not share authorization. UTC's
Expand All @@ -114,13 +109,13 @@ provider-group input. This test input grants no collection authorization.
## Common Event Fields (Part A)

Every MXC telemetry event carries the WinExt-required Common Schema fields
`PartA_PrivacyProduct = 11` and `PartA_PrivacyDataCategory = 1`. The
`PartA_PrivacyProduct` and `PartA_PrivacyDataCategory`. The
`PartA_PrivTags` field is also always present. Ordinary public and local
builds use `0`; grouped builds use Performance for the four development
events and Usage otherwise, as described above. Product `11` is
privacy-approved and registered in the
WinExt product list. The provider-group identifier remains a secure build
input and must not be copied into source, tests, documentation, or commits.
events and Usage otherwise, as described above. The product is
privacy-approved and registered in the WinExt product list. The provider-group
identifier and these values come from the telemetry component and must not be
copied into source, tests, documentation, or commits.

## Common Event Fields (Part C)

Expand Down Expand Up @@ -156,14 +151,13 @@ does not start a collection session, upload events, contact a network service,
or create a background process. Events exist only when a local ETW consumer
enables the provider.

The provider is defined by the name `Microsoft.MXC`.
`MXC_TELEMETRY_PROVIDER_GROUP_GUID` supplies its WinExt routing group for all
events; event metadata is selected from the source contract — see
[Private GUID Substitution](#private-guid-substitution-internal-builds)
below. Public/OSS and normal development builds do not set it, so
`group_id(...)` is omitted, `MXC_EVENT_KEYWORD` is a provider-local bit with
no UTC meaning, and `MXC_PRIVACY_TAG` is `0`: events are ordinary local ETW
and are not directed to an upload pipeline by MXC.
The provider is defined by the name `Microsoft.MXC`. The signed telemetry
component supplies its WinExt routing group for all events; event metadata is
selected from the source contract — see
[Telemetry component](#telemetry-component) below. Debug and unrouted builds
have no group, so `group_id(...)` is omitted, `MXC_EVENT_KEYWORD` is a
provider-local bit with no UTC meaning, and `MXC_PRIVACY_TAG` is `0`: events
are ordinary local ETW and are not directed to an upload pipeline by MXC.

The M-ETW scope is Windows `process_container` and `isolation_session`.
Linux/macOS retain no-op telemetry stubs and are not requirement-behavior
Expand Down Expand Up @@ -502,8 +496,8 @@ free-form text.

The development `1.1.0-alpha` contract emits only the four Required events. MXC
moved these events to Required telemetry to keep MXC secure, reliable, and up to
date, so they are gated by administrative policy and provider availability, not
by the per-run request or MXC consent.
date, so they are gated by build eligibility and provider availability, not
by the per-run request, MXC consent or administrative policy.

Published contracts keep the optional model: emission is gated by the per-run
request, MXC-owned consent, administrative policy, and provider availability.
Expand Down Expand Up @@ -835,13 +829,13 @@ or production approval.

| Handle | Private build input | Metadata when configured |
|---|---|---|
| `MXC_PROVIDER` | `MXC_TELEMETRY_PROVIDER_GROUP_GUID` | Measures/Usage, or Critical Data/Performance for the four selected development events |
| `MXC_PROVIDER` | Signed telemetry component | Measures/Usage, or Critical Data/Performance for the four selected development events |

An absent or empty group produces a local-only registration with keyword
`0x1` and privacy tag `0` for every event. Malformed nonempty GUIDs fail the
Windows build. The obsolete `MXC_TELEMETRY_CORE_PROVIDER_GROUP_GUID` input
also fails Windows builds when nonempty; clear it rather than substituting
its value for WinExt. Non-Windows builds ignore group inputs.
An unrouted build produces a local-only registration with keyword
`0x1` and privacy tag `0` for every event. The obsolete
`MXC_TELEMETRY_CORE_PROVIDER_GROUP_GUID` and
`MXC_TELEMETRY_PROVIDER_GROUP_GUID` inputs fail Windows builds when nonempty.
Non-Windows builds ignore them.

The shared event writer selects the keyword and privacy tag using the event
and request's source contract, always on the same registration. Field names, types
Expand All @@ -862,30 +856,57 @@ The zero-value default (no group GUID → keyword `0x1`, privacy tag `0`)
mirrors Microsoft's own public WSL repo, which defines these same telemetry
constants as literal `0` for non-official builds.

### Telemetry component

Release builds read the group, keywords, privacy tags, product and data
category from a signed `mxc_telemetry.dll` that is built in a private
repository. The component only carries data; MXC never loads it at run time.

The signed x64 and ARM64 DLLs are committed under
`src/mxc-sdk/resources/telemetry-component/<arch>/` so source builds are
routed too. Release builds on Windows use the DLL for the build host's
architecture. Debug builds, and builds where that DLL is absent, are unrouted.
Comment thread
richiemsft marked this conversation as resolved.
Resolved
`MXC_TELEMETRY_COMPONENT_PATH` overrides the DLL; set it to an empty value to
force an unrouted release build. To refresh the DLLs, rebuild them in the
private pipeline and replace both files. The values are plain data in the DLL,
so anyone can read them, and forks inherit the routing.

When a DLL is selected on a Windows build host, the MXC build script:

1. opens the DLL with writers denied and verifies its Authenticode signature
and signer name, and fails the build if either is wrong;
2. loads it and calls the exported `mxc_telemetry_metadata_v1` function, which
returns `1;<group>;<keyword>;<privacy tag>;<critical keyword>;<critical privacy tag>;<product>;<data category>`;
3. passes the numbers to the crate as `MXC_BUILD_*` environment variables read
with `env!`.

Without a component, builds use placeholder values that are never routed.
`MXC_TELEMETRY_PROVIDER_GROUP_GUID` is no longer supported: a nonempty value
fails Windows builds, so routing always comes from the verified component.

### CI pipeline steps

The private pipeline owner supplies the approved WinExt group through
`MXC_TELEMETRY_PROVIDER_GROUP_GUID` on the final Windows Cargo build,
including builds of the SDK/FFI native library. Generation happens before
compilation; there is no runtime or post-build binary patch.
Do not copy private values into repository files or logs.
The private pipeline builds and signs the component DLL; its output is copied
into `src/mxc-sdk/resources/telemetry-component/`. Do not copy private values
into repository files or logs.

Published contracts retain their existing registration, Measures/Usage
metadata, request opt-in, consent and policy gates. Public/OSS builds omit
the group input and retain local-only metadata.
metadata, request opt-in, consent and policy gates. Debug builds and builds
with an empty `MXC_TELEMETRY_COMPONENT_PATH` retain local-only metadata.

The public Windows test artifact explicitly clears the group input. Its local
metadata tests cannot verify the private pipeline's group value or establish
Microsoft ingestion.
The public Windows test artifact explicitly clears the component input. Its
local metadata tests cannot verify the routing values or establish Microsoft
ingestion.

### WinExt routing acceptance

On an approved disposable Windows VM, use a build with the approved WinExt
group injected. For published contracts keep MXC's explicit consent, request
On an approved disposable Windows VM, use a release build with the signed
component. For published contracts keep MXC's explicit consent, request
opt-in, and permitting MXC policy satisfied. Development requests need no
consent or opt-in. Verify actual collection for both, including successful
process exit and operational failure; verify published suppression after
consent withdrawal and development suppression at policy value `0`.
consent withdrawal. Development requests keep emitting at every
administrative policy value, including `0`.

Windows Required/Optional settings do not authorize or gate these WinExt
events. Do not use acceptance at a Windows diagnostic-data level as evidence
Expand All @@ -895,13 +916,13 @@ metadata, not acceptance by the Microsoft collection pipeline.
### Local developer testing

```powershell
# Test with a dummy group GUID (not the real one) — telemetry-mode metadata
$env:MXC_TELEMETRY_PROVIDER_GROUP_GUID = '00000000-1111-2222-3333-444444444444'
cargo build -p mxc-sdk
# Release build with the bundled signed component (routed)
cargo build --release -p mxc-sdk

# Test without (public build) — local-only metadata
Remove-Item Env:\MXC_TELEMETRY_PROVIDER_GROUP_GUID
cargo build -p mxc-sdk
# Release build without routing (local-only metadata)
$env:MXC_TELEMETRY_COMPONENT_PATH = ''
cargo build --release -p mxc-sdk
Remove-Item Env:\MXC_TELEMETRY_COMPONENT_PATH
```

### What's public vs. private
Expand All @@ -910,9 +931,9 @@ cargo build -p mxc-sdk
|------|---------|-----|
| Provider name `"Microsoft.MXC"` | Yes | Standard ETW naming |
| Provider GUID `{7f10def4-a258-5fea-510e-2c3bb976687f}` | Yes | Derived from the name; identifies the provider, harmless |
| `build.rs` env var mechanism | Yes | Mechanism is public |
| `MXC_TELEMETRY_PROVIDER_GROUP_GUID` env var name | Yes | Key is public; value is private |
| Actual Microsoft telemetry group GUID | No | Private - set in CI only |
| `build.rs` component mechanism | Yes | Mechanism is public |
| Signed `mxc_telemetry.dll` | Yes | Committed so source builds are routed |
| Component source and pipeline | No | Private repository |

## SDK License Override (EULA for npm Package)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ required cases fail the job even when the runner exits successfully.
Every scheduled `isolation-session` job runs
`run_telemetry_etw_smoke_test.ps1 -IsolationSessionLifecycle -CheckKeywords -SkipClean` against
the architecture-matched telemetry test executor. That artifact is built with
an empty `MXC_TELEMETRY_PROVIDER_GROUP_GUID` input, so the single registration
an empty `MXC_TELEMETRY_COMPONENT_PATH` input, so the single registration
has no UTC routing. Consent is granted only in a temporary test store (needed by
the published contract; development Required collection ignores it) and policy is
redirected to an isolated HKCU key; real user consent and HKLM policy are not
Expand Down
16 changes: 16 additions & 0 deletions docs/development/build-and-test/pull-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ regressions also reject empty selections and skipped SDK streaming cases.
These checks do not require PSEC; scheduled ProcessContainer hosts execute the
prebuilt public SDK streaming tests from the Windows SDK test bundle.

### Telemetry component build checks

The Windows workflow also runs a separate `Telemetry component` check on native
x64 and arm64 hosts. It checks that the default release build loads the bundled
signed DLL, an empty component path builds without routing, and a tampered DLL
fails Authenticode verification. Each failure fails the check.

Run locally on Windows from the repository root:

```powershell
.\scripts\ci\check-telemetry-component.ps1
```

The script uses `src\target\telemetry-component-check` for its Cargo output,
removes the tampered copy, and restores the caller's environment.

### Native module lifetime regression

From `src`, run:
Expand Down
Loading
Loading