Security

Muzzle's checks run on your Mac. This page lists everything that leaves it, what our servers keep, and how rules, licences and updates are signed.

Last updated 29 October 2026

Checks run locallyHooks and a proxy on 127.0.0.1. Your protected values, secrets, files and code are never sent to us.
Signed and notarizedDeveloper ID app, notarized by Apple. Updates carry a signature the app verifies before installing.
Signed rules and licencesThreat Feed rules and licence answers are Ed25519-signed and checked against keys built into the app.
Database in Western EuropeOur API database runs on Cloudflare D1 in its Western Europe region.

On your Mac

The local certificate authority

To read HTTPS requests, the proxy uses a certificate authority created on your Mac at setup. Each Mac has its own.

NameMuzzle Local CA
KeyECDSA P-256, generated on the Mac; the private key file is readable only by your user (0600, in a 0700 folder) and never leaves the Mac
ScopeServer authentication only; cannot sign other CAs (path length 0); valid 10 years
TrustAdded to your login keychain, not the System keychain
Site certificatesMade in memory per host, valid one year, never written to disk
RemovalUninstalling from the app removes the certificate and its trust from the login keychain

The proxy decrypts nothing without Pro. With Pro, it still passes through without decrypting the agents' own model APIs, sites you mark as trusted, and, for an hour, any host whose client rejects the certificate.

Package checks

When an agent installs a package, the Mac looks up the package name directly with OSV (api.osv.dev) for known malware and with its registry (npm, PyPI or crates.io) for its age. The request carries the package name and a Muzzle/<version> user agent; nothing reaches our servers. You can turn package checks off in Settings.

What reaches our servers

Without Pro or the Threat Feed, the app sends only the first two.

RequestWhenWhat it carriesWhat we keep
Update check
usemuzzle.com/updates
At launch and every 6 hoursApp versionA count by version and country
Threat Feed rules
GET /v1/feed/rules
Once a dayNo identifierNothing
Licence check
/v1/license/activate
On activation, then about dailyLicence key, device id, computer name (paid keys)Salted hash of the device id, computer name, last seen
Joining the Threat Feed
/v1/email/start, /v1/community/join
When you turn contributing onEmail address, device id, a random install secretEmail, salted device hash, hash of the install secret
Threat Feed signals
POST /v1/feed
Every 6 hours while contributingThe fields shown belowDaily counts, salted device hash, salted network hash
Contact address
/v1/aliases
When you turn it on (Pro)Forwarding email address, licence keyAddress, status, forward count, hash of its secret

The device id is a SHA-256 hash of the Mac's hardware UUID, computed on the Mac. Signals are posted with the Threat Feed key, not your email.

Threat Feed

Contributing is off until you turn it on. A post contains only signals of these five shapes:

// blocked request
{"t":"block", "rule":"protected:email", "host_domain":"example.com", "context":"body"}
// injection attempt: SHA-256 of the normalised text, never the text
{"t":"injection", "kind":"exfiltration", "snippet_hash":"9f2c…e41a", "host_domain":"example.com"}
// a false alarm you cleared
{"t":"false_positive", "rule":"protected:name", "context":"header:user-agent"}
// a site you trusted
{"t":"trusted", "host_domain":"example.com"}
// a secret format that matched
{"t":"secret_format", "rule":"aws-access-key"}

The rules file is signed with Ed25519. The app checks the signature before saving it and again every time it loads it; an unsigned or altered file is ignored. Public key:

KwLMyW65OqSK5AFtNoJByLspxHex8CC9MF7uQV7gWtI=

Licences

Pro is unlocked by a signed answer from our licence API, not by a local flag. The answer names the key's hash, its status, the device id and an expiry 14 days out, and is signed with Ed25519. The app verifies it against a built-in public key on every check and refreshes it about once a day; if our API can't be reached, the last answer keeps working until it expires. A paid key works on 3 Macs at a time.

hfHNRUQcU3Kq4UZm3HvziXv+wEfJ9nfChKHC+SANN8E=

App signing and updates

Code signingDeveloper ID Application: aabergkvist AB (team 8VRX6BSAXA), hardened runtime
NotarizationThe app and the disk image are notarized by Apple and stapled
UpdatesFetched over HTTPS from usemuzzle.com. Each update archive is signed with our updater key; the app checks the signature against the public key built into it and refuses an update that doesn't match.
DownloadThrough a personal link sent by email. The installer URL it opens is signed and expires after 10 minutes.

To check an installed copy: spctl -a -vv /Applications/Muzzle.app should report source=Notarized Developer ID.

Infrastructure and data location

Website and APICloudflare Workers (usemuzzle.com, api.usemuzzle.com)
DatabaseCloudflare D1, primary in Western Europe, no read replicas
EmailCloudflare Email Routing receives; Resend sends
PaymentsStripe, which handles all card details
AnalyticsUmami, run by us on Hetzner in the EU; no cookies

The app and the website talk to our servers over HTTPS. The full list, with what each provider receives, is on Sub-processors.

Sign-in tokens, confirmation tokens and contact-address secrets are stored only as SHA-256 hashes. Device ids, abuse counters and networks are stored as salted hashes. Licence keys and email addresses are stored as they are, because we email and look them up.

Access

Retention

A cleanup job runs every day at 03:17 UTC.

Threat Feed signals91 days
Abuse counters3 days
Sign-in and confirmation links1 day
Unconfirmed contact addresses7 days
Macs on a key12 months after last seen
Contact addresses12 months after the last forward or status check
LicencesWhile the subscription exists, then 24 months after cancellation
Quote and download requests24 months
Confirmed emails and Threat Feed registrationsUntil you ask us to delete them

Mail forwarded through a contact address or to support is not stored in our database. Details are in the privacy policy.

For your security review

Report a vulnerability

Email support@usemuzzle.com. Scope and safe harbour are in our disclosure policy; machine-readable contact details are in security.txt.