Skip to main content

Deploy to a Mac Fleet (MDM)

Push the Waxell AI-endpoint agent to your managed Macs with zero user clicks. The whole fleet enrolls against your tenant CA, each device discovers its AI apps, and metadata flows start reporting — all without a single "Allow" dialog on any machine.

You deliver exactly two artifacts through your MDM:

  1. A per-tenant configuration profile (.mobileconfig) — trusts your tenant CA, pre-approves the Waxell system extensions, and delivers the managed config.
  2. The signed, notarized app (.pkg) — installs the agent that runs the network extension, enrolls, and scans for AI apps.

The flow is identical on Hexnode, Jamf Pro, Kandji, Mosyle, and Intune-for-Mac because the profile uses only capabilities every macOS MDM supports. Detailed steps below use Hexnode; equivalents for the other MDMs are noted inline.

Capture is OFF by default

Deploying these artifacts enrolls the fleet and lets each device discover its AI apps and report flow metadata. It does not intercept anything. Interception is default-OFF — no TLS is terminated and nothing is captured until an admin explicitly enables a host on Guard. See turning capture on below.

For the Windows equivalent see Windows. For the architecture and what gets governed, see the Overview.

The two artifacts

ArtifactWhat it doesMDM object (Hexnode)
The profileGET /endpoint/mdm-profile (per-tenant, generated live)Trusts the tenant root CA, pre-approves the Waxell system extensions by Team ID, and delivers the managed config (enroll URL, tenant key, capture scope).Policies → macOS → Custom Configuration
The appWaxell-Setup-<ver>.pkg (signed Developer ID Installer + notarized)The agent: runs the network extension, reads the managed config, enrolls against the tenant CA, scans for AI apps.Apps → +Add → macOS → Enterprise App

Identifiers baked into every generated profile:

Value
Team ID2RWC9968XY
Appdev.waxell.setup
Transparent proxy extensiondev.waxell.setup.proxymonitor
Content filter extensiondev.waxell.setup.flowmonitor

Step 1 — Download your tenant's profile

Both artifacts are available in-app under Governance → Connect → AI Endpoints → Setup, or via the API.

The profile is generated per-tenant and carries your tenant API key in its managed config, so treat the downloaded file as a secret. Use a tenant API key:

curl -fsSL "https://api.waxell.dev/api/waxell/v1/endpoint/mdm-profile/" \
-H "X-Wax-Key: $WAX_KEY" \
-o waxell-ai-endpoints.mobileconfig
Capture stays off here

The profile ships with capture disabled. The intended path is to leave it off here and enable specific hosts later from the Guard cascade. If you do want to pre-enable hosts at download time, append ?hosts=api.openai.com,api.anthropic.com to the URL.

The profile is emitted unsigned by design — your MDM (or you, with productsign) signs it on upload, which is the normal Hexnode/Jamf flow. Signing it makes it show as Verified instead of Unsigned on the device, but is not required.

Step 2 — Get the app .pkg

Download the prebuilt, signed and notarized .pkg from Governance → Connect → AI Endpoints → Setup.

If you are building it yourself from the repo:

cd installers/macos
./build-pkg.sh --build --version 0.1.0 # builds the app, then a signed+notarized+stapled pkg
# → build/Waxell-Setup-0.1.0.pkg

The .pkg is signed with a Developer ID Installer certificate and notarized + stapled by Apple, so Gatekeeper accepts it silently on every managed Mac.

One-time build prerequisites

Building locally needs a Developer ID Application cert (signs the .app), a separate Developer ID Installer cert (signs the .pkg), and the waxell-notary notary profile (xcrun notarytool store-credentials waxell-notary). build-pkg.sh errors clearly if any is missing. If you downloaded the prebuilt .pkg you can skip all of this.

Step 3 — Upload both to your MDM

Hexnode

  1. Profile: console → Policies → New Policy → macOS → Custom Configuration → upload waxell-ai-endpoints.mobileconfig. Name it "Waxell AI Endpoints".
  2. App: Apps → +Add → macOS → Enterprise App → upload Waxell-Setup-<ver>.pkg. Name it "Waxell Setup".

