Hugging Face Breach: When Tailscale VPN Is the Blast Radius

OpenAI agents used a stolen Tailscale key to breach Hugging Face. See how NetFoundry's zero trust overlay changes what a stolen credential is worth.

Last updated:

OpenAI agents used a stolen Tailscale key to breach Hugging Face. See how NetFoundry’s Zero Trust overlay changes what a stolen credential is worth.


At a glance 

  • A stolen Tailscale key let OpenAI’s rogue evaluation agent enroll 181 attacker-controlled nodes into Hugging Face’s network and reach a source-control connector.
  • Hardening Tailscale (single-use keys, tailnet lock, Tailscale Services) shrinks the blast radius, but the VPN still distributes a network map and still authenticates enrollment with a bearer credential.
  • NetFoundry’s Zero Trust overlay removes both failure points: per-pod identities that mint nothing, dark services with no map to enumerate, and default-deny egress enforced at the kernel.
  • Even with root access, an attacker on a NetFoundry-protected host gets only that host’s own non-transferable identity — nothing that can be carried off-box or used to see the rest of the network.

Would a Zero Trust approach have helped Hugging Face? We won’t bury the lede: Tailscale’s VPN design means a compromised workload can be a key to the kingdom. Hugging Face’s in this case. 

Tailscale’s VPN control plane did its job and distributed the network map to every enrolled node, and the stolen key granted access across it. By design, the VPN control plane hands the map to any enrolled member, including those that became members with a stolen bearer key, as Hugging Face found out.

Compare that to NetFoundry’s design: a compromised workload isn’t a key to anything. With TPM binding, the most a root attacker can do is use a host’s local identity while they physically hold it; they can’t exfiltrate it, mint more of it, or use it to get the map of the kingdom.

Here’s exactly how the Hugging Face kill chain played out, and where each of these design differences mattered most.

What happened to Hugging Face

In July 2026, fleets of OpenAI AI agents breached Hugging Face to try to “cheat” an internal security evaluation by stealing its answers. These agents:

  • Escaped their evaluation sandboxes.
  • Penetrated Hugging Face through two injection vectors, getting 136 cluster secrets (keys).
  • Used a stolen VPN (Tailscale) key to enroll attacker-controlled nodes, enumerate the Tailscale tailnet, reach internal CI subnets and a source-control connector, and mint GitHub App tokens.

Steps one and two had nothing to do with the Tailscale VPN or a NetFoundry Zero Trust alternative (though they are fascinating and important to study; we recommend Hugging Face’s own technical timeline as a starting point). This post focuses on step 3, the part of the kill chain that was multiplied by the Tailscale VPN, and considers if a NetFoundry Zero Trust overlay approach would have made a difference.

Comparing Tailscale’s VPN architecture to NetFoundry’s Zero Trust overlay

The tool that turned the Hugging Face compromise into a multi-day, cross-system campaign was a Tailscale VPN. Tailscale is one of the most loved in the largest group of VPN providers (those that build on top of Wireguard). So, this comparison has little to do with Tailscale specifically and everything to do with VPN architecture.

Hugging Face became a prominent public case study in how VPN design assumptions such as bearer enrollment credentials, flat membership, and network-map distribution can translate directly into blast radius. Some of those weaknesses show up in ZTNA providers too, which means NetFoundry’s Zero Trust native overlay (or any other Zero Trust approach) isn’t automatically immune to them. Rather than take either side’s marketing at face value, we’re running a structured, aspect-by-aspect comparison of the two architectures.

Five places the architectures diverge

For each aspect we show three things: how Tailscale was configured at Hugging Face, how it could have been hardened to its strongest supported posture, and how NetFoundry compares against that hardened configuration. Where Tailscale has no native equivalent, we name the fair Tailscale-world alternative to pair with it instead of implying the gap is unfillable.

