Paul Lorenz 929ea54eaf Index links by router id, and serialize the two structures that index. Fixes #4246
BuildRouterLinks runs once per router connect and scanned the whole link table to
find the connecting router's links. The table is proportional to the square of the
router count in a full mesh, so a wave in which every router reconnects walks the
cube of it in link visits. That never shows in steady state; it arrives during a
mass reconnect, when the connect path is already the bottleneck.

Indexing every link under both endpoint router ids fixes the cost, and introduces
the problem that the table and the index are two structures written in sequence.
Three things follow from that, and the destination repair below turned out to
depend on the connect path's ordering as well.

Performance:

- indexes every link under both endpoint router ids, maintained in the add and
  remove paths all link mutations funnel through, so a connect costs its own
  router's link count rather than the whole table
- prunes a full refresh against the reporting router's own links. That report is
  what every reconnect sends, and it walked the whole table to find the router's
  stale links, so the cost the connect path shed was still being paid one message
  later
- indexes link ids rather than link pointers, so a per-router query resolves each
  id against the table and hands back whatever it currently holds. A reconnect
  replaces a link's Router object and a higher iteration replaces the Link object,
  but neither replaces the id, which is exactly why the scan existed
- holds each router's link ids in a LockedSet rather than a sharded map, which
  cost a map and a mutex per shard for an index that is usually empty and is held
  for as long as the router has existed: 2.9KB per router id against 150 bytes
- drops a router's index when the router is deleted, guarded on the store still
  having no router under that id and checked inside the index map's removal
  callback. A router id can come back, since fabric router ids are the enrollment
  certificate's common name, and nothing orders the cleanup against that: store
  event handlers run after bolt has released the writer lock, so one can execute
  after the id was recreated, connected and reported links. Indexing a link takes
  the same shard as the guarded read, so an entry for a live router cannot appear
  without its create having committed first, which the read then sees, and an entry
  arriving later blocks and lands in a freshly created index
- keeps an index when the store cannot be read, since that is not evidence the
  router is gone, and dropping one otherwise could discard a link another goroutine
  had already fetched the index to record

Keeping the two structures agreeing:

- serializes Add and Remove on the link's id. Without it a removal running between
  an add's two writes takes the table entry, finds nothing yet in the index, and
  lets the add index a link the table no longer holds, which nothing cleans up.
  Only RouterReportedLink held that lock before; the four other removal paths held
  nothing
- serializes the connect-time pairing on that id as well, which was the last writer
  left outside it. A removal reading no destination could complete while the
  pairing was still deciding, leaving the link in the destination's link set after
  the table had dropped it, where path computation still routed over it
- unindexes an id only when the table no longer holds a link under it, so a
  replacement that has taken the table slot is not unindexed by the older link's
  removal. A stale id is the safe direction to err in, since a query filters it,
  where an id wrongly dropped leaves a live link no per-router query finds
- has per-router queries resolve ids against the table, so an id it no longer
  holds is not handed out as a live link

Pairing a link with its destination:

- re-resolves the destination once the link is in the table, and on every later
  report, so a link that reached a bad state heals. A report resolves the
  destination before the link is in the table and a router's connect repairs links
  already in it, so a router connecting between those two moments was seen by
  neither, leaving a link with no adjacency that every operator-facing view still
  called healthy
- makes that re-resolve unconditional rather than only for a link holding no
  destination. The resolve happens under the source router's connect stripe, not
  the destination's, so the destination can be replaced between the resolve and the
  report landing, and a link left on a connection that is no longer registered
  carries no adjacency just as one holding none does
- registers a connecting router before building its links, which is what makes
  that re-resolve close the window rather than narrow it
- adds Link.PointDestAt so the two repair paths cannot both index the link on the
  destination, since that index is a slice that does not deduplicate

Also here:

- logs the link count on both sides of the reconnect exchange, and why a report was
  discarded. A router announces its links once per reconnect and is not asked
  again, so a discarded report left it with no links for a router that believed it
  had announced them, with nothing recorded to compare. The discard reports the
  state it decided on, read once, since a reconnect landing between the decision
  and the log would otherwise have it name a state that discards nothing
- gives test routers version info, which a connected router always has because the
  accept path refuses a hello without it, and which the router sync path
  dereferences with no nil check

