The private key problem
A WireGuard-compatible client whose Curve25519 private key is generated inside an Encedo HEM and never leaves it. The tunnel asks the module to perform the Diffie–Hellman at every handshake. A host that never holds the key cannot lose it, and an operator who runs the network cannot take it.
Why this keeps happening
Not the protocol. The cryptography is not what fails — the file holding its key is, and it fails at industrial scale. These are the last eighteen months.
Configuration files and the credentials inside them stolen from Fortinet firewalls and VPN gateways in the leak known as FortiBleed. Named organisations in the dump include Chevron, Samsung, AT&T and Mercedes-Benz.
A stolen configuration is a stolen identity only when the identity is inside it.
Configs, addresses and VPN credentials for FortiGate devices published openly. The files were the payload; no one needed to break the tunnel to get in through it.
Here the file carries references. Publishing it hands over the ability to do nothing.
Hardcoded secrets added to public repositories in a single year, up 34% (State of Secrets Sprawl). Attackers harvest them within five minutes; the median fix takes 94 days; 64% of 2022’s leaks still worked in 2026.
At five minutes against ninety-four days, rotation is not a control. Having nothing to rotate is.
None of these were failures of WireGuard, and none would have been prevented by a stronger cipher. They are all the same shape: a secret written to a file, and a file that went somewhere files go.
Two guarantees, not one
These properties fail separately and protect against different people. One decides who can steal an identity. The other decides who can quietly redirect traffic while that identity stays perfectly safe.
Generated inside the module, non-exportable, and never present on the host. A root shell, a hypervisor snapshot, a disk image, a departing administrator with full access — none of them contain it, because it was never there. Each handshake sends a public value in and receives a shared secret back.
A file with no key in it is still a file somebody can edit. Change one AllowedIPs entry and traffic goes somewhere else, silently, with the key untouched. So addresses, routes, endpoints and DNS move into the module as well, under a MAC computed inside it. A failed check refuses the start. There is no degraded mode.
Why this is not already everywhere
Keeping a key inside a module is old and well understood. Using one inside WireGuard is neither, and the reason sits in the handshake itself. The exchanges that involve the other side’s ephemeral public key cannot be prepared in advance, because that value does not exist until its packet arrives. The module has to answer while the handshake is still open, twice, every time. A key store that is merely safe is not enough. It has to be on the path, and it has to be quick.
That rules out most of what already exists. No mainline WireGuard implementation speaks PKCS#11, and the attempts that come close need kernel patches, custom builds, or give up and move to IPsec. A cloud KMS will hold the key, but it is reached over the network the tunnel was supposed to be providing — the dependency points back at itself. A TPM protects a key for the one machine it is soldered into, which is the opposite of an identity you can carry to the next one.
What is left is a module that performs a raw Curve25519 exchange over a link you control, fast enough that the far end never notices. That is the whole of it, and it is why the protocol did not have to change to accommodate any of this.
The product line
Each tier removes something from the machine. Start where your deployment already is and move when the threat model asks for it — the far end never changes, and neither does the protocol.
v2 in practice
The operational consequences of an empty disk turn out to matter as much as the security ones, and they show up on the first day rather than after an incident.
Move the module to another host, run one command, and it is the same tunnel with the same key and the same peers. Nothing is copied between machines, so nothing is in transit to intercept and nothing has to be securely deleted from the old one.
After an incident you rebuild the host and carry on. You do not rotate the identity, re-issue peers or coordinate a change window with the other side, because the identity was never on the machine that was compromised.
The module answers at a fixed address on its own link, so the client starts with no arguments at all. There is no file for Ansible to render, no secret for a vault to inject, and no first-boot step that has to be trusted with key material.
No file to include or exclude, nothing to scrub from an image, no rule to write about which directories a backup agent may touch. A routine snapshot cannot become an identity leak when the identity is not in the filesystem.
Works with what you already run
Your side gains hardware key custody. The far end stays an ordinary WireGuard endpoint holding an ordinary [Peer] entry — same public key, same handshake, same bytes on the wire. Any implementation that follows the standard is a valid other end, and it is not asked to prove it.
So there is no migration. No coordination window with the other side, no vendor to renegotiate with, no fleet-wide change to schedule, no provider to leave. Fit it to one laptop this afternoon and the rest of the network does not notice — which is also how you evaluate it without committing to anything.
Threat model
Verified against a stock kernel implementation on an unmodified distribution: the other end needed no software, no patches and no knowledge that hardware was involved.
| If someone… | v0 · standard | v1 · hardware key | v2 · config-free |
|---|---|---|---|
| reads the configuration file | Identity taken | A reference, useless alone | No file exists |
| restores a backup or snapshot | Identity taken | No key in the image | No key in the image |
| obtains a root shell | Identity taken | Nothing to dump | Nothing to dump |
| edits the routing | Traffic redirected, silently | Traffic redirected, silently | Startup refused |
| takes the hardware itself | — | Authentication required | Authentication required |
The other side of the ledger
A design that puts the key in hardware has to reach that hardware. These are consequences of the guarantee rather than defects awaiting a fix, and they are worth knowing before you buy, not after.
Where the separation earns its keep
Provision the key, hand the machine to whoever runs it, and running the network and holding its identity become different powers — enforced by hardware rather than by a policy document that assumes good behaviour.
Key material stays in an approved store rather than in a file, and every use of the private key is an operation the module performs and can account for. Custody becomes demonstrable instead of asserted.
Equipment in places you do not control, reachable by people you have not vetted. Physical access to the host stops being equivalent to possession of the identity.
Operate a client's infrastructure and prove non-access to their keys at the same time. The proof is structural: there is nothing on the machine to hand over.
Getting there
Provision once. The module generates the identity and stores the configuration. Nothing is written to disk, and the client public key comes back on standard output for whoever runs the other end.
Add one ordinary peer on the server. It is a normal WireGuard entry; the server administrator needs to know nothing else.
Bring it up. The handshake completes against a key that exists only inside the module.
$ wg-hem provision \ --address 10.99.0.7/32 \ --peer 'pubkey=…,endpoint=vpn:51820,allowed-ips=10.99.0.0/24' → aIGeAZjTRcM2Tqcpy9cODxs0DOaWRGUIMPnCNBC7y3k= $ sudo wg-hem up → Interface wg0 is up. → Handshake with "hq" completed.
Open source under the MIT licence at github.com/encedo/encedo-wg-hsm. The hardware is an Encedo HEM.
If this fits something you run
This is 0.9.2 and the people building it read what arrives. Say what you run today and what you would try it against — that is worth more to us, and to you, than a place on a mailing list.
Honest status
Provisioning, configuration verification, tunnel establishment, peer failover, rekey and status have been exercised against real hardware, on Linux and on Windows, where a graphical client and a privileged service install from an MSI. A long-running soak, loss of the module mid-tunnel, token expiry and full-tunnel routing have not. The Windows binaries carry the company’s signature since September 2026, made through Azure Artifact Signing: a tag asks for it, a person approves the one job that can sign, and no key lives in the repository. macOS is under way: the command line and the graphical client are built for Intel and Apple silicon, and testing against a module and Apple signing and notarisation are still in progress. This page will say so until each of these changes.