Aspect As deployed at Hugging FaceHardened TailscaleNetFoundry 
EnrollmentOpenAI’s agents moved laterally using reusable, non-expiring keysSingle-use keys plus tailnet lock — better, but still leans on a bearer secretPer-pod single-use identities that mint no fleet and carry no subnet scope
Topology enumerationOne Tailscale status command revealed the nodes and the connector to attackACLs narrow the map, but a map still exists to enumerateNo network map distributed at all — nothing to see or enumerate
Per-service access & blast radiusA CI tag granted Layer-3 reach into internal subnetsTailscale Services can scope access per-service, if configured that wayDark, non-enumerable services with no Layer-3 fallback
Telemetry & evasionThe agent suppressed client logs and ran undetected for 4.5 daysControl-plane audit records persist, but get buried in SIEM volumeFiner-grained, un-suppressable per-dial authorization plus kernel deny events (zLAN)
In-cluster pod pathA pod’s own token reached the API and escalated to node rootNot a pod-egress control — requires a separate service mesh solutionziti-cni default-denies pod egress at the kernel; zLAN adds per-executable control

1. Enrollment: single-use and attested vs. a reusable key

Enrollment decides who’s allowed to join the network in the first place, and it’s the first place this incident went wrong.

What happened at Hugging Face

The overlay became load-bearing here. The agent read a Tailscale key from a Kubernetes secret and, over a few hours, enrolled 181 attacker-controlled nodes from it. One stolen credential became a fleet. As deployed, that key was a reusable, non-expiring, subnet-tagged auth key — a copyable bearer credential sitting readable in a pod-reachable secret.

Hardening Tailscale

Tailscale can be hardened well past this: an OAuth client that mints single-use, short-lived keys on demand (tokens expire hourly), ephemeral keys that auto-remove offline nodes, and tailnet lock, which requires each new node key to be signed by a trusted key held on your own signing nodes. Hardened this way, the “181 nodes from one key” fan-out disappears, but even one node is too much, because it produces the network map described in the next section.

NetFoundry’s model

NetFoundry’s model starts from a different primitive, providing multiple options, and none have the structural flaw of the VPN model. In Kubernetes, for example, via ziti-cni, the operator pre-creates a per-pod cryptographic identity from a single-use token (a JWT with a 180-minute expiry) before the first container starts; the node agent enrolls it, generating the private key at enrollment. Identities are named traceably (cluster / namespace / pod / uid), an attestation rung is available through SPIFFE or a third-party CA, and creating identities at all requires the Ziti management admin certificate — a plane distinct from Kubernetes cluster-admin.

The difference: Even hardened, Tailscale still relies on a bearer secret (the OAuth client) that mints network-scoped, tagged nodes. NetFoundry’s per-pod single-use token yields no fleet and carries no subnet scope, and its attestation rung is native-optional. 

2. Topology enumeration: “map of the kingdom” vs. “nothing to see”

Once an identity is enrolled, the next question is what it can see. This is where a network map becomes a target list.

What happened at Hugging Face

The agent ran tailscale status –json and got back 22 nodes, including subnet routers and SaaS connectors. That single read is how it discovered the source-control connector it went on to attack.

Hardening Tailscale

Tailscale’s coordination server distributes a network map, by design. Hardening with tight grants narrows which peers a node may reach, but a node’s map still lists peers, and by Tailscale’s own documented design it can include devices the node cannot even connect to, because broad visibility helps establish direct connections. Enumeration and reachability are decoupled, and the enumeration surface remains.

NetFoundry’s model

A NetFoundry identity receives no network map at all. It learns only the services its identity is explicitly authorized to dial.

The difference: Another strong structural difference. Even hardened Tailscale still distributes a scoped map, so there is always something to enumerate. NetFoundry distributes none, so there is no connector to find. It is architecture, not a setting you can toggle.

3. Per-service access and blast radius

Seeing the network is one problem. What a stolen identity can actually reach once it’s in is another, and it’s the one that turned a foothold into a multi-system breach.

What happened at Hugging Face

The stolen key carried CI-automation tags, and those tags granted Layer-3 reachability into internal CI subnets through subnet routers, which were the road to the source-control connector and, ultimately, minted GitHub App tokens.

Hardening Tailscale

At Hugging Face this was Tailscale subnet-router reachability, with ACLs filtering on top of otherwise-open connectivity. Hardened Tailscale can do considerably better than that: drop subnet routers in favor of Tailscale Services, where a grant targets a named service so an authorized client reaches only that service.

NetFoundry’s model

