Skip to content
← Back to blog

Sign Your Own Agent Packages: Your Certificate, Your Trust Root

• Todd Hebebrand
self-hosting security open-source

An RMM agent runs as SYSTEM on every machine it manages. Whoever controls the code-signing certificate on that agent controls what those machines will run. That’s not a Breeze quirk. It’s the shape of the product category, and the industry has already paid the tuition for it: when an RMM vendor’s release pipeline gets compromised, the attacker doesn’t get one network, they get every network downstream of that signature.

Every RMM on the market answers this the same way: trust our signing chain. Hosted or self-hosted, open source or not, the binary that lands on your customer’s domain controller carries the vendor’s certificate, built on the vendor’s infrastructure, on the vendor’s schedule.

As of v0.105.0, Breeze self-hosters get a different answer. Signed agents now come from your certificate, not ours. We publish the inputs and the manifest. You do the signing.

What Shipped

Two things, and they’re both small on purpose.

First, official Breeze releases now publish the exact pre-signing Windows build outputs (the *-unsigned.exe files) alongside the signed assets. These aren’t a parallel build or a special SKU. They’re the same artifacts our own signing step consumes, published before that step touches them.

Second, every release ships a signed release manifest that records, among other things, the sourceCommit the artifacts were built from. The manifest is signed with an Ed25519 key whose public half you pin. That manifest is what makes the rest of this safe.

The Template Repo

The pipeline lives at github.com/LanternOps/breeze-selfhost-signing, marked “Use this template.” You stamp out your own copy, wire in your certificate, and from then on each official Breeze release becomes a release in your repo, signed under your name.

The order of operations is the part worth reading closely, because it’s where supply-chain pipelines usually go wrong.

Verification happens before anything that can spend a secret. The workflow first verifies the official release manifest against an Ed25519 public key committed in your own repo, and binds the release tag to the manifest’s sourceCommit. Only after both checks pass does the pipeline proceed to any step that consumes your signing credentials. If the manifest doesn’t verify, or the tag doesn’t match the commit the artifacts claim to come from, the run dies before your certificate is ever in play. Your signature can’t be tricked onto inputs you didn’t intend to sign.

After verification, the pipeline does four things:

  1. Signs the Windows artifacts. We recommend Azure Trusted Signing: no hardware token to babysit, keys live in an HSM you never touch, and GitHub Actions authenticates via OIDC, so there’s no long-lived signing secret sitting in your repo settings. Classic certificate setups work too if you already have one.
  2. Signs and notarizes the macOS artifacts under your Apple Developer ID.
  3. Mirrors the assets that are never signed (checksums, scripts, Linux packages) so your release is complete on its own.
  4. Publishes your own release with your own signed manifest. Your fleet’s trust chain now terminates at your key, not ours.

Pointing Your Instance at Your Own Releases

Two environment variables repoint a Breeze instance at the new trust root: BINARY_GITHUB_REPOSITORY (your release repo) and RELEASE_ARTIFACT_MANIFEST_PUBLIC_KEYS (your manifest signing key).

If those two disagree, the API refuses to boot. That’s deliberate. The failure mode we refused to ship is the quiet one, where a mismatched trust root silently freezes fleet updates and you find out three versions later that nothing has been patching. A loud boot failure at deploy time is annoying. A fleet that stopped updating without telling anyone is a security incident with a delay timer. Breeze fails closed.

The Honest Tradeoff

Here’s the part another vendor would bury: v0.104.0 was the last public release carrying a Breeze-signed Windows agent. The public MSI is now unsigned. Download it bare from our releases page and SmartScreen will complain. Nothing already installed is affected, and previously signed packages stay validly signed.

That’s not an oversight. It’s the point.

A signature is a claim about who vouches for the code. For a self-hosted fleet, the honest answer to “who vouches for this binary” should be the operator who runs the infrastructure, not a vendor on the other side of the internet. Publishing unsigned inputs plus a verifiable manifest, and letting you apply your own signature, is a stronger trust story than us signing everything and asking you to extend that trust forever. Unsigned inputs with your signature on your infrastructure beats our signature on your infrastructure.

If you’re on hosted Breeze, none of this lands on you: hosted deployment flows are unchanged. If you’re self-hosting and you want installers that pass SmartScreen with your company’s name in the publisher field, the template repo is the path, and it’s about an afternoon of setup.

Editions, So Fleets Can’t Cross Streams

One more piece shipped alongside this, because giving operators their own release channel creates a new failure mode: a fleet accidentally straddling two update tracks.

Agent builds now carry an edition identity, hosted or self-host. Updaters refuse a manifest from the wrong edition. MSIs refuse to install across editions. A machine enrolled against your self-signed channel can’t silently wander back onto ours, and a hosted machine can’t be fed a self-host build. Existing fleets keep updating normally; the fence only stops crossings, not traffic.

Why We Built It This Way

Breeze is open source and free to self-host. That was never in question. But “you can read the code” and “you control what runs” are different promises. Reading the code tells you what we wrote. Controlling the signing chain tells you what your customers’ machines will actually execute, on whose say-so, and on whose schedule.

With this release, a self-hoster owns all three of the things that matter: the certificate, the trust root the API enforces, and the release cadence. You decide when a release is ready for your fleet. You re-sign it, your manifest gates it, your instance verifies it. The software stays open and free. The trust becomes yours.

The step-by-step guide is at docs.breezermm.com/deploy/sign-your-own-packages, and the full v0.105.0 release notes are here.


Breeze RMM is open source. The signing template is at github.com/LanternOps/breeze-selfhost-signing; everything it verifies and signs is published with every release from v0.105.0 on.