MSI installer
PermitUSB ships as a single PermitUSB.msi: per-machine install, signed, no forced reboot, clean uninstall. This page covers invoking msiexec directly, which is right when the install is driven by an external system rather than an interactive admin.
Prerequisites
The installer blocks at launch with a clear message if either is missing. For fleet deploys, put both in place before pushing the MSI.
- Windows 10, Windows 11, or Windows Server 2016 or later, x64. Older Windows lacks the WMI surface the agent uses for USB enumeration
- .NET 10 Desktop Runtime (x64). The service and tray ship framework-dependent, so the runtime must already be present. Pick the Windows Desktop Runtime installer, not the ASP.NET or SDK variants. For Intune, SCCM or GPO rollouts the Microsoft Update Catalog distributes it as a deployable package: chain it ahead of PermitUSB in your sequence
Silent install
From cmd.exe, the format every msiexec doc shows:
msiexec /i PermitUSB.msi /qn TENANT_TOKEN="<token>"From PowerShell, use the stop-parsing token:
msiexec --% /i PermitUSB.msi /qn TENANT_TOKEN="<token>"Properties
| Property | What it does |
|---|---|
| TENANT_TOKEN | Required for fresh installs. The enrollment token from the dashboard. |
| SERVER | Optional agent-channel URL, default https://agent.permitusb.com. It lives on its own DNS-only subdomain so installed-agent traffic bypasses edge buffering and bot-detection challenges, while the dashboard stays proxied. |
| ENDPOINT_GROUP | Optional group name for the new endpoint. Matched case-insensitively and trimmed. Unrecognized names fall back to the default group with a warning logged cloud-side. |
| PURGE_DATA | Optional, only meaningful on uninstall. Set to 1 to also remove %ProgramData%\PermitUSB\. Default uninstall leaves it so a reinstall keeps the same endpoint identity. |
The Enrollment page generates a token and shows the full msiexec command with the token pre-filled, so there is no need to type it by hand.
Logging
msiexec is silent on failure by default. Pass /l*v to write a verbose log somewhere you can find it:
msiexec /i PermitUSB.msi /qn TENANT_TOKEN="<token>" /l*v %TEMP%\PermitUSB-install.log| Exit code | Meaning |
|---|---|
| 1603 | Generic install failure, usually a missing prereq or no admin rights. |
| 1618 | Another install is running. Wait and retry. |
| 1625 | Blocked by group policy. |
| 3010 | Success, reboot requested. The service enforces without it, but the kernel filter only attaches during boot, so the endpoint stays on user-mode enforcement until it restarts. |
For 1603, search the log for "Return value 3" or "LaunchCondition". The line just above is typically the actual cause.
Upgrading the agent
New version, same machines: run the new MSI with no properties.
msiexec /i PermitUSB.msi /qnWindows 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 its next policy poll and the dashboard Agent version column catches up through the version drift header.
What you do not need to pass on upgrade
- TENANT_TOKEN: already enrolled. credentials.bin exists, so the agent skips first-run enrollment entirely
- ENDPOINT_GROUP: agent.json is canonical post-enrollment and the bootstrap registry is not consulted again. Even if you pass it, it is ignored
- SERVER: same story. ApiUrl in agent.json wins
Version-bump rule: the MSI Version must increase in one of its first three components or MajorUpgrade silently no-ops, because Windows Installer ignores the fourth component when comparing. The assembly version feeds the dashboard Agent version column, so bump both together.
Moving an endpoint between groups after install
Two equally good ways, and you do not need to reinstall for either.
- From the dashboard’s Endpoints page. The agent picks the change up on its next policy poll, within PolicyPollIntervalSeconds (default 5 minutes), and rewrites its local agent.json to match. Use "Check in now" to make that happen in seconds instead
- On the endpoint, edit %ProgramData%\PermitUSB\agent.json, set EndpointGroup to the new group’s name and restart the PermitUSB.Agent service. Useful for a site-side admin re-homing a machine without dashboard access
Edge cases
- Migrating an enrolled endpoint to a new API host: agent.json’s ApiUrl is set during first-run enrollment and survives MSI upgrades, so an existing endpoint keeps talking to the old host. Either hand-edit agent.json and restart the service, or reset and reinstall, which picks up the new default automatically
- Downgrade is blocked: MajorUpgrade refuses to install an older version over a newer one. Uninstall first if you need to roll back
- Force a re-enroll: stop the service, delete credentials.bin and agent.json, then reinstall with a fresh token
What gets installed
| Path or object | Contents |
|---|---|
| %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 PermitUSB.Tray | Launches the tray per user |
| HKLM\Software\PermitUSB\Bootstrap | Install-time properties, cleared on uninstall |
Code signing
Production builds are EV code-signed. The service, tray app, bundled libraries and the MSI package each carry a SHA-256 Authenticode signature from JJMK Studios, LLC, so Windows shows a verified publisher and SmartScreen does not warn on install. Local pre-release builds may be self-signed, and SmartScreen warns on those.
Uninstall
msiexec /x PermitUSB.msi /qn
# Also remove %ProgramData%\PermitUSB\
msiexec /x PermitUSB.msi /qn PURGE_DATA=1Uninstall removes the service, tray app, files and registry keys. It leaves the endpoint record in the cloud: delete that from the Endpoints page.
Something missing or wrong here? Tell us. Documentation gaps get filled fast.