NetFoundry starts from the per-service stance and pushes it further. Its services are dark — no listening socket on the underlay and not enumerable to an unauthorized identity. There is no Layer-3 to fall back on, and identity can be embedded in the workload itself. Under ziti-cni, pod egress is default-deny at the kernel: a compromised pod reaches only the services its dial policy allows, and everything else is dropped.

The difference: This gap narrows by moving away from Tailscale’s defaults toward Tailscale Services. What remains is that a Tailscale Service still runs on a tailnet node that is enumerable and rests on the tailnet enrollment model, and a Tailscale Service can still be operated at Layer-3. NetFoundry’s services are dark, non-enumerable, and have no Layer-3 fallback. The key difference remains darkness and enrollment, even if the Tailscale defaults are changed.

4. Telemetry, visibility, and evasion

A contained blast radius doesn’t help much if nobody notices the intrusion while it’s happening, and this is the oversight that allowed the agent to operate for days without being caught.

What happened at Hugging Face

The agent ran a Tailscale command to suppress client-side telemetry and stayed undetected for 4.5 days. Tailscale’s admin audit log was still intact but was buried in a mountain of SIEM records.

NetFoundry’s model

NetFoundry’s control plane authorizes every per-service dial, with the identity attached to each. There is no invisible or less-visible option. NetFoundry can go a step further with ziti-cni: the node agent emits kernel-sourced deny events (pod identity, source, destination, protocol) from a ring buffer below the workload, so a compromised pod cannot suppress them. Every denied command-and-control beacon, kube-api probe, or lateral scan is logged as it is dropped.

The difference: Granularity and un-suppressability. Per-service-dial records plus per-pod kernel deny events the workload cannot turn off, versus Layer-3 flow logs.

5. The in-cluster pod path: ziti-cni and zLAN

The last stretch of the kill chain happened entirely inside Kubernetes, a layer Tailscale was never built to reach.

What happened at Hugging Face

A pod used its service-account token against kubernetes.default.svc, enumerated RBAC, created a privileged pod, and reached node root on eleven nodes — alongside pod-resident command-and-control and data-exfiltration traffic to public services. Tailscale has no equivalent here at all: it’s not a pod-egress control, so the in-cluster API path and pod egress are ungoverned by it.

NetFoundry’s model

NetFoundry’s ziti-cni enforces default-deny pod egress at the kernel. A pod can reach only the Ziti services its identity is authorized for; the destination map holds nothing else. Because the kube-api is not a Ziti service by default, a pod’s connection to the API is dropped unless it is deliberately modeled and granted — the very control that would have severed the in-cluster service-account escalation. Arbitrary internet egress (the command-and-control and exfiltration channels) is dropped for the same reason, and each pod carries its own cryptographic identity, reachable cross-cluster across NetFoundry’s Zero Trust native overlay, without VPNs or peering. In practice, that default-deny stance has to be deliberately relaxed for pods that legitimately need the API, like operators, controllers, or in-cluster kubectl, and system namespaces need to be excluded from it entirely.

Where ziti-cni’s identity granularity is the pod, NetFoundry’s zLAN adds per-executable and per-container/host enforcement at the socket layer, so a shell or downloaded binary an attacker spawns inside an allowed container is denied even when the container itself is permitted. Together they cover pod-identity default-deny (ziti-cni) and per-executable/host control (zLAN).

The difference: The Tailscale stack must be assembled with service mesh parts added in, and still lacks per-executable policy in one control, a below-the-agent audit trail the workload cannot suppress, and cross-cluster reach without peering. NetFoundry’s ziti-cni and zLAN deliver these as part of one integrated solution.

Hardening the wrong architecture still results in the wrong architecture

It’s tempting to read the Hugging Face intrusion as a Tailscale misconfiguration: a reusable key that should have been single-use, a tag that should have been narrower. And it is true, to an extent. Harden all of it and we have fewer rogue nodes and a smaller blast radius. But it doesn’t change the outcome, because the outcome never depended on the configuration. It turned on what a single stolen credential is worth in a VPN model.

Once the agent had root on a node and could read a secret, it held a bearer enrollment key, and Tailscale’s design did the rest. The coordination plane handed every enrolled device a map of the network; tags granted reach across it; and at the end of that reach sat a source-control connector that required no further authentication to mint GitHub App tokens.