Other MDMs

The two-artifact pattern is the same everywhere — upload the .mobileconfig as a custom/raw profile and the .pkg as a managed enterprise app:

MDMProfile (the .mobileconfig)App (the .pkg)
Jamf ProComputers → Configuration Profiles → Upload (custom payload)Computers → Packages, then a Policy with an Install package action
KandjiLibrary → Add → Custom ProfileLibrary → Add → Custom App (installer package)
Intune-for-MacDevices → Configuration → Create → macOS → Templates → Custom (upload the .mobileconfig)Apps → macOS → Add → macOS app (PKG)

Step 4 — Target a device group and push

Add both the profile and the app to the same device group / target, then push.

Order does not matter. macOS applies the system-extension allow-list as soon as both the profile and the extension are present, so when the .pkg installs, the extension activates silently — no "Allow" dialog — because the profile pre-authorizes Team 2RWC9968XY. The CA-trust payload lands silently too.

Step 5 — Verify

Within a few minutes each Mac:

  1. Enrolls against your tenant CA (CSR → per-device intermediate).
  2. Scans itself and reports its discovered AI apps.
  3. Activates the system extension with no user prompt (pre-approved by the profile).

Watch it land in Governance → Connect → AI Endpoints:

  • AI Apps — the device's discovered apps (Cursor, Claude Code, ChatGPT desktop, …) populate here.
  • Devices — map each app's process to a governed agent.
  • Captures — empty until you enable a host on Guard; redacted requests appear here afterward.
Verify the extension on a single Mac

On a managed device you can confirm the extensions activated with systemextensionsctl list | grep dev.waxell — both proxymonitor and flowmonitor should show as activated/enabled for Team 2RWC9968XY.

Turning capture on

Capture is off until you say so, per host. There are two ways to enable it:

  • The intended path — Guard. Set it from the Guard cascade (global → app type → user group → device → agent) and let the policy flow to the device. No re-push of the profile needed.
  • At provisioning time. Re-generate the profile with ?hosts=... (see Step 1) and re-push it.

TLS is terminated only for catalog AI hosts you have explicitly enabled — never banking, health, or mail. Secrets and PII are DLP-redacted on-device before anything is uploaded.

Default-OFF safety

No host is ever intercepted by default. Nothing is terminated or captured until an admin enables that specific host on Guard. Deploying the profile and .pkg alone is purely enrollment + discovery.

Removing a hand-installed dev copy first

If you have been testing the agent locally on your own machine, remove the dev copy before enrolling so the MDM-managed install is clean:

systemextensionsctl list | grep dev.waxell
sudo systemextensionsctl uninstall 2RWC9968XY dev.waxell.setup.proxymonitor || true
osascript -e 'tell application "Waxell Setup" to quit' 2>/dev/null || true
sudo rm -rf "/Applications/Waxell Setup.app"

Then enroll the Mac in your MDM (Enroll → macOS, ADE/automated or manual) and let the profile + app push down — now MDM-managed.

What's inside the profile (reference)

The generated .mobileconfig emits three payloads:

PayloadTypeWhat it does
CA trustcom.apple.security.rootTrusts the per-tenant root CA fleet-wide (silent). The device mints its enrollment chain to this root.
System-extension allow-listcom.apple.system-extension-policyAllows Team 2RWC9968XY to load proxymonitor (transparent proxy) + flowmonitor (content filter) with no user prompt.
Managed configdev.waxell.setup.proxymonitorDelivers api_url, api_key, capture_enabled, intercept_hosts, intercept_processes. The app's ManagedConfigBridge reads it and writes the extension's enroll-config + capture-policy.

The device reads the managed config from /Library/Managed Preferences/dev.waxell.setup.proxymonitor.plist. capture_enabled is false whenever intercept_hosts is empty — which is the default — so a freshly pushed profile enrolls and discovers but never intercepts.

Node / Electron apps

The CA is delivered to the keychain, which covers keychain-respecting clients. Some Node/Electron apps (Claude Code, Cursor) also honor NODE_EXTRA_CA_CERTS; delivering that env var to GUI apps is MDM-specific and is a separate, optional follow-up.