The destination repair is backported from the gossip link-state branch, where the
connect path registers before building links for unrelated reasons. That ordering
is load-bearing here and is now explicit at the call site.
2026-09-10 19:55:32 -04:00
2026-05-26 14:46:01 -04:00
2026-08-24 15:23:01 +00:00
2026-01-28 15:01:35 -05:00
2026-09-01 15:50:42 -04:00
2019-12-19 11:47:34 -05:00
2026-02-24 16:38:13 -05:00
2026-03-24 10:44:38 -04:00
2026-08-27 13:34:27 -04:00
2026-02-25 09:18:57 -05:00
2023-03-01 13:43:48 -05:00

Build Status Go Report Card GoDoc Discourse License GitHub Stars

OpenZiti

OpenZiti is an open-source zero-trust networking platform that makes network services invisible to unauthorized users. Every connection, whether from a user, a service, a device, or a workload, is authenticated with cryptographic identity, authorized by policy, and encrypted end-to-end.

OpenZiti works with both existing applications (using lightweight tunnelers with no code changes required) and new applications (using embedded SDKs for the strongest zero-trust model). This makes it practical for both brownfield environments and greenfield development.

Created and sponsored by NetFoundry. Licensed under Apache 2.0.

OpenZiti Architecture: Controller, Edge Routers, SDKs, and Tunnelers


Table of Contents


Use Cases

OpenZiti enables you to extend zero-trust anywhere for any use case, including non-human workloads and workflows, across multiple networks and third parties. The following are some common use cases.

Replace VPNs

Provide secure access to internal services without VPN clients, split tunneling headaches, or concentrator bottlenecks. Each service is individually authorized. No "once you're in, you can reach everything" problem.

Dark APIs and Services

Make APIs and services invisible to the internet. Zero listening ports means zero attack surface. Authorized clients connect through OpenZiti; everyone else sees nothing.

IoT and Non-Human Identity

Give every device, sensor, and machine a unique cryptographic identity. OpenZiti's identity model works for non-human workloads just as well as human users, providing strong authentication for the machine-to-machine connections that make up the majority of modern network traffic.

Zero Trust Workloads

Secure workload-to-workload communication across clouds and environments. Services authenticate each other with cryptographic identity, not network location. No shared secrets, no IP allowlists, no ambient authority.

Agentic AI

Secure agent-to-service and agent-to-agent communication with cryptographic identity for every AI participant. MCP servers, tool endpoints, and private LLMs stay dark, with no listening ports or public URLs. Agents authenticate with strong, unique identities and reach only the resources that the policy allows, so autonomous workflows get the access they need without ambient authority over everything else.

Multi-Cloud and Hybrid Connectivity

One overlay network across AWS, Azure, GCP, on-prem data centers, and edge locations. No cloud-specific networking tools, no VPN tunnels between environments, no complex peering arrangements.

Self-Hosted Service Access

Access home lab or self-hosted services like Nextcloud, Home Assistant, media servers, and development environments from anywhere. No open router ports, no dynamic DNS, no reliance on third-party tunnel services. You control the entire path.

Kubernetes and Cross-Cluster Services

Connect services across Kubernetes clusters without complex ingress rules, service mesh sidecars, or VPN tunnels between clusters. Works beyond Kubernetes, supporting connecting k8s services to VMs, bare metal, IoT devices, or anything else on the overlay.


Key Capabilities

Capability Description
Dark Services Services have zero listening ports. Invisible to scanners and unauthorized users.
Identity for Everything Cryptographic identity for users, services, devices, and non-human workloads (NHI). Not IP-based.
Identity-Based Operations Manage networks through identities and policies instead of IP addresses and firewall rules. Simplifies operations and eliminates manual network configuration.
End-to-End Encryption Data encrypted from source to destination using libsodium. mTLS for authentication. Zero trust in the network path.
No VPNs or Open Ports Connections route through OpenZiti's overlay. No VPN clients, no inbound firewall rules, no exposed ports.
Smart Routing Mesh fabric with intelligent path selection for performance and reliability.
Flexible Deployment Embed SDKs, use tunnelers, or deploy at the network level. Mix and match per service.
Policy-Driven Access Fine-grained, identity-based policies. Access can be revoked in real time, closing active connections.
Programmable REST APIs Full management API for automation and integration. Web-based admin console included.
Fully Self-Hostable Run the entire platform on your infrastructure. No vendor dependencies. Open source, Apache 2.0.

Three Deployment Models

OpenZiti supports three zero-trust models. Mix them in a single network and migrate between them over time.

Network Access

