Mesh security Packetman saysPacketman here. The mesh is how every DataStun agent measures the path to every other agent in your tenant. For those measurements to mean anything, the probe an agent receives has to actually be from another agent in your tenant — not from a scanner, not from an attacker, not from someone else's fleet. This page is how we make that true. Per-tenant key, derived pair keys, HMAC on every packet, monotonic nonces, five-second window. Anything that fails the check gets dropped silently — no error message for an attacker to learn from.
How DataStun agents authenticate every probe they send each other on the measurement fabric — in five steps, with diagrams.
Audience: network engineers and security architects who want to understand the mechanism before deploying. If you just want to use the mesh, the /mesh-diagnostics page is the right starting point.
Two things go wrong if you don’t. Spoofed responses skew measurements — if anyone on the internet can answer an agent’s probe, the throughput and latency numbers you see aren’t for the path you think you’re measuring. Unauthenticated punch-hints are a reflection vector — an attacker who can send to the agent’s mesh port can cause it to send a burst at a target of the attacker’s choice. Neither is acceptable in a measurement fabric.
The fix is to bind every probe to a key only the two endpoints share, and to drop anything that doesn’t prove possession of that key. The rest of this page is how that works.
2. The five-step flow
From key issuance through to a verified round-trip:
End-to-end flow. The key never leaves the TLS-encrypted heartbeat channel until it lands on the agent’s disk at mode 0600. The two agents independently derive the same pair key — no key exchange between them — and from then on every probe and every reply carries an HMAC that proves the sender knows that pair key.
Step 1
Key issuance. A 32-byte random key is generated per tenant at signup. Different tenants get different keys, always.
Step 2
Key delivery. ten returns the key in every heartbeat response. TLS in flight; 0600 file at rest on the agent.
Step 3
Pair-key derivation. Each agent pair derives a unique key from the tenant root plus both agent IDs. No key exchange needed.
Step 4
Probe sent. Probe payload travels with an 8-byte nonce, a sender_id, and a 16-byte HMAC-SHA256 trailer.
Step 5
Verify & reply. The receiver runs four checks (next section). Pass → reply same shape. Fail → silent drop.
3. Pair-key derivation — why no key exchange
Two agents in the same tenant need a shared secret to authenticate probes between them — but having them negotiate one over the wire would be both fragile and a new attack surface. So we use a key derivation function: given the same inputs, both sides compute the same answer without ever talking to each other about it.
Same inputs, same output, no exchange. The agent IDs are sorted lexically — min(A,B) | max(A,B) — so A→B and B→A produce the same key. The tenant ID is in the formula, so the same root key in two different tenants — which can’t happen today, but defense in depth — would still produce different pair keys per tenant. Rotation: change the root, every pair key changes on the next derivation.
4. The probe wire format
Every authenticated probe is the same byte layout. The HMAC covers everything except the HMAC itself, so an attacker can’t flip a field after the fact without invalidating the trailer.
Annotated byte layout. The HMAC is computed over a canonical input — not over a copy of the wire bytes — so an attacker who flips the version byte and recomputes everything except the trailer still won’t produce a valid MAC. Every field that could be abused is covered: type so a request can’t be served as a response, version so v1 can’t be downgraded to v0, payload so a punch-hint target can’t be rewritten in flight.
5. Four checks — or silent drop
Every inbound v1 probe runs the same four checks. All four must pass; any failure drops the packet with no response and no log line above debug. Off-path attackers, port scanners, and stale binaries from other tenants all hit the same wall — no error message for them to learn from.
1. Pair key exists
The receiver looks up sender_id in its derived pair-key cache. No key for this sender — same tenant or otherwise — means no shared secret to verify against. Drop.
2. HMAC verifies
HMAC-SHA256 over the canonical input must match the trailer using the pair key. Wrong key, flipped bit, truncated packet — all fail here. Drop.
3. Timestamp within ±5s
The sendTS_us field must be within five seconds of the receiver’s clock. A captured probe replayed an hour later fails this. Drop.
4. Nonce strictly increases
Per-sender high-water mark. A replayed nonce inside the five-second window — the only window where check 3 still passes — fails here. Drop.
Three categories of probe also drop unconditionally, even before the four checks:
v0 packets (older mesh wire format, no authenticator). The fleet finished migrating to v1 on 2026-05-26; v0 receive is rejected from then on.
v1 packets with no pair key for the sender_id. No shared secret derived — either the sender is from another tenant, or it’s an attacker guessing a sender_id.
Punch-hints without a pair key. Acting on an unauthenticated hint is the reflection attack we’re here to close. Refused at the gate, not warn-and-accept.
6. Limits & caveats
Honest accounting of what this design does and doesn’t cover. We’d rather you know.
Key delivery on the LAN hop. The mesh root key rides the heartbeat: TLS from the agent through Cloudflare Tunnel, plaintext on the LAN hop from the tunnel host to ten. Today this is a single trusted network. Before we run the platform across a geographically separated link, that LAN-hop assumption gets removed.
HMAC is truncated to 16 bytes. The full HMAC-SHA256 is 32 bytes; we use the first 16. This is standard practice for compact protocols and well within the safety margin against the threat model here. A determined cryptanalyst attacking a custom protocol would prefer 32; for measurement-probe authentication, 16 is enough.
No automated key rotation yet. Rotation works — change the root in postgres, the next heartbeat pushes the new value, the pair-key cache invalidates on next derivation — but there is no scheduled rotation or operator-facing UI for it today. Coming.
Authentication, not encryption. Probes prove sender identity and integrity; they don’t hide the payload. Mesh probes carry timing and size measurements, not user data, so this is by design — but worth being explicit about.
Compromise of an agent compromises that agent’s pair keys. Anyone who reads the 0600 key file on a host can authenticate as that host to its peers. Endpoint security still matters; the mesh authenticator doesn’t replace it.
Threat model in one sentence
An off-path attacker who can send UDP to an agent’s mesh port should be unable to skew its measurements or weaponize it as a reflector. That’s what the mechanism on this page is for. It is not a defense against a compromised endpoint, and not a substitute for endpoint security.
See also: /mesh-diagnostics for the buyer-facing overview · /diagnostics for the mesh testing fabric and Advanced Packet Diagnostics · /security lane hub.