MSI installer

PermitUSB ships as a single PermitUSB.msi. Per-machine install, signed, no forced reboot, clean uninstall.

Where to get it

Sign in and visit /app/enrollment. Click Download PermitUSB.msi. The download is gated to authenticated users — each request mints a short-lived signed URL, so anonymous traffic can't pull the binary directly.

Silent install parameters

  • TENANT_TOKEN — required. The enrollment token from the dashboard.
  • SERVER — optional. The agent-channel URL. Defaults to https://agent.permitusb.com. Lives on its own DNS-only subdomain (named agent. rather than api. because it's for our installed software, not a public REST API) so traffic bypasses Cloudflare's edge buffering and bot-detection challenges; the dashboard at permitusb.com stays Cloudflare-proxied.
  • ENDPOINT_GROUP — optional. Name of the endpoint group the new endpoint should join. Matched case-insensitively and trimmed of surrounding whitespace, so "Information Technology" and "information technology" resolve to the same group. Unrecognized names fall back to the tenant default group with a warning logged on the cloud side.

Manual silent install

cmd.exe (the format every msiexec doc on the internet shows):

msiexec /i PermitUSB.msi /qn TENANT_TOKEN="<token>"

PowerShell — use the --% stop-parsing token:

msiexec --% /i PermitUSB.msi /qn TENANT_TOKEN="<token>"
PowerShell quoting wart. PowerShell strips outer double quotes from arguments before invoking external programs, then re-tokenizes on whitespace. That breaks any property whose value contains a space — e.g. ENDPOINT_GROUP="Information Technology" arrives at msiexec as two arguments and you get the usage dialog instead of an install. Workarounds, in order of preference:
  • --% stop-parsing token (everything after is passed verbatim) — works on PS 5.1 and 7+.
  • $PSNativeCommandArgumentPassing = 'Standard' in your $PROFILE — PS 7.3+ only, fixes the wart globally.
  • Run from cmd.exe (or shell out via cmd /c msiexec …) — the original quoting works as documented.

Quote values that contain spaces — e.g. ENDPOINT_GROUP="Information Technology".

Moving an endpoint between groups after install

Two equally good ways:

  • From the dashboard's Endpoints page. The agent picks up the change on its next policy poll (within PolicyPollIntervalSeconds, default 5 min) and updates its local agent.json to match.
  • On the endpoint, edit %ProgramData%\PermitUSB\agent.json and set EndpointGroup to the new group's name, then restart the PermitUSB.Agent service. Useful for site-side admins re-homing a machine without dashboard access. Local edits win on service restart; mid-session moves come from the dashboard.

The dashboard's Enrollment page generates a token and shows the full msiexec command with the token pre-filled — no need to type it by hand. The Quickstart walks through it.

Upgrading the agent

New version, same machines: just run the new PermitUSB.msi with no properties.

msiexec /i PermitUSB.msi /qn

Windows Installer matches the UpgradeCode across builds and runs a MajorUpgrade: stop service → uninstall old binaries → install new binaries → restart service. The agent picks up the new build on the next policy poll (~5 min by default) and the dashboard's "Agent version" column catches up via the X-Agent-Version drift header.

What survives the upgrade. Everything in %ProgramData%\PermitUSB\agent.json, credentials.bin, events.db, policy.bin. Those are written by the agent at runtime, not by the MSI, so the installer's RemoveFiles never touches them. The endpoint stays enrolled, keeps its identity, keeps its group assignment, keeps its event queue.

What you don't need to pass on upgrade.

  • TENANT_TOKEN — already enrolled. credentials.bin exists, the agent skips first-run enrollment entirely.
  • ENDPOINT_GROUPagent.json is canonical post-enrollment. The bootstrap registry isn't consulted again. Even if you pass it, it's ignored. To move groups post-install: dashboard, or edit agent.json and restart the service.
  • SERVER — same story; ApiUrl in agent.json wins.

Version-bump rule. The MSI's Version in Product.wxs must increase in one of its first three components or MajorUpgrade silently no-ops (Windows Installer ignores the fourth component when comparing). The C# binaries' assembly version is set in agent/Directory.Build.props and feeds the dashboard's "Agent version" column — bump both files together.

Edge cases.

  • Migrating an enrolled endpoint to a new API host. agent.json's ApiUrl is set during first-run enrollment and stays put across MSI upgrades — the installer doesn't rewrite the file. So if you change the cloud-side API hostname (e.g. moving from permitusb.com to agent.permitusb.com), an existing endpoint keeps talking to the old host. Two ways to migrate: (a) hand-edit %ProgramData%\PermitUSB\agent.json to the new ApiUrl and restart the service, or (b) run Reset-PermitUSB.ps1 + reinstall the MSI (which picks up the new default SERVER automatically). New installs always go to the installer's current default, no migration needed.
  • Downgrade is blocked. WiX's MajorUpgrade refuses to install an older version over a newer one. Uninstall first if you really need to roll back.
  • Force a re-enroll (tenant move, stuck agent): stop the service, delete credentials.bin and agent.json from %ProgramData%\PermitUSB\, then reinstall with a fresh TENANT_TOKEN.
  • Wipe data on uninstall: msiexec /x PermitUSB.msi /qn PURGE_DATA=1 removes %ProgramData%\PermitUSB\ too. Default uninstall leaves the data behind so a reinstall picks up the same endpoint identity.

What gets installed

  • %ProgramFiles%\PermitUSB\Agent\ — the Worker Service
  • %ProgramFiles%\PermitUSB\Tray\ — the per-user tray app
  • %ProgramData%\PermitUSB\ — config, SQLite event store, encrypted credentials
  • Service: PermitUSB.Agent, starts automatically
  • Run-key: HKLM\Software\Microsoft\Windows\CurrentVersion\Run\PermitUSB.Tray
  • Bootstrap registry: HKLM\Software\PermitUSB\Bootstrap (cleared on uninstall)

Code signing

Production builds are EV code-signed for Defender SmartScreen compatibility. Pre-release builds may be self-signed; SmartScreen will warn on those, and the warning is expected until the EV-signed build replaces them.

Uninstall

msiexec /x PermitUSB.msi /qn

Removes service, tray app, files, registry keys. Leaves the tenant + endpoint records on the cloud — delete those from the dashboard's Endpoints page.

MSI installer — PermitUSB docs