Deploy an OpenZiti edge router in a trusted network zone. Traffic enters the overlay from authenticated clients and exits into the private network where services run.

  • Code changes: None
  • Agent on service host: None
  • Security model: Identity-based access at the network boundary. Similar to a gateway, but with cryptographic identity and encrypted transport.

Host Access

Run an OpenZiti tunneler on the same host as your service. The tunneler handles identity, authentication, and encryption. The service only needs to accept connections from localhost.

  • Code changes: None
  • Setup: Install tunneler, enroll identity
  • Security model: Trust boundary at the host OS. Service is dark to the network and only reachable through the tunneler.

Application Access (Strongest)

Embed an OpenZiti SDK directly in client and/or server applications. The application itself holds the cryptographic identity and encrypts traffic in-process. No listening ports exist, not even on localhost.

  • Code changes: Yes
  • Security model: Strongest. End-to-end encryption in-process. Fully dark. Identity at the application layer, not the network, not the host.

Where to start: Many teams begin with Host Access (tunnelers) for existing services. It deploys in minutes with no code changes. For new development or high-security workloads, Application Access (SDKs) provides the strongest zero-trust posture.


Getting Started

The following Quick Starts show how to set up a local OpenZiti network for development, testing, and learning. For production deployments, see the product documentation at https://netfoundry.io/docs/openziti/category/deployments/.

Quick Start with Docker

The fastest way to get a local OpenZiti network running:

wget https://get.openziti.io/dock/all-in-one/compose.yml
docker compose up

This starts a controller, edge router, and the Ziti console in a single compose stack. The console is available at https://localhost:1280/zac/. From here you can create identities, define services, and configure access policies.

See the all-in-one Docker quickstart for full details including storage options, environment variables, and CLI usage.

Quick Start with the CLI

Download the latest ziti binary from GitHub Releases, then:

ziti edge quickstart

This brings up a local development network: controller, router, and a default admin identity. Ideal for testing and learning.

To add the Ziti Admin Console (ZAC) to a running controller:

ziti ops console download --location /opt/openziti/console
ziti ops console configure /path/to/controller.yml --all --location /opt/openziti/console
# restart the controller, then open https://<controller-address>/zac/

Or serve ZAC locally without touching the controller config:

ziti run console --version latest
# opens https://127.0.0.1:8443. point it at any controller from the browser

Learn More

Resource Description
Introduction Core concepts and how OpenZiti works
Quickstart Guides Step-by-step setup for local, Docker, and hosted environments
Zero Trust Models Deep dive into the three deployment models
Tunneler Reference Get started with zero code changes

Architecture

OpenZiti's overlay network runs on top of existing infrastructure: any IP network, any cloud, any combination. The core components:

Controller

The controller is the management plane. It handles:

  • Identity management: issues and verifies cryptographic identities (x509 certificates) for every participant in the network
  • Policy enforcement: defines which identities can access which services, through which edge routers
  • Network state: tracks routers, services, and topology; provides a REST API and web-based admin console for management

Edge Routers

Edge routers form the data plane, a mesh fabric that carries encrypted traffic between endpoints.

  • Public routers are reachable from the internet, serving as entry points to the network
  • Private ("dark") routers are deployed inside private networks with only outbound connections

Routers automatically discover each other, form mesh connections, and use smart routing to select the best path based on latency, throughput, and cost.

Endpoints: SDKs and Tunnelers

Endpoints are how applications and users connect to the OpenZiti network:

  • SDKs (Go, C, Python, Node.js, Java, Swift, C#): embed zero trust directly in your application. The app itself holds the identity and handles encryption. No sidecar, no agent, no listening ports.

  • Tunnelers (Linux, Windows, macOS, iOS, Android): lightweight apps that provide OpenZiti connectivity to unmodified software. Traffic is intercepted and routed through the overlay transparently. No code changes required.


Zero Trust, Dark Services, and End-to-End Encryption

Zero Trust and Application Segmentation

Every participant (e.g., user, service, device, workload) in an OpenZiti network carries a unique cryptographic identity backed by x509 certificates. When a connection is attempted, OpenZiti verifies:

  1. The identity is valid and enrolled
  2. A policy exists granting that identity access to the requested service
  3. The connection is through an authorized edge router

If any check fails, the connection is denied. If access is later revoked, active connections are terminated immediately. There is no implicit trust based on network location. Being on the same LAN grants no more access than being across the internet, unless policy explicitly allows it.

This model provides zero trust application segmentation: each service is independently authorized. Gaining access to one service does not grant access to any other.

Dark Services

A "dark" service has no open ports. It doesn't listen on any network interface for incoming connections. Instead, the service (or a tunneler alongside it) makes an outbound connection to an OpenZiti edge router and registers itself. Clients reach it only through the OpenZiti fabric, after authentication and authorization.

What this means in practice:

  • Port scans find nothing: there are no listening ports to discover
  • No attack surface: you can't exploit what you can't reach
  • DDoS resistance: there is no public endpoint to flood
  • Invisible to unauthorized users: only identities with matching policy even know the service exists
  • NAT and firewall friendly: all connections are outbound, so CG-NAT, double-NAT, and restrictive firewalls are not a concern

Edge routers can also be dark. Private routers make only outbound connections, so no inbound firewall rules are needed in your private network.

End-to-End Encryption

With OpenZiti SDKs, traffic is encrypted from the sending application to the receiving application using libsodium for the data path and mTLS for identity authentication. Even if routers or intermediate networks are compromised, traffic cannot be decrypted or tampered with.

With tunnelers, encryption covers the path from tunneler to tunneler (or tunneler to SDK), providing machine-to-machine encryption without application changes.


SDKs

Embed zero-trust networking directly in your applications:

Language Repository Notes
Go sdk-golang Used by the OpenZiti project itself
C ziti-sdk-c Ideal for embedded systems, IoT, and high-performance use cases
Java / Kotlin ziti-sdk-jvm Includes Android support
Swift ziti-sdk-swift iOS and macOS
Node.js ziti-sdk-nodejs
C# / .NET ziti-sdk-csharp
Python ziti-sdk-py

All SDKs are listed under the OpenZiti GitHub organization.


Security

OpenZiti is a security-focused project. Responsible disclosure of vulnerabilities helps us keep the platform and its users safe.

Reporting a vulnerability: If you discover a security issue, please review our Vulnerability Disclosure Policy for full details. Sensitive issues should be reported to security@openziti.org. Non-sensitive issues can be filed as GitHub issues in the appropriate repository. You should receive a response within 7 days.

How we handle vulnerabilities: Our Product Security Incident Response Process describes how reported vulnerabilities are triaged, documented, and resolved — including how CVE releases are coordinated with fixes.

Safe harbor: OpenZiti and NetFoundry will not pursue legal action against anyone who researches and reports vulnerabilities in good faith. We encourage security research and attribute reported findings to their reporters in advisories and release notes.


Community & Support

OpenZiti has an active and growing community:

  • Discourse Forum: Ask questions, share projects, get help from the community and maintainers
  • YouTube: Tutorials, demos, and deep dives
  • Blog: Project updates and technical articles
  • Twitter/X: News and announcements

Contributing

The OpenZiti project welcomes contributions including code, documentation, bug reports, and feedback.

Key Repositories

Repository Description
openziti/ziti Core platform: controller, routers, CLI
sdk-golang Go SDK
ziti-sdk-c C SDK
ziti-sdk-jvm Java / Kotlin / Android SDK
ziti-sdk-swift Swift / iOS SDK
ziti-sdk-nodejs Node.js SDK
ziti-sdk-csharp C# SDK
ziti-sdk-py Python SDK
ziti-tunnel-sdk-c Linux tunneler and core tunneler SDK
ziti-tunnel-apple macOS and iOS edge clients
desktop-edge-win Windows desktop edge client
ziti-doc Documentation site

Building from Source

See the local development tutorial for build instructions.

Developer Documentation


Adopters

OpenZiti is used in production by organizations including DeltaSecure (managed SOC), Resulticks (marketing automation), Chirp Wireless (IoT/telecom), GIGO Dev (cloud dev environments), OSMIT (managed IT/GDPR compliance), and open-source projects like zrok and BlueBubbles.

See the full list: ADOPTERS.md. Using OpenZiti? We'd love to add you — open an issue or submit a PR.


Managed Solution

For zero-trust networking without managing your own infrastructure, NetFoundry provides a fully managed, globally distributed OpenZiti network as a service, with SLAs, enterprise support, and a global fabric of edge routers.


OpenZiti is developed and open-sourced by NetFoundry, Inc.

S
Description
The parent project for OpenZiti. Here you will find the executables for a fully zero-trust, programmable network @OpenZiti
Readme Apache-2.0 Cite this repository 67 MiB
Languages
Go 95.9%
Go Template 1.9%
Shell 1.7%
HTML 0.2%