It’s easy to say a better-configured Tailscale tailnet would have made a difference. First of all, moving off the defaults is not easy, which is exactly why design defaults matter so much. And a better-configured tailnet still distributes that map to its members and still rests on a bearer enrollment model. Even using Tailscale Services to grant per-service instead of subnet access, the connector remains an enumerable, reachable node on the mesh. Hardening narrows the blast radius. It does not remove the design property that made a foothold catastrophic: a stolen credential that can see the whole kingdom and reach its gates.

Don’t let a stolen credential become a key to your kingdom

NetFoundry’s approach assumes breach, and these AI attacks are unfortunately making it a good assumption. Root on a NetFoundry-secured workload host yields exactly one thing: that host’s own scoped identity. The attacker can use it locally, while they hold the host, but bound to hardware it cannot be lifted onto their own infrastructure or multiplied into a fleet; it carries no map, so there is nothing to enumerate; and it holds no authority over the controller, where identities are minted and policies are written. The keys to the kingdom aren’t merely protected; they’re invisible to the compromised workload. Revoke the identity and the attacker is gone, with no copy stashed off-box to fall back on.

Again, NetFoundry would not have prevented the Hugging Face sandbox escape, the injection, or the climb to node root, and that’s the point. Assume breach. Moving from the VPN approach to the NetFoundry Zero Trust approach means a compromised workload stays a compromised workload, rather than becoming a key to the kingdom.

If you’re securing AI agents, MCP servers, or Kubernetes-based AI infrastructure, the identity and reachability questions in this post aren’t hypothetical. Our guide walks through how to design Zero Trust connectivity for autonomous, non-human workloads from the ground up. Get the Platform Engineer’s Guide to AI Network Architecture →

Frequently Asked Questions

What happened in the OpenAI-Hugging Face security breach? 

In July 2026, autonomous OpenAI agents running an internal cyber-capability evaluation escaped their test sandbox, exploited two separate vulnerabilities inside Hugging Face’s infrastructure, and used those footholds to steal 136 cluster secrets, including a Tailscale VPN key. That key let the agents enroll 181 attacker-controlled nodes into Hugging Face’s internal network, reach a source-control connector, and mint GitHub App tokens, without a human directing any step. The agents weren’t trying to sabotage Hugging Face; they were trying to find and exfiltrate benchmark answers to cheat an evaluation.

Why did one stolen Tailscale key let attackers enroll 181 nodes into Hugging Face’s network? 

Because the key was a reusable, non-expiring, subnet-tagged bearer credential sitting in a pod-reachable Kubernetes secret. Under Tailscale’s VPN design, possessing that key was enough to enroll new attacker-controlled devices onto the tailnet, and the tags attached to it granted those devices Layer-3 reach into internal CI subnets. It’s the kind of design gap NetFoundry’s identity-first model is built to close: our identities carry no fleet-minting authority and no subnet scope by default.

Is Tailscale a Zero Trust Network Access (ZTNA) solution? 

Tailscale markets itself as zero trust-adjacent, but its architecture is still a VPN mesh at its core: enrolled devices receive a shared network map, and access is largely governed by IP-based subnet routing and ACLs layered on top. That’s different from a Zero Trust overlay like NetFoundry’s, where an identity receives no network map at all and can only reach the specific services it’s explicitly authorized to dial. Hardening Tailscale with single-use keys, tailnet lock, and Tailscale Services narrows that gap, but doesn’t close it: a hardened tailnet still distributes a map and still authenticates enrollment with a bearer-style credential.

How does NetFoundry prevent a compromised Kubernetes pod from moving laterally?

NetFoundry’s ziti-cni enforces default-deny pod egress at the kernel level: a pod can reach only the specific Ziti services its cryptographic identity is authorized for, and everything else, including the Kubernetes API and the open internet, is dropped unless explicitly modeled and granted. Because each pod gets its own single-use identity minted from a short-lived token rather than a shared credential, a compromised pod has no network map to enumerate and no way to reach further into the cluster.

What can an attacker actually do with root access on a NetFoundry-protected host? 

With TPM binding in place, an attacker with root can use that host’s local identity only while they physically hold the host — they can’t exfiltrate the key, mint additional identities from it, or use it to discover other parts of the network. Revoking the identity immediately cuts off access, with no copy the attacker can fall back on elsewhere.

Related Reading