Deploying the agent with a fleet tool (Jamf, Kandji, Mosyle, Intune, …)
The goal: an IT admin adds Metermark to every managed machine in one afternoon, employees never see a prompt, and the dashboard fills in by the next morning. It works like any other agent you already push: a package, a configuration profile, a background schedule.
1. What you deploy
macOS
| Artifact | Purpose | Where it lands |
|---|---|---|
Metermark-<version>.pkg (universal; signed by Hassion Studio, Inc. and notarized) | the agent as a background-only app, the launch agent, a metermark command | /Applications/Metermark.app, /Library/LaunchAgents/ai.metermark.agent.plist, /usr/local/bin/metermark |
| a configuration profile (managed preferences) | which organization the machine belongs to and how the agent behaves | /Library/Managed Preferences/ai.metermark.agent.plist, or the per-user copy under /Library/Managed Preferences/<user>/ |
The launch agent is installed at the system level, so it starts in every user's session on the machine, including accounts created later. It runs as the user, reads that user's session history, and needs no privileges; there is no root daemon. The package registers the app with Launch Services (Login Items shows Metermark from Hassion Studio, Inc.), starts the agent for whoever is logged in, and retires a self-install's per-user launch agent if one is present, so a machine moving from self-install to fleet never runs two copies.
Windows
| Artifact | Purpose | Where it lands |
|---|---|---|
Metermark-<version>.msi (per machine) | metermark.exe on the machine PATH, and an Active Setup entry that creates each user's scheduled task at their next sign-in | %ProgramFiles%\Metermark\metermark.exe; per user, a Task Scheduler entry "Metermark Agent" (every 15 minutes, as the user, no elevation) |
| registry policy | the same keys as the macOS profile | HKLM\SOFTWARE\Policies\Metermark (machine; wins) or HKCU\SOFTWARE\Policies\Metermark (user) |
The MSI is not code-signed yet. Intune and other management tools install it silently without a SmartScreen prompt.
Linux has no package: the shell installer plus one JSON file do the job. See "Linux fleets" below.
Downloads: https://metermark.ai/download/Metermark.pkg and https://metermark.ai/download/Metermark.msi
always point at the latest release, each with a .sha256 beside it; the manifest at
https://metermark.ai/download/manifest.json lists every version's assets with URL, checksum and
size. Verify the checksum before uploading to your fleet tool.
2. The enrollment key
An enrollment key is an organization-scoped credential that lets a machine send data and nothing
else: it can upload sessions and ask for classifications, and it cannot read the dashboard, the API
or the MCP server. Owners and admins create one in Settings → Machines → Enrollment keys, name
it (for example Jamf — engineering), and can revoke it at any time; revocation stops every machine
that used it at its next sync. Keys look like mm_enroll_….
The key is the machine's credential, not a person's: it keeps working after the admin who created it leaves, and the sessions it uploads belong to whoever §4 resolves, never to that admin. Rotate by creating a new key, pushing the updated profile, then revoking the old one; machines pick up the new value on their next run. Settings shows each key's name, who created it, and when it was last used.
Without a fleet tool, a machine can be enrolled by hand: metermark setup --api-key mm_enroll_…
stores the key locally and skips the browser sign-in.
3. The managed configuration
The agent reads the managed configuration first, then environment variables, then the user's own settings; a managed value cannot be overridden by the user. Every key is in the configuration reference; the ones a fleet deployment needs:
<!-- ai.metermark.agent.plist — deploy as a Custom Settings / managed preferences payload -->
<dict>
<key>enrollment_key</key> <string>mm_enroll_…</string>
<key>endpoint</key> <string>https://metermark.ai</string>
<key>user_email</key> <string>$EMAIL</string> <!-- see §4 -->
<key>sync_interval_minutes</key> <integer>15</integer>
<key>content_mode</key> <string>samples</string> <!-- samples | metadata -->
<key>update_channel</key> <string>managed</string> <!-- managed | stable -->
<key>collectors</key> <array><string>claude-code-local</string><string>cursor-local</string><string>codex-local</string></array>
</dict>
content_mode: metadataturns prompt sampling off. Sessions are still counted and costed, but not labeled by kind of work. Use it if policy forbids any prompt text leaving the machine, even transiently.update_channel: managedmeans the agent never updates itself; you push new package versions.stablelets it self-update from Metermark's release store after verifying the checksum.sync_interval_minutes: 0stops the machine: the schedule still ticks, the agent exits at once.identity_map(optional) carries the content of anidentity.jsonfile,{ "people": [ { "canonical": "…", "emails": ["…"] } ] }, for people who use several logins.metermark statuson a machine lists every managed value and where it came from, under a Configuration heading, so a rollout can be checked without reading plists.
On Windows the same keys are registry values under HKLM\SOFTWARE\Policies\Metermark (machine
policy, which wins) or HKCU\SOFTWARE\Policies\Metermark, written by Intune's registry policy,
Group Policy, or any tool that can set registry values; the types are in the
configuration reference:
reg add "HKLM\SOFTWARE\Policies\Metermark" /v enrollment_key /t REG_SZ /d "mm_enroll_..." /f
reg add "HKLM\SOFTWARE\Policies\Metermark" /v sync_interval_minutes /t REG_DWORD /d 15 /f
reg add "HKLM\SOFTWARE\Policies\Metermark" /v update_channel /t REG_SZ /d managed /f
reg add "HKLM\SOFTWARE\Policies\Metermark" /v collectors /t REG_MULTI_SZ /d "claude-code-local\0cursor-local\0codex-local" /f
On Linux the same keys are a JSON object in /etc/metermark/config.json.
4. Who the sessions belong to
Every session is attributed to one email, so one human is one row on the People page whatever
tools they use. The agent resolves it in this order, and metermark status says which rule applied:
- The fleet's answer:
user_emailfrom the managed configuration (orMETERMARK_USER_EMAIL). Fleet tools can inject the assigned user at install time (Jamf$EMAIL, Kandji$EMAIL, Mosyle%UserEmail%), so attribution matches your directory. - An explicit mapping:
identity_mapin the profile, or anidentity.jsonfile on the machine: when the tool's login is a listed alias of a person, that person's email is used. - The signed-in Metermark account: what
metermark setuprecorded. This is what self-installs use: Claude Code, Codex and Cursor sessions all land on the account the person signed in with. - The AI tool's own login: Claude Code, Codex and Cursor store the signed-in account locally; the agent reads that email when nothing above applies.
Whenever a rule above the tool login wins, the tool login is remembered as an alias of that person
(metermark status lists them under "also this person"), and sessions imported earlier under the
alias are moved onto the person on the next run and re-synced, so the organization collapses to one
row per human. Sessions from an email that is not a member of the organization are held as
unassigned and listed for an admin to claim, never silently dropped or attributed.
5. Step by step
Jamf Pro
- Upload
Metermark-<version>.pkgto Packages. - Computer Management → Configuration Profiles → New → Application & Custom Settings → Upload:
preference domain
ai.metermark.agent, upload the plist from §3 with your enrollment key. Use$EMAILforuser_email; Jamf fills it from the computer's assigned user. - Policy: install the package on enrollment complete and on a recurring check-in; scope both the policy and the profile to the same smart group (for example Engineering laptops).
- Verify on one machine (§6), then widen the scope.
Kandji
- Library → Add → Custom App → upload the
.pkg, install on enrollment, audit and enforce. - Library → Add → Custom Profile → upload the plist as a managed preferences payload for
ai.metermark.agent; Kandji substitutes$EMAILfrom the assigned user. - Assign both to the Blueprint for the target devices.
Mosyle
- Management → Apps → Custom apps → upload the
.pkg. - Management → Profiles → Custom command / Managed preferences → domain
ai.metermark.agent,%UserEmail%foruser_email. - Assign to the device group.
Microsoft Intune (macOS)
- Apps → macOS → Add → macOS app (PKG) → upload the package; assign as Required.
- Devices → macOS → Configuration profiles → Preference file: domain
ai.metermark.agent, upload the plist. Use a dynamic group for scope. Intune has no email variable for macOS preference files, so either deploy anidentity_map(§4) or let the agent use the tool login.
Microsoft Intune (Windows)
- The package. Apps → Windows → Add → Line-of-business app → upload
Metermark-<version>.msi(check its.sha256). Install behaviour System, command-line arguments/qn. Detection is the MSI product code, which Intune reads from the file. Assign as Required to the device group. A Win32 app wrapped with the Content Prep Tool works the same way, withmsiexec /i Metermark-<version>.msi /qnto install andmsiexec /x {ProductCode} /qnto uninstall. - The configuration. There is no Metermark ADMX, so use a custom OMA-URI profile or a
PowerShell script that writes the values from §3 under
HKLM\SOFTWARE\Policies\Metermark(enrollment_key,update_channel=managed, optionallysync_interval_minutes,content_mode,collectors). Assign it to the same group. Intune has no per-user email variable for a device-scope registry write, so either deploy anidentity_map(§4) or let the agent use the tool login. - What happens on the machine. The MSI puts
metermark.exein%ProgramFiles%\Metermarkand on the machinePATH, and registers an Active Setup entry. At each user's next sign-in, Active Setup runsmetermark scheduleonce for that user, creating the per-user "Metermark Agent" scheduled task (every 15 minutes, as the user, no elevation, no window). Each run reads the enrollment key from the registry, imports that user's Claude Code, Codex and Cursor history, classifies what is new, and syncs. Upgrading the MSI re-runs the step and refreshes the task. - Verify on one machine (§6):
metermark statusin any terminal shows "machine (enrollment key …)", the managed values under Configuration with the registry key they came from, and the schedule asTask Scheduler \Metermark Agent.
Uninstalling the MSI removes the program and the Active Setup entry but not each user's scheduled
task, which then fails quietly; push schtasks /Delete /TN "Metermark Agent" /F as a user-context
script to tidy it.
Linux fleets
The shell installer and two JSON files do the job, and any configuration-management tool can drive them.
Managed configuration is a JSON file with the keys from §3, read at the start of every run:
/etc/metermark/config.json(machine policy; root-owned, it wins) and, when a per-user payload is needed,$XDG_CONFIG_HOME/metermark/config.json(default~/.config/metermark/config.json). Neither can be overridden by the user;metermark statuslists the values and the file each came from.{ "enrollment_key": "mm_enroll_…", "user_email": "drew@corp.example", "sync_interval_minutes": 15, "update_channel": "managed" }Install per user, without a terminal. With the key in the machine file, setup reads it from there and skips the browser.
# as root, once per machine install -d -m 755 /etc/metermark install -m 644 config.json /etc/metermark/config.json # as each user (a login session; on a headless host run `loginctl enable-linger $USER` first) curl -fsSL https://metermark.ai/install | METERMARK_NO_SETUP=1 sh ~/.metermark/bin/metermark setup --plain </dev/nullAnsible, for example:
- copy: { src: metermark.json, dest: /etc/metermark/config.json, mode: "0644" } - shell: curl -fsSL https://metermark.ai/install | METERMARK_NO_SETUP=1 sh become_user: "{{ item }}" - shell: ~/.metermark/bin/metermark setup --plain </dev/null become_user: "{{ item }}"The schedule is a systemd user timer (
metermark-agent.timer, everysync_interval_minutes). It needs a user manager: a desktop or SSH login, orloginctl enable-linger <user>so it starts at boot. Without one,metermark setupinstalls the unit files and says so;metermark schedulefrom a login session finishes the job.Updates:
update_channel: managedin the machine file stops self-update; pin the version you chose withMETERMARK_INSTALL_VERSION=<version>(the installer verifies the checksum either way).Verify:
metermark statuson a machine shows the organization ("machine (enrollment key …)"), the managed values with/etc/metermark/config.jsonbeside them, and the timer's next run.
6. Verifying a rollout
- On a machine:
metermark statusprints the organization ("machine (enrollment key Jamf — engineering)" when enrolled), the attributed email and which rule chose it, the aliases folded into it, every managed value under Configuration, the last sync, and the next scheduled run.metermark coverageshows which tools it found.metermark whoamiis the short form. - In the dashboard: Settings → Machines shows each enrollment key with who created it and when it was last used; People shows one row per person, with the tools that reported for them; Coverage shows which people have a reporting machine and which do not.
- Logs:
~/Library/Logs/Metermark/agent.logon macOS,~/.metermark/logs/agent.logon Linux,%LOCALAPPDATA%\Metermark\logs\agent.logon Windows. Rotated, and never any prompt content.
7. Updates, pauses and removal
- Updates: with
update_channel: managedyou push a new package; on macOS the package stops the running agent and starts the new one. Withstable, the agent replaces itself after verifying the release checksum, and reports its version on the next sync. - Pause a fleet: revoke the enrollment key (machines keep importing locally and stop syncing) or
set
sync_interval_minutesto0in the profile (machines stop entirely). - Remove, macOS:
sudo /Applications/Metermark.app/Contents/Resources/uninstall.sh(add--keep-datato leave each user's~/.metermarkin place), pushed from the fleet tool as a script. It unloads the launch agent for every logged-in user and deletes the app, the launch agent,/usr/local/bin/metermark, the package receipt and, by default, each user's data and logs. - Remove, Windows: uninstall the MSI (
msiexec /x {ProductCode} /qn) and pushschtasks /Delete /TN "Metermark Agent" /Fplusrmdir /s /q "%LOCALAPPDATA%\Metermark"as a user-context script. - Data already synced stays in the organization until an admin deletes the person or the organization.
8. Network and proxy
Outbound HTTPS to metermark.ai (sync, classify, downloads) and, only for interactive sign-in,
auth.metermark.ai. Fleet deployments with an enrollment key never contact auth.metermark.ai.
HTTPS_PROXY and NO_PROXY are honoured; a private root certificate is trusted when
NODE_EXTRA_CA_CERTS points at its PEM file. No other hosts, and no telemetry to third parties.
9. Frequently asked by IT
- Does it need Full Disk Access? No. It reads the AI tools' own data directories inside the user's home, which need no permission grant. It avoids looking up git remotes inside Documents, Desktop and Downloads so that macOS never prompts.
- Does it read code or prompts? It reads short prompt excerpts to classify a session and
discards them; with
content_mode: metadatait does not read them at all. It never reads source files or diffs. See Security review. - Can employees see what it sends? Yes:
metermark sync --dry-runprints what the next sync would upload,metermark statusshows what the last one did, and the dashboard's privacy receipt shows the same for the organization. - What about contractors' machines we do not manage? They use the self-install path with a sign-in; their sessions land in the same organization under their email.