## 1. Project Overview & Quickstart (openziti/ziti) ## File: README.md [](https://github.com/openziti/ziti/actions/workflows/main.yml?query=branch%3Arelease-next) [](https://goreportcard.com/report/github.com/openziti/ziti) [](https://pkg.go.dev/github.com/openziti/ziti) [](https://openziti.discourse.group/) [](https://github.com/openziti/ziti/blob/main/LICENSE) [](https://github.com/openziti/ziti) # 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](https://netfoundry.io). Licensed under [Apache 2.0](LICENSE). --- ## Table of Contents - [Use Cases](#use-cases) - [Key Capabilities](#key-capabilities) - [Three Deployment Models](#three-deployment-models) - [Getting Started](#getting-started) - [Architecture](#architecture) - [Zero Trust, Dark Services, and End-to-End Encryption](#zero-trust-dark-services-and-end-to-end-encryption) - [SDKs](#sdks) - [Community & Support](#community--support) - [Contributing](#contributing) - [Adopters](#adopters) - [Managed Solution](#managed-solution) --- ## 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: ```bash 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](./quickstart/docker/all-in-one) for full details including storage options, environment variables, and CLI usage. ### Quick Start with the CLI Download the latest `ziti` binary from [GitHub Releases](https://github.com/openziti/ziti/releases/latest), then: ```bash 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: ```bash 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:///zac/ ``` Or serve ZAC locally without touching the controller config: ```bash 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](https://netfoundry.io/docs/openziti/learn/introduction/) | Core concepts and how OpenZiti works | | [Quickstart Guides](https://netfoundry.io/docs/openziti/learn/quickstarts/) | Step-by-step setup for local, Docker, and hosted environments | | [Zero Trust Models](https://netfoundry.io/docs/openziti/learn/core-concepts/zero-trust-models/overview/) | Deep dive into the three deployment models | | [Tunneler Reference](https://netfoundry.io/docs/openziti/reference/tunnelers/) | 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](https://github.com/openziti/sdk-golang) | Used by the OpenZiti project itself | | C | [ziti-sdk-c](https://github.com/openziti/ziti-sdk-c) | Ideal for embedded systems, IoT, and high-performance use cases | | Java / Kotlin | [ziti-sdk-jvm](https://github.com/openziti/ziti-sdk-jvm) | Includes Android support | | Swift | [ziti-sdk-swift](https://github.com/openziti/ziti-sdk-swift) | iOS and macOS | | Node.js | [ziti-sdk-nodejs](https://github.com/openziti/ziti-sdk-nodejs) | | | C# / .NET | [ziti-sdk-csharp](https://github.com/openziti/ziti-sdk-csharp) | | | Python | [ziti-sdk-py](https://github.com/openziti/ziti-sdk-py) | | All SDKs are listed under the [OpenZiti GitHub organization](https://github.com/openziti). --- ## 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](https://github.com/openziti/security/blob/main/vulnerability_disclosure_policy.md) 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](https://github.com/openziti/security/blob/main/product_security_incident_response_process.md) 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](https://openziti.discourse.group/)**: Ask questions, share projects, get help from the community and maintainers - **[YouTube](https://www.youtube.com/@OpenZiti)**: Tutorials, demos, and deep dives - **[Blog](https://blog.openziti.io)**: Project updates and technical articles - **[Twitter/X](https://twitter.com/openziti)**: News and announcements --- ## Contributing The OpenZiti project welcomes contributions including code, documentation, bug reports, and feedback. ### Key Repositories | Repository | Description | |---|---| | [openziti/ziti](https://github.com/openziti/ziti) | Core platform: controller, routers, CLI | | [sdk-golang](https://github.com/openziti/sdk-golang) | Go SDK | | [ziti-sdk-c](https://github.com/openziti/ziti-sdk-c) | C SDK | | [ziti-sdk-jvm](https://github.com/openziti/ziti-sdk-jvm) | Java / Kotlin / Android SDK | | [ziti-sdk-swift](https://github.com/openziti/ziti-sdk-swift) | Swift / iOS SDK | | [ziti-sdk-nodejs](https://github.com/openziti/ziti-sdk-nodejs) | Node.js SDK | | [ziti-sdk-csharp](https://github.com/openziti/ziti-sdk-csharp) | C# SDK | | [ziti-sdk-py](https://github.com/openziti/ziti-sdk-py) | Python SDK | | [ziti-tunnel-sdk-c](https://github.com/openziti/ziti-tunnel-sdk-c) | Linux tunneler and core tunneler SDK | | [ziti-tunnel-apple](https://github.com/openziti/ziti-tunnel-apple) | macOS and iOS edge clients | | [desktop-edge-win](https://github.com/openziti/desktop-edge-win) | Windows desktop edge client | | [ziti-doc](https://github.com/openziti/ziti-doc) | Documentation site | ### Building from Source See the [local development tutorial](./doc/002-local-dev.md) for build instructions. ### Developer Documentation - [Developer Overview](./doc/001-overview.md) - [Local Development](./doc/002-local-dev.md) - [Local Deployment](./doc/003-local-deploy.md) - [Controller PKI](./doc/004-controller-pki.md) - [Release Notes](./CHANGELOG.md) --- ## Adopters OpenZiti is used in production by organizations including [DeltaSecure](https://deltasecure.de/) (managed SOC), [Resulticks](https://www.resulticks.com/) (marketing automation), [Chirp Wireless](https://chirpwireless.io/) (IoT/telecom), [GIGO Dev](https://gigo.dev/) (cloud dev environments), [OSMIT](https://osmit.de/) (managed IT/GDPR compliance), and open-source projects like [zrok](https://zrok.io) and [BlueBubbles](https://bluebubbles.app). See the full list: **[ADOPTERS.md](./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](https://netfoundry.io/docs/openziti/#deploy_an_overlay) 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](https://netfoundry.io).* --- ## File: common/pb/edge_ctrl_pb/README.md # Prerequisites 1. Install the protoc binary from: https://github.com/protocolbuffers/protobuf/releases 1. The release should include an `include` folder that needs to be next to the `protoc` binary 2. Install the protoc plugin for Go ```go install google.golang.org/protobuf/cmd/protoc-gen-go@latest``` 3. Ensure ```protoc``` is on your path. 4. Ensure your Go bin directory is on your path # Generate Go Code Two options, run the command manually or use `go generate` ## Go Generate 1. Navigate to the root project directory `edge` 2. run `go generate ./common/pb/edge_ctrl_pb/...` or `go generate \common\pb\edge_ctrl_pb\...` Note: Running a naked `go generate` will trigger all `go:generate` tags in the project, which you most likely do not want ## Manually 1. Navigate to the project root 2. Run: ```protoc -I ./common/pb/edge_ctrl_pb/ ./common/pb/edge_ctrl_pb/edge_ctrl.proto --go_out=./pb/edge_ctrl_pb``` --- ## File: dist/docker-images/cross-build/README.md # Cross-build Container for Building the Linux Executables for this Ziti Project Running this container produces three executables for each Ziti component, one for each platform architecture: amd64, arm, arm64. You may specify the target CPU architecture as a parameter to the `docker run` command. ## Local Development This article supports local development by providing a local containerized method for cross-building Linux executables. Alternatively, you may push to your GitHub repo fork to run [the main GitHub Actions workflow](../../.github/workflows/main.yml) (CI), which will cross-build the binaries for macOS, Windows, and Linux. Please refer to [the main article about local development](../../doc/002-local-dev.md) for help getting up and running without Docker. ## Build the Container Image You only need to build the container image once unless you change the Dockerfile or `./linux-build.sh` (the container's entrypoint). ```bash # build a container image named "ziti-go-builder" docker buildx build \ --tag=ziti-go-builder \ --build-arg uid=$UID \ --build-arg gid=$GID \ --load \ ./docker-images/cross-build/ ``` ## Run the Container to Build Executables for the Desired Architectures Executing the following `docker run` command will: 1. Mount the top-level of this repo on the container's `/mnt` 2. Run `linux-build.sh ${@}` inside the container 3. Deposit built executables in `./release` ```bash # build for all three architectures: amd64 arm arm64 docker run \ --rm \ --name=ziti-go-builder \ --volume=$PWD:/mnt \ ziti-go-builder # build only amd64 docker run \ --rm \ --name=ziti-go-builder \ --volume=$PWD:/mnt \ ziti-go-builder \ amd64 ``` You will find the built artifacts in `./release`. ## Example Output ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ## File: dist/docker-images/ziti-controller/README.md # Run Ziti controller in Docker You can use this container image to run a Ziti Controller in a Docker container. ## Container image The `openziti/ziti-controller` image is thin and is based on the `openziti/ziti-cli` image, which only provides the `ziti` CLI. The `ziti-controller` image adds an entrypoint that provides controller bootstrapping when `ZITI_BOOTSTRAP=true` and uses the same defaults and options as the Linux package. The controller always runs in clustered mode, even for a single-node deployment. ## Docker compose The included `compose.yml` demonstrates how to bootstrap a controller container. ### Example At a minimum, you must set the permanent external address and password in the parent environment or in an `.env` file. ```text # fetch the compose file for the ziti-controller image wget https://get.openziti.io/dist/docker-images/ziti-controller/compose.yml ZITI_PWD="mypass" \ ZITI_CTRL_ADVERTISED_ADDRESS=ctrl.127.21.71.0.sslip.io \ docker compose up ``` After a few seconds, `docker compose ps` will show a "healthy" status for the controller. Then, log in to the controller using the `ziti` CLI. ```text ziti edge login ctrl.127.21.71.0.sslip.io:1280 -u admin -p mypass ``` ### Environment variables These are the most relevant variables for bootstrapping. See `compose.yml` for the full list. | Variable | Required | Default | Description | | --- | --- | --- | --- | | `ZITI_BOOTSTRAP_CLUSTER` | no | `true` | Set to `false` when joining an existing cluster | | `ZITI_CTRL_ADVERTISED_ADDRESS` | yes | — | Permanent external address (DNS name) of this controller | | `ZITI_CTRL_ADVERTISED_PORT` | no | 1280 | TCP port | | `ZITI_CLUSTER_NODE_NAME` | no | `ziti-controller1` | Unique name for this cluster node | | `ZITI_CLUSTER_TRUST_DOMAIN` | no | `ziti` | Trust domain shared by all cluster nodes (SPIFFE ID) | | `ZITI_PWD` | yes | — | Password for the default admin user | ### Joining an existing cluster To add a controller to an existing cluster, set `ZITI_BOOTSTRAP_CLUSTER=false` and provide the first node's root CA via a volume mount, and set `ZITI_CLUSTER_NODE_PKI` to that mountpoint. See the `compose.test.yml` for a working example. ### Certificate renewal Leaf certificates (server and client) are valid for 365 days and are automatically renewed at each container startup when `ZITI_AUTO_RENEW_CERTS=true` (the default). Ensure the controller container is restarted at least once a year — any routine restart (image upgrade, host reboot, config change) is sufficient. --- ## File: dist/docker-images/ziti-router/README.md # Run Ziti router in Docker You can use this container image to run a Ziti Router in a Docker container. ## Container image The `openziti/ziti-router` image is thin and is based on the `openziti/ziti-cli` image, which only provides the `ziti` CLI. This `ziti-router` image adds an entrypoint that provides router bootstrapping when `ZITI_BOOTSTRAP=true` and uses the same defaults and options as the Linux package. ## Docker compose The included `compose.yml` demonstrates how to bootstrap a router and documents the most relevant environment variables that influence bootstrapping. ### Standalone example First, create the router in the controller, saving the enrollment token. ```text # create the router, saving the enrollment token to a file ziti edge create edge-router "router1" \ --jwt-output-file=./router1.jwt # fetch the compose file for the ziti-router image wget https://get.openziti.io/dist/docker-images/ziti-router/compose.yml ZITI_ENROLL_TOKEN="$(<./router1.jwt)" \ ZITI_ROUTER_ADVERTISED_ADDRESS=router1.127.0.0.1.sslip.io \ ZITI_ROUTER_PORT=3022 \ docker compose up ``` ### Environment variables These are the most relevant variables for bootstrapping. See `compose.yml` for the full list. | Variable | Required | Default | Description | | --- | --- | --- | --- | | `ZITI_ROUTER_ADVERTISED_ADDRESS` | yes | — | Permanent external address of this router (DNS name or IP) | | `ZITI_ENROLL_TOKEN` | yes | — | Enrollment token (JWT) from the controller | | `ZITI_ROUTER_PORT` | no | 3022 | TCP port | | `ZITI_CTRL_ADVERTISED_ADDRESS` | no | (from token) | Controller address override | | `ZITI_CTRL_ADVERTISED_PORT` | no | (from token) | Controller port override | | `ZITI_ROUTER_MODE` | no | `host` | Tunneler mode: `none`, `host`, or `tproxy` | ### Sidecar example You can use this image as a sidecar container that provides Ziti DNS and TPROXY interception to another container. This contrived example provides a web server that listens on port 8000 and a client that waits for the webserver to be available. The client container shares a network interface with the router container and waits for the router to be healthy before running. ```text # fetch the compose file for the ziti-router image wget -O ./compose.router.yml https://get.openziti.io/dist/docker-images/ziti-router/compose.yml # fetch the router tproxy compose overrides files wget -O ./compose.tproxy.yml https://get.openziti.io/dist/docker-images/ziti-router/compose.override.yml # fetch the all-in-one quickstart compose file wget -O ./compose.quickstart.yml https://get.openziti.io/dock/all-in-one/compose.yml ``` Patch the Compose project to use the quickstart network and provide a web server to test the hello service. ```text cat <>./compose.tproxy.yml # link the router to the quickstart network so it can reach the Ziti controller networks: - quickstart # add a hello web server to use for a Ziti service target hello: image: openziti/hello-world expose: - 8000 networks: - quickstart # add a web client that waits for a healthy tproxy router tproxy-demo-client: image: busybox network_mode: service:ziti-router depends_on: ziti-router: condition: service_healthy command: wget --output-document=- http://hello.internal/ EOF ``` Your `compose.tproxy.yml` should look like this. ```text services: ziti-router: dns: - 127.0.0.1 - 1.1.1.1 user: root cap_add: - NET_ADMIN networks: - quickstart hello: image: openziti/hello-world expose: - 8000 networks: - quickstart tproxy-demo-client: image: busybox network_mode: service:ziti-router depends_on: ziti-router: condition: service_healthy command: wget --output-document=- http://hello.internal/ ``` Define the Compose project files. ```text export COMPOSE_FILE=compose.router.yml:compose.tproxy.yml:compose.quickstart.yml ``` Run the Ziti controller in the background with the all-in-one quickstart container. ```text docker compose up quickstart-check ``` Start the hello web server listening on 8000. ```text docker compose up hello --detach ``` Log in to the Ziti controller ```text ziti edge login 127.0.0.1:1280 -y -u admin -p admin ``` Create a Ziti service for the hello web server. ```text ziti edge create config "hello-intercept-config" intercept.v1 \ '{"portRanges":[{"high":80,"low":80}],"addresses":["hello.internal"],"protocols":["tcp"]}' ziti edge create config "hello-host-config" host.v1 \ '{"address":"hello","port":8000,"forwardProtocol":true,"allowedProtocols":["tcp"]}' ziti edge create service "hello" \ --configs "hello-intercept-config,hello-host-config" \ --role-attributes 'hello.services' ziti edge create service-policy "hello-dial-policy" Dial \ --semantic AnyOf \ --service-roles '#hello.services' \ --identity-roles '#hello.clients' ziti edge create service-policy "hello-bind-policy" Bind \ --semantic AnyOf \ --service-roles '#hello.services' \ --identity-roles '#hello.servers' ``` Grant the quickstart router permission to bind (provide) the hello service. ```text ziti edge update identity quickstart-router \ --role-attributes=hello.servers ``` Create a second Ziti router to use as a tproxy client. ```text ziti edge create edge-router "tproxy-router" \ --jwt-output-file=./tproxy-router.jwt \ --tunneler-enabled ``` Grant the tproxy client permission to dial (consume) the hello service ```text ziti edge update identity tproxy-router \ --role-attributes=hello.clients ``` Simulate policies to check for authorization problems ```text ziti edge policy-advisor services -q ``` Run the demo client which triggers the run of the tproxy router because it is a dependency. ```text ZITI_ENROLL_TOKEN="$(<./tproxy-router.jwt)" \ ZITI_ROUTER_MODE=tproxy \ ZITI_CTRL_ADVERTISED_ADDRESS=quickstart \ ZITI_CTRL_ADVERTISED_PORT=1280 \ ZITI_ROUTER_PORT=3023 \ ZITI_ROUTER_ADVERTISED_ADDRESS=ziti-router \ docker compose up tproxy-demo-client ``` --- ## File: dist/dist-packages/README.md # nfpm-configs These are `nfpm` configuration files. `nfpm` generates Linux packages, e.g., RPM. These files reference environment variables set in GitHub Actions workflow `.github/workflows/publish-linux-install-packages.yml`. --- ## File: dist/cloudfront/get.openziti.io/README.md # CloudFront Function for get.openziti.io This function provides the routing logic for the GitHub raw reverse proxy for downloading specific versions of OpenZiti files that are checked in to GitHub. [Documented as part of the quickstart release](../../../quickstart/README.md#github-raw-reverse-proxy) --- ## File: doc/ha/dev-setup.md # HA Setup for Development **NOTE: HA is in beta. Bug reports are appreciated** To set up a local three node HA cluster, do the following. ## Create The Necessary PKI Run the `create-pki.sh` script found in the folder. ## Running the Controllers 1. The controller configuration files have relative paths, so make sure you're running things from this directory. 2. Start all three controllers 1. `ziti controller run ctrl1.yml` 1. `ziti controller run ctrl2.yml` 1. `ziti controller run ctrl3.yml` 1. Initialize the first controller using the agent 1. `ziti agent cluster init -i ctrl1 admin admin 'Default Admin'` 1. Add the other two nodes to the cluster 1. `ziti agent cluster add -i ctrl1 tls:localhost:6363` 1. `ziti agent cluster add -i ctrl1 tls:localhost:6464` You should now have a three node cluster running. You can log into each controller individually. 1. `ziti edge login localhost:1280` 2. `ziti edge -i ctrl2 login localhost:1380` 3. `ziti edge -i ctrl3 login localhost:1480` You could then create some model data on any controller: ``` # This will create the client side identity and policies ziti demo setup echo client # This will create the server side identity and policies ziti demo setup echo single-sdk-hosted ``` Any view the results on any controller ``` ziti edge login localhost:1280 ziti edge ls services ziti edge login -i ctrl2 localhost:1380 ziti edge -i ctrl2 ls services ziti edge login -i ctrl3 localhost:1480 ziti edge -i ctrl3 ls services ``` ## Running HA Go SDKs As of Golang SDK v1.2.3, no special changes should be required to work with HA systems. --- ## File: doc/ha/quickstart.md # HA Quickstart You can explore Ziti HA by running three local processes on unique TCP ports. This interactive tutorial is self-contained, so you don't need to do anything else before or after this to have a working, local cluster. 1. Create an empty working directory. All commands are run here. ```bash cd $(mktemp -d) ``` 1. Run the first member in the background to create the cluster. ```bash nohup ziti edge quickstart \ --instance-id="ctrl1" \ --ctrl-port="1281" \ --router-port="3021" \ --home="${PWD}" \ --ctrl-address="127.0.0.1" \ --router-address="127.0.0.1" \ --trust-domain="ha-quickstart" \ &> ctrl1.log & ``` Confirm the first job is running and check the log for startup errors. ```bash tail ctrl1.log; echo; jobs ``` Expected output: ```text . . . ... logs ... . . . [1] + running nohup ziti edge quickstart --instance-id="ctrl1" --ctrl-port="1281" & ``` 1. Run the second member and join the cluster. ```bash nohup ziti edge quickstart join \ --instance-id="ctrl2" \ --ctrl-port="1282" \ --router-port="3022" \ --home="${PWD}" \ --ctrl-address="127.0.0.1" \ --router-address="127.0.0.1" \ --trust-domain="ha-quickstart" \ --cluster-member="tls:127.0.0.1:1281" \ &> ctrl2.log & ``` Confirm the second job is running and check the log for startup errors. ```bash tail ctrl2.log; echo; jobs ``` Expected output: ```text . . . ... logs ... . . . [1] - running nohup ziti edge quickstart --instance-id="ctrl1" --ctrl-port="1281" & [2] + running nohup ziti edge quickstart join --instance-id="ctrl2" --ctrl-port="1282" ``` 1. Run the third member and join the cluster. ```bash nohup ziti edge quickstart join \ --instance-id="ctrl3" \ --ctrl-port="1283" \ --router-port="3023" \ --home="${PWD}" \ --ctrl-address="127.0.0.1" \ --router-address="127.0.0.1" \ --trust-domain="ha-quickstart" \ --cluster-member="tls:127.0.0.1:1281" \ &> ctrl3.log & ``` Confirm the third job is running and check the log for startup errors. ```bash tail ctrl3.log; echo; jobs ``` Expected output: ```text . . . ... logs ... . . . [1] running nohup ziti edge quickstart --instance-id="ctrl1" --ctrl-port="1281" & [2] - running nohup ziti edge quickstart join --instance-id="ctrl2" --ctrl-port="1282" [3] + running nohup ziti edge quickstart join --instance-id="ctrl3" --ctrl-port="1283" ``` 1. Optionally, follow interleaved logs in another window. ```bash tail -F -n +1 *.log ``` 1. List agent applications. ```bash ziti agent list ``` Expected output: ```text ╭────────┬────────────┬────────┬─────────────────────────────┬────────────┬─────────────┬───────────╮ │ PID │ EXECUTABLE │ APP ID │ UNIX SOCKET │ APP TYPE │ APP VERSION │ APP ALIAS │ ├────────┼────────────┼────────┼─────────────────────────────┼────────────┼─────────────┼───────────┤ │ 276912 │ ziti │ ctrl1 │ /tmp/gops-agent.276912.sock │ controller │ v0.0.0 │ │ │ 277714 │ ziti │ ctrl2 │ /tmp/gops-agent.277714.sock │ controller │ v0.0.0 │ │ │ 281490 │ ziti │ ctrl3 │ /tmp/gops-agent.281490.sock │ controller │ v0.0.0 │ │ ╰────────┴────────────┴────────┴─────────────────────────────┴────────────┴─────────────┴───────────╯ ``` 1. Identify the cluster leader. ```bash ziti agent cluster list --app-id ctrl1 ``` Expected output: ```text ╭───────┬────────────────────┬───────┬────────┬─────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ true │ v0.0.0 │ true │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ false │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ false │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────┴───────────╯ ``` 1. Simulate a member availability incident. Identify the job number for the cluster leader. It may not be `%1`. ```bash jobs ``` Job 1 belongs to ctrl1, the current leader. ```text [1] running nohup ziti edge quickstart --instance-id="ctrl1" --ctrl-port="1281" [2] - running nohup ziti edge quickstart join --instance-id="ctrl3" --ctrl-port="1283" [3] + running nohup ziti edge quickstart join --instance-id="ctrl2" --ctrl-port="1282" ``` ```bash kill %1 ``` 1. Inspect the cluster via another member ID. ```bash ziti agent cluster list --app-id ctrl2 ``` Expected output: ```text ╭───────┬────────────────────┬───────┬────────┬─────────────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ false │ │ false │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ true │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ false │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────────────┴───────────╯ ``` 1. Restart any disconnected member. Any member can be restarted with the quickstart command. ```bash nohup ziti edge quickstart \ --instance-id="ctrl1" \ --home="${PWD}" \ &>> ctrl1.log & ``` 1. Inspect the cluster. Once restarted, ctrl1 does not necessarily resume being the leader. ```bash ziti agent cluster list --app-id ctrl2 ``` Expected output: ```text ╭───────┬────────────────────┬───────┬────────┬─────────────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ false │ v0.0.0 │ true │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ false │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ true │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────────────┴───────────╯ ``` 1. Stop all background jobs. BASH ```bash kill $(jobs -p) ``` ZSH ```bash kill ${${(v)jobstates##*:*:}%=*} ``` Expected output: ```text [1] + done nohup ziti edge quickstart --instance-id="ctrl1" --ctrl-port="1281" [2] done nohup ziti edge quickstart join --instance-id="ctrl2" --ctrl-port="1282" [3] + done nohup ziti edge quickstart join --instance-id="ctrl3" --ctrl-port="1283" ``` --- ## File: doc/transwarp_b1/transwarp_b1.md # Transwarp beta_1 `openziti/ziti@v0.19.8` ## Goal: Long-haul, High-performance Data Plane for Ziti The primary goal of the Transwarp project is to create a long-haul, high-performance data plane protocol for the [Ziti fabric][fabric]. Current production deployments of the Ziti fabric currently rely primarily on standard TCP connections for the data plane links between routers. As has been previously discussed, TCP is designed with more generalized objectives, resulting in potentially less-than-optimal performance in a number of important conditions. TCP is designed to reduce its sending rate when confronted with packet loss in a way that creates even, balanced utilization for multiple connections sharing an underlay link. Transwarp is not primarily concerned with this kind of balance. In a typical Ziti deployment, Transwarp should be deployed when a network link can be mostly dedicated to the Ziti data plane. The Transwarp-based underlay is responsible for completely utilizing the available underlay capacity, and the overlay is responsible for balancing session resources at a higher layer. Transwarp is a set of Ziti framework components that integrate the high-performance `westworld3` protocol implemented in the [Dilithium project][dilithium]. See the Dilithium [concepts guide][dilithium-concepts] for more details. ## Configurability, Extensibility, Modern Architecture Transwarp is designed to be configurable and extensible in ways that traditional kernel-space networking stacks are not. This aligns with a number of other "programmable networking" concepts that are present in the Ziti fabric. The Dilithium components support programmatic extensibility allowing their core behavior to be influenced by application-specific extensions and requirements. This is outside of the scope of Transwarp but important to understand. The `westworld3` protocol supports a number of tunable parameters that deeply and directly affect its performance in different deployment scenarios. See the [tuning guide][tuning-guide] for more details on these parameters. See the sections below on `westworld3` _profiles_ for information on implementing those parameters in plain Dilithium (without Ziti) testing environments and also in the Ziti fabric. ## Head's Up Deployment Transwarp _does not_ provide a one-size-fits-all profile that works in any deployment situation as TCP does. Transwarp will need to be intelligently deployed using a reasonable profile selection, so that it provides the appropriate posture to work well in a specific deployment scenario. The default _baseline profile_ has proven to be reasonably performant and effective in long-haul geographically dispersed deployments between cloud regions (in testing environments). It _does not work well_ when deployed on any kind of loopback interface. You can think of the baseline profile as your _starting point_ for developing a profile specific to your deployment. Incorrect deployment of Transwarp should not result in non-functioning communication links, but incorrect profile selection could result in under-performance, possibly below the level of TCP. In the case of loopback links, current `westworld3` profiles will _significantly_ underperform versus TCP. Think of Transwarp and `westworld3` as being similar to a race car, in that operating it successfully and well requires more awareness of the application than the typical fully-automated passenger car. TCP is like the latter... you can expect it to behave well in most situations, but you're not likely to get optimal performance from it in the cases where it matters. There are very limited "guard rails" to keep you from misconfiguring `westworld3` and creating performance issues, rather than solving them. Future work on the Transwarp stack will continue to evolve the "self-tuning" abilities of Transwarp, making it much more suitable for lights-out deployment. But like any powerful tool, when you get it properly dialed in for your deployment, it is possible to achieve throughputs that are _multiple times greater than TCP_. # Isolated Protocol Testing Before we look at Transwarp in the Ziti fabric, let's start with the `westworld3` protocol and the `dilithium` framework. This is the foundation of the Transwarp stack, and it is important to have an understanding of the protocol itself. ## Dilithium Tunnel & Dilithium Loop The Dilithium project includes tooling for apples-to-apples protocol comparisons through an overlay proxy infrastructure, which is designed to operate like a simplified version of the Ziti fabric data plane. The proxy provides an _initiating_ side and a _terminating_ side, with local TCP loops on either side of the protocol under test. Using the `dilithium tunnel` as the basis of your testing environment allows for just the protocol between the terminator and the initiator to be swapped out for other protocols, minimizing variables in the testing. The Dilithium project also includes tooling for generating consistent loading on both sides of a `dilithium tunnel`. The `dilithium loop` facility is able to saturate a `dilithium tunnel` up to the capacity of a link under test, while (optionally) checking the veracity of the data reception. `dilithium loop` supports both uni-directional and bi-directional testing. The `dilithium tunnel` supports any TCP protocol on the initiating or terminating sides. There is no requirement to work exclusively with `dilithium loop`. ## Host Requirements The current implementation of the `westworld3` protocol requires that the host operating system is tuned to support large socket buffer sizes. Development and testing environments include a set of `sysctl` configuration directives: ``` # adjust the socket buffer sizes net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.tcp_mem = 8388608 8388608 16777216 net.ipv4.udp_mem = 8388608 8388608 16777216 ``` > Please Note! Failure to properly configure these kernel parameters will result in SIGNIFICANT under-performance of `westworld3`. > Please Note! `westworld3` is a UDP-based protocol. Any firewall rules that you create for Transwarp-enabled connections will require UDP permissions. ## Profiles The `westworld3` protocol includes support for _profiles_, which allow the behavior of the protocol to be tuned to match a specific deployment requirement. Here is the default profile that ships with `v0.3.3`: ``` *westworld3.Profile { randomize_seq false connection_setup_timeout_ms 5000 connection_inactive_timeout_ms 15000 send_keepalive true close_wait_ms 5000 close_check_ms 500 tx_portal_start_sz 98304 tx_portal_min_sz 16384 tx_portal_max_sz 4194304 tx_portal_increase_thresh 224 tx_portal_increase_scale 1.0 tx_portal_dupack_thresh 64 tx_portal_dupack_capacity_scale 0.9 tx_portal_dupack_success_scale 0.75 tx_portal_retx_thresh 64 tx_portal_retx_capacity_scale 0.75 tx_portal_retx_success_scale 0.825 tx_portal_rx_sz_pressure_scale 2.8911 retx_start_ms 200 retx_scale 1.5 retx_scale_floor 1 retx_add_ms 0 retx_evaluation_ms 2000 retx_evaluation_scale_incr 0.15 retx_evaluation_scale_decr 0.01 retx_batch_ms 2 rtt_probe_ms 50 rtt_probe_avg 8 rx_portal_sz_pacing_thresh 0.5 max_segment_sz 1450 pool_buffer_sz 65536 rx_buffer_sz 16777216 tx_buffer_sz 16777216 tx_portal_tree_len 16384 retx_monitor_tree_len 65536 rx_portal_tree_len 16384 listener_peers_tree_len 1024 reads_queue_len 1024 listener_rx_queue_len 1024 accept_queue_len 1024 } ``` These values represent all of the tunable parameters that are exposed from the `westworld3` protocol. These can be provided to the `dilithium tunnel` invocations by providing the path to a YAML file containing override values for the default profile, like this: ``` profile_version: 1 # Cable Upstream Profile # tx_portal_max_sz: 655360 tx_portal_increase_scale: 0.05 tx_portal_dupack_thresh: 192 ``` ``` $ dilithium tunnel -w cable_upstream.yml ``` See the [tuning guide][tuning-guide] for more details about all of the profile parameters and guidance about how to tune them. > Please Note! You _MUST_ include the `profile_version: 1` declaration in your profile YAML. The out-of-the-box default profile should perform reasonably well for the average long-haul connection. See the sections below on the _Westworld Analyzer_ for details about analyzing the operation of `westworld3`. There is no substitute for iterative tuning with real-world workloads over real-world underlay. ## Metrics Instrument Westworld profiles include support for _instruments_. Instruments allow for development and operational analysis of different parts of the operation of the protocol stack. `westworld3` includes a `metrics` instrument that we'll make extensive use of for performance analysis. We can enable this instrument with a profile definition like this: ``` profile_version: 1 instrument: name: metrics path: logs snapshot_ms: 250 enabled: true ``` The `path` value controls the filesystem path where metrics snapshots will be saved. The `snapshot_ms` value controls how frequently the metrics will be snapshotted. ## Westworld Analyzer The Westworld Analyzer is the primary tool for understanding what is happening with the `westworld3` protocol and the internals that manage its state. In any situation where we're trying to understand why `westworld3` is performing a certain way, we're going to want to grab the output from the metrics instrument as described above. This section describes how to configure and operate the Westworld Analyzer for local analysis. ### Run InfluxDB and Grafana with Docker The `dilithium/bin` folder includes a `run-influxdb.sh` script and a `run-grafana.sh` script. These are configured to launch an InfluxDB and Grafana instance to use with the Westworld Analyzer. Adjust the mount path in the `run-influxdb.sh` script if you want your persistent storage to go to a different folder. Once you have the InfluxDB container running, you'll want a local `influx` client to access the database. With that installed, you'll need to create a `dilithium` database instance to contain metrics from `westworld3`. ``` $ influx > create database dilithium ``` With the Grafana container running, connect to it through a web browser on port `3000`. The default username and password are `admin`/`admin`. With Grafana running, you'll need to add an InfluxDB datasource: You'll want to update the name to `Dilithium`, the URL to `http://127.0.0.1:8086`, and the database to `dilithium`. The "Save & Test" button should return a successful result. Once you've got the datasource configured, you'll need to import the Westworld 3.1 Analyzer dashboard, which is found in the `dilithium/grafana` folder: Make sure you've selected the `Dilithium` datasource for the dashboard as defined in the previous step. ### Run Scenario, Snapshot Metrics With the metrics instrument enabled in your `westworld3` profile, the `dilithium tunnel` command should produce a log line like this: ``` [ 0.001] INFO dilithium/util.(*CtrlListener).run: [logs/westworld3.13389.sock] started ``` The `logs/westworld3.13389.sock` is a unix domain socket that can be used to control the metrics instrumentation with the `dilithium ctrl` commands. Use the `dilithium ctrl client` command to snapshot the metrics: ``` $ dilithium ctrl client logs/westworld3.13389.sock [ 0.049] INFO dilithium/cmd/dilithium/ctrl.client: received 'ok' [ 0.049] INFO main.main: finished ``` The above command will cause the onboard metrics instrument to write all observed snapshots out to a series of files, representing the metrics collected by the instrument: ``` [fedora@ip-10-0-0-217 ~]$ find logs/listener_0.0.0.0-6262_806204675/ logs/listener_0.0.0.0-6262_806204675/ logs/listener_0.0.0.0-6262_806204675/rx_keepalive_bytes.csv logs/listener_0.0.0.0-6262_806204675/tx_portal_sz.csv logs/listener_0.0.0.0-6262_806204675/metrics.id logs/listener_0.0.0.0-6262_806204675/rx_keepalive_msgs.csv logs/listener_0.0.0.0-6262_806204675/rx_ack_bytes.csv logs/listener_0.0.0.0-6262_806204675/tx_keepalive_msgs.csv logs/listener_0.0.0.0-6262_806204675/dup_rx_msgs.csv logs/listener_0.0.0.0-6262_806204675/dup_acks.csv logs/listener_0.0.0.0-6262_806204675/rx_ack_msgs.csv logs/listener_0.0.0.0-6262_806204675/tx_portal_rx_sz.csv logs/listener_0.0.0.0-6262_806204675/rx_msgs.csv logs/listener_0.0.0.0-6262_806204675/retx_scale.csv logs/listener_0.0.0.0-6262_806204675/tx_bytes.csv logs/listener_0.0.0.0-6262_806204675/tx_ack_msgs.csv logs/listener_0.0.0.0-6262_806204675/rx_bytes.csv logs/listener_0.0.0.0-6262_806204675/allocations.csv logs/listener_0.0.0.0-6262_806204675/retx_ms.csv logs/listener_0.0.0.0-6262_806204675/tx_portal_capacity.csv logs/listener_0.0.0.0-6262_806204675/errors.csv logs/listener_0.0.0.0-6262_806204675/dup_rx_bytes.csv logs/listener_0.0.0.0-6262_806204675/rx_portal_sz.csv logs/listener_0.0.0.0-6262_806204675/tx_keepalive_bytes.csv logs/listener_0.0.0.0-6262_806204675/retx_bytes.csv logs/listener_0.0.0.0-6262_806204675/retx_msgs.csv logs/listener_0.0.0.0-6262_806204675/tx_msgs.csv logs/listener_0.0.0.0-6262_806204675/tx_ack_bytes.csv ``` You'll want to collect these files for processing by the Westworld Analyzer. ### Import Metrics The `dilithium influx` commands are used to load the metrics data produced by the metrics instrument into the Westworld Analyzer. With the above metrics data collected, it can be imported into the Westworld Analyzer with the `dilithium influx load` command, like this: ``` $ dilithium influx load /home/michael/.fablab/instances/transwarp/forensics/1616435348256 [ 0.001] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [265] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-60243_412359814] dataset [tx_bytes] [ 0.002] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [265] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-60243_412359814] dataset [tx_msgs] [ 0.002] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [265] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-60243_412359814] dataset [retx_bytes] [ 0.003] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [265] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-60243_412359814] dataset [retx_msgs] [ 0.005] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [265] points for ... [ 0.124] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [266] points for westworld3.1 peer [dialerConn_[--]-60243_54.167.243.24-6262_623195017] dataset [dup_rx_msgs] [ 0.125] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [266] points for westworld3.1 peer [dialerConn_[--]-60243_54.167.243.24-6262_623195017] dataset [allocations] [ 0.125] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [266] points for westworld3.1 peer [dialerConn_[--]-60243_54.167.243.24-6262_623195017] dataset [errors] [ 0.142] INFO main.main: finished ``` `dilithium influx load` is designed to take the root of the filesystem tree where the directory structure created by the metrics instrument is located. It will automatically traverse the tree and load any metrics it finds into the analyzer database. ### Analyze With the dataset loaded into the Westworld Analyzer, it can be explored using the _Westworld 3.1 Analyzer_ dashboard you previously imported. You'll want to use the time range controls to find your run telemetry and zoom in on relevant portions of the dataset: Notice the "peer" dropdown in the upper left corner of the dashboard. This will allow you to select the metrics from the peer components present in the dataset. Typically there are separate metrics from the `dialerConn` and `listenerConn` connections, and also a separate set of metrics from the active `listener` that is persistently available in the `westworld3` application. Every connection has at least 1 `dialerConn` peer and 1 `listenerConn` peer. An application embedding `westworld3` will have only 1 global `listener` peer. ### Clean Metrics The `dilithium influx clean` command will remove all metrics snapshots from your analyzer database. Because differentiating multiple peers and time ranges can get cumbersome, it's often best to run `dilithium influx clean` to start with a clean slate, and then load in an appropriate dataset for investigation. It's easiest to keep the datasets on disk as files, and only load them into the analyzer for analysis. ## Complete Dilithium Example The following illustrates a complete example using `dilithium tunnel` and `fablab` (outside of scope for this document) to execute a test and analyze the performance. There is no requirement to use `fablab`, and these scenarios can be re-created manually or through any other automation stack. ### Scenario In the testing scenario there are two Linux hosts each in a VPC in a different AWS region, separated by significant geography. We're going to compare the performance of `westworld3` and `tcp` over the `dilithium tunnel`. To keep things simple, we're going to run a file transfer over the tunnel using `scp`, but any TCP-based application protocol should work just fine. ### Launch Tunnel Server and Client First, we'll need to launch the `dilithium tunnel server` and the `dilithium tunnel client`: ``` [fedora@ip-10-0-0-217 ~]$ fablab/bin/dilithium tunnel server 0.0.0.0:6262 127.0.0.1:22 -w fablab/cfg/dilithium/westworld3.1/development.yml [ 0.000] INFO dilithium/protocol/westworld3.NewMetricsInstrument: config { path logs snapshot_ms 250 enabled true } [ 0.001] INFO dilithium/cmd/dilithium/tunnel.tunnelServer: created tunnel listener at [0.0.0.0:6262] [ 0.001] INFO dilithium/protocol/westworld3.(*listener).run: started [ 0.001] INFO dilithium/util.(*CtrlListener).run: [logs/westworld3.13877.sock] started [ 0.001] INFO dilithium/protocol/westworld3.(*metricsInstrumentInstance).snapshotter: started ``` Use `dilithium tunnel server --help` for details. `0.0.0.0:6262` is the address that the server will listen on for incoming connections for the protocol under test. `127.0.0.1:22` is the "service address" where the tunneled connection will terminate (an `ssh` endpoint on port `22` in our case). ``` [fedora@ip-10-0-0-222 ~]$ fablab/bin/dilithium tunnel client 54.167.243.24:6262 127.0.0.1:1122 -w fablab/cfg/dilithium/westworld3.1/development.yml [ 0.001] INFO dilithium/cmd/dilithium/tunnel.tunnelClient: created initiator listener at [127.0.0.1:1122] ``` Use `dilithium tunnel client --help` for details. `54.167.243.24:6262` corresponds to the public address of the `server` we created above. `127.0.0.1:1122` is the local listening address where connections to be tunneled will be accepted. So in the above example, we'll point our `scp` client at `127.0.0.1:1122` on the host running the `dilithium tunnel client`, and that connection will be proxied across the `westworld3` connection between the `server` and the `client`, and will be terminated at `127.0.0.1:22` on the host running the server. ### Run Workload The scenario workload is executed from the host running the `dilithium tunnel client`: ``` [fedora@ip-10-0-0-222 ~]$ scp -P 1122 junk 127.0.0.1: junk 100% 511MB 9.1MB/s 00:56 ``` ### Collect Metrics Snapshot `server` metrics: ``` [fedora@ip-10-0-0-217 ~]$ fablab/bin/dilithium ctrl client logs/westworld3.13877.sock [ 0.104] INFO dilithium/cmd/dilithium/ctrl.client: received 'ok' [ 0.104] INFO main.main: finished ``` Snapshot `client` metrics: ``` [fedora@ip-10-0-0-222 ~]$ fablab/bin/dilithium ctrl client logs/westworld3.25891.sock [ 0.017] INFO dilithium/cmd/dilithium/ctrl.client: received 'ok' [ 0.017] INFO main.main: finished ``` Retrieve metrics data from both hosts using whatever mechanism makes the most sense for your environment. In this example, I'm using `fablab` to easily pull down the entire metrics tree from both hosts with a single command: ``` $ fablab exec logs [ 0.000] INFO fablab/zitilib/actions.(*logs).forHost: => [/home/michael/.fablab/instances/transwarp/forensics/1616440013148/logs/local/local] [ 3.452] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287/rx_keepalive_bytes.csv => 5.9 kB [ 3.718] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287/tx_portal_sz.csv => 6.3 kB [ 3.996] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287/metrics.id => 26 B ... [ 14.628] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listener_0.0.0.0-6262_142707432/metrics.id => 26 B [ 14.938] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listener_0.0.0.0-6262_142707432/rx_keepalive_msgs.csv => 7.7 kB [ 15.211] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listener_0.0.0.0-6262_142707432/rx_ack_bytes.csv => 7.7 kB [ 15.507] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/listener_0.0.0.0-6262_142707432/tx_keepalive_msgs.csv => 7.7 kB ... [ 23.288] INFO fablab/zitilib/actions.(*logs).forHost: => [/home/michael/.fablab/instances/transwarp/forensics/1616440013148/logs/remote/remote] [ 51.316] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/dialerConn_[--]-53553_54.167.243.24-6262_389501639/rx_keepalive_bytes.csv => 5.9 kB [ 53.770] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/dialerConn_[--]-53553_54.167.243.24-6262_389501639/tx_portal_sz.csv => 7.3 kB [ 56.217] INFO fablab/kernel/fablib.RetrieveRemoteFiles: logs/dialerConn_[--]-53553_54.167.243.24-6262_389501639/metrics.id => 26 B ... ``` We've pulled down peer data for `listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287`, `listener_0.0.0.0-6262_142707432`, and `dialerConn_[--]-53553_54.167.243.24-6262_389501639`, and those trees of files are located at `/home/michael/.fablab/instances/transwarp/forensics/1616440013148/logs`. If you're working with Transwarp and `westworld3` regularly, you'll want to invest in tooling to easily retrieve metrics snapshot trees for analysis with the Westworld Analyzer. Anytime we need to diagnose what's going on with Transwarp or `westworld3`, getting a metrics dataset will be the first requirement. Next, we'll make sure the analyzer is cleaned of previous analysis data: ``` $ dilithium influx clean [ 0.003] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [dup_acks] [ 0.004] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [errors] [ 0.005] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [retx_scale] ... [ 0.075] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [dup_rx_bytes] [ 0.075] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [rx_portal_sz] [ 0.076] INFO dilithium/cmd/dilithium/influx.influxClean: dropped series [tx_portal_rx_sz] [ 0.076] INFO main.main: finished ``` Then we'll import the new dataset: ``` $ dilithium influx load /home/michael/.fablab/instances/transwarp/forensics/1616440013148/logs [ 0.001] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [352] points for westworld3.1 peer [listener_0.0.0.0-6262_142707432] dataset [tx_bytes] [ 0.002] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [352] points for westworld3.1 peer [listener_0.0.0.0-6262_142707432] dataset [tx_msgs] ... [ 0.091] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [267] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287] dataset [dup_rx_msgs] [ 0.091] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [267] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287] dataset [allocations] [ 0.092] INFO dilithium/cmd/dilithium/influx.loadWestworld31Metrics: wrote [267] points for westworld3.1 peer [listenerConn_0.0.0.0-6262_13.211.35.123-53553_388949287] dataset [errors] [ 0.111] INFO main.main: finished ``` If we load up the Westworld 3.1 Analyzer in Grafana, we should see something like this: By default the dashboard shows the last 5 minutes, so we'll want to zoom out to the last 30 minutes (or whatever is necessary to find your dataset in time): From there, you can zoom in on the dataset in detail: ### Comparing Against TCP We can run the scenario again, swapping in the `tcp` protocol instead of the `westworld3` protocol by invoking the `dilithium tunnel server` and `client` like this: ``` [fedora@ip-10-0-0-217 ~]$ fablab/bin/dilithium tunnel server 0.0.0.0:6262 127.0.0.1:22 -p tcp ``` ``` [fedora@ip-10-0-0-222 ~]$ fablab/bin/dilithium tunnel client 54.167.243.24:6262 127.0.0.1:1122 -p tcp ``` The `-p ` option allows for specifying a different underlay protocol to be used when proxying. # Enabling Transwarp in Ziti Transwarp is the integration of `westworld3` into Ziti's `transport` framework, allowing it to be used with any Ziti channel. Even though you _could_ use it for a Ziti control channel, it is only recommended that it be used for data plane links. ## Transwarp vs TranswarpTLS The `transport` integration means that Ziti now has support for the `transwarp:` and `transwarptls:` transports. These `transwarp:` is analogous to `tcp:` and `transwarptls:` is conceptually similar to `tls:`. `transwarptls:` wraps the `westworld3` protocol in a TLS wrapper, providing the same privacy and authentication mechanisms as `tls:`. ## Link Listener To enable Transwarp in the Ziti fabric data plane, simply adjust any link listeners (in target router configurations) to use the `transwarp:` or `transwarptls:` protocols: ``` link: listeners: - binding: transport bind: transwarptls:127.0.0.1:6002 advertise: transwarptls:127.0.0.1:6002 ``` ## Profile Specification The underlying `westworld3` protocol profile can be specified in the Ziti router configuration like this: ``` transport: westworld3: profile_version: 1 tx_portal_min_sz: 16384 tx_portal_max_sz: 1073741824 instrument: name: metrics path: /tmp/westworld3 snapshot_ms: 250 enabled: true ``` This allows for configuring the `westworld3` profile values as described above in the `dilithium`-only example. Without specifying a profile the default _baseline profile_ will be used. ## Debugging Transwarp in Ziti The metrics instrument (and the other `dilithium` instruments) can be used with the downstream Westworld 3.1 Analyzer in the same way as the `dilithium`-only example. Enable the metrics instrument in the router's profile, and then use the `dilithium ctrl client` tool to snapshot the instrument, producing the dataset for the analyzer. # A Complete Ziti Example This example will be configured similarly to the pure Dilithium example above. There are two routers, each in an AWS VPC in different regions separated by geography. One of the routers provides a link listener, configured to use `transwarptls:`. Here's the relevant logging output from the `ziti router` process: ``` [ 0.002] INFO foundation/transport/transwarptls.Listen: westworld3 profile = [ *westworld3.Profile { randomize_seq false connection_setup_timeout_ms 5000 connection_inactive_timeout_ms 15000 send_keepalive true close_wait_ms 5000 close_check_ms 500 tx_portal_start_sz 98304 tx_portal_min_sz 16384 tx_portal_max_sz 4194304 tx_portal_increase_thresh 224 tx_portal_increase_scale 1.0 tx_portal_dupack_thresh 64 tx_portal_dupack_capacity_scale 0.9 tx_portal_dupack_success_scale 0.75 tx_portal_retx_thresh 64 tx_portal_retx_capacity_scale 0.75 tx_portal_retx_success_scale 0.825 tx_portal_rx_sz_pressure_scale 2.8911 retx_start_ms 200 retx_scale 1.5 retx_scale_floor 1.0 retx_add_ms 0 retx_evaluation_ms 2000 retx_evaluation_scale_incr 0.15 retx_evaluation_scale_decr 0.01 retx_batch_ms 2 rtt_probe_ms 50 rtt_probe_avg 8 rx_portal_sz_pacing_thresh 0.5 max_segment_sz 1450 pool_buffer_sz 65536 rx_buffer_sz 16777216 tx_buffer_sz 16777216 tx_portal_tree_len 16384 retx_monitor_tree_len 65536 rx_portal_tree_len 16384 listener_peers_tree_len 1024 reads_queue_len 1024 listener_rx_queue_len 1024 accept_queue_len 1024 } ] [ 0.002] INFO fabric/router.(*Router).startXlinkListeners: started Xlink listener with binding [transport] advertising [transwarptls:54.167.243.24:6000] ``` Here's the relevant log output when starting the second `ziti router`, configured without a link listener. This output represents it dialing the `transwarptls:` link connection to the first router: ``` /* Detailed source-code truncated for AI context efficiency. */ ``` Ziti links currently require 2 underlay connections, with one of them dedicated to flow control processing. This is why there are 2 `westworld3` connections established above. > There is currently an open issue in the `openziti/fabric` project to allow the ACK channel to operate on a protocol other than the data channel. Running the ACK channel over TCP and the data channel over `westworld3` will make analyzing the metrics simpler in Ziti-enabled cases. Coming soon. After running our workload over the Ziti fabric with a Transwarp data plane, we can retrieve the `westworld3` analysis dataset in the same way we did in the plain `dilithium` scenario above. Both routers emit log messages advertising their metrics instrument control socket: ``` [ 0.003] INFO dilithium/util.(*CtrlListener).run: [logs/westworld3.17856.sock] started ``` ``` [ 0.637] INFO dilithium/util.(*CtrlListener).run: [logs/westworld3.30033.sock] started ``` We can use those sockets to snapshot the metrics: ``` [fedora@ip-10-0-0-217 ~]$ fablab/bin/dilithium ctrl client logs/westworld3.17856.sock [ 0.070] INFO dilithium/cmd/dilithium/ctrl.client: received 'ok' [ 0.071] INFO main.main: finished ``` ``` [fedora@ip-10-0-0-222 ~]$ fablab/bin/dilithium ctrl client logs/westworld3.30033.sock [ 0.053] INFO dilithium/cmd/dilithium/ctrl.client: received 'ok' [ 0.053] INFO main.main: finished ``` Retrieve the metrics dataset from the routers and ingest them into the Westworld 3.1 Analyzer using the approach described in the plain Dilithium example. The analyzer does not care if the metrics dataset came from a `dilithium tunnel` or from Ziti. When loading the dataset into the analyzer, you'll see that there is 1 `listener` peer, and 2 each of `listenerConn` and `dialerConn` for the above scenario. In situations where more links are being used, there will potentially be more peers and a larger dataset. # This is a BETA This is the first Transwarp beta release (`beta_1`). Building a protocol like `westworld3` and integrating it into an overlay like Ziti requires a great deal of testing and generation of experience in different environments with different profiles. Anytime we're investigating an issue with Transwarp or with `westworld3`, it's going to be very important that we have a clear metrics dataset and a concrete description of the profile that was used to generate the dataset. Happy Transwarp-ing. [dilithium]: https://github.com/openziti/dilithium [dilithium-concepts]: https://github.com/openziti/dilithium/blob/main/docs/concepts.md [tuning-guide]: https://github.com/openziti/dilithium/blob/main/docs/tuning.md [fabric]: https://github.com/openziti/fabric ## 2. Official Technical Reference & Guides (openziti/ziti-doc) # Building this Project ## Prerequisite * Linux - Documentation is run routinely by our CI * Windows - Developed with [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10) * Doxygen - [Doxygen](http://www.doxygen.nl/) is used to generate the api documentation for the C SDK and is necessary to be on the path ## Building the Docs Site This project uses [GitHub Pages](https://pages.github.com/) to host the static HTML output of building the docs site. GitHub has a few options for where you can put your doc at this time: the main branch, a folder on the main branch named "docs," or a branch named "gh-pages." This project is currently configured to publish by running `publish.sh` which destructively pushes the build output to the root of branch "master" in another repository "openziti/openziti.github.io". That repo is configured to publish the contents of the "master" branch as the GitHub Pages custom domain "openziti.io." The best/easiest thing to do in order to build these docs is to have Windows Subsystem for Linux installed or any shell which can execute a `.sh` script. As of 2020, there's a multitude of ways to get a BASH/shell interpreter in Windows. It's not feasible to test all these shells to make sure this script works so it's encouraged that you use a Linux-based flavor of BASH. If the script doesn't function - open an [issue](./issues). After cloning this repository, open the BASH shell and execute the [gendoc.sh](./gendoc.sh) script. The script has a few flags to pass that control the cleanup of what the script does. In general, it's recommended you use the -w flag so that warnings are treated as errors. Expected usage: `./gendoc.sh -c` > [!NOTE]\ > you must configure NodeJS >=18.0 before running `yarn` > [!WARNING]\ > Do not use `npm` to build this project or add a plugin. Always use `yarn`. ### Local Development Server If you only need to preview changes to docs you're editing, you can skip `gendoc.sh` and run the dev server directly. Some pages import content from external repos (cloned into `docusaurus/docs/_remotes/` by `gendoc.sh`). To satisfy those imports without cloning, create stub files: ```text cd docusaurus/docs mkdir -p \ _remotes/ziti-cmd/dist/docker-images/ziti-router \ _remotes/ziti-cmd/dist/docker-images/ziti-controller \ _remotes/helm-charts/charts/ziti-host \ _remotes/helm-charts/charts/ziti-controller \ _remotes/helm-charts/charts/ziti-router \ _remotes/helm-charts/charts/ziti-edge-tunnel \ _remotes/ziti-android-app \ _remotes/ziti-tunnel-sdk-c/docker for dir in \ _remotes/ziti-cmd/dist/docker-images/ziti-router \ _remotes/ziti-cmd/dist/docker-images/ziti-controller \ _remotes/helm-charts/charts/ziti-host \ _remotes/helm-charts/charts/ziti-controller \ _remotes/helm-charts/charts/ziti-router \ _remotes/helm-charts/charts/ziti-edge-tunnel \ _remotes/ziti-android-app \ _remotes/ziti-tunnel-sdk-c/docker; do echo "# Placeholder" > "$dir/README.md" done ``` Then install dependencies and start the dev server: ```text cd docusaurus yarn install --frozen-lockfile yarn start ``` The dev server hot-reloads as you edit. Pages that depend on stub content will render a placeholder heading instead of the real external content. If you're testing configuration changes, serve the production build with `yarn serve` instead. ## Publishing this Site `./publish.sh` is intended to run in GitHub Actions on branch `main` where the following variables are defined: * `GIT_BRANCH`: output of `git rev-parse --abbrev-ref HEAD` * `gh_ci_key`: base64 encoding of an OpenSSH private key authorized to clobber the `main` branch of [the root doc repo for GitHub pages](https://github.com/openziti/openziti.github.io/tree/main). The [./publish.sh](./publish.sh) script emits a `CNAME` file that defines the custom domain to be used by GitHub Pages. If the script is not changed, the domain will revert to the value defined in the script every time the site is published. To run this script locally, define the necessary variables in your environment. ## Adding a NodeJS Package Docusaurus plugins are distributed as NodeJS packages. This project uses `yarn`, not `npm`, to manage packages, build the project, and run the development server. To add a NodeJS package: ```text cd ./docusaurus yarn add @whatever ``` This will update `./docusaurus/package.json` and `./docusaurus/yarn.lock`. Test and commit these changes. ## Upgrading a NodeJS Package Docusaurus plugins are distributed as NodeJS packages. This project uses `yarn`, not `npm`, to manage packages, build the project, and run the development server. To upgrade a NodeJS package: ```text cd ./docusaurus yarn upgrade @docusaurus/plugin-content-docs ``` This will update `./docusaurus/package.json` and `./docusaurus/yarn.lock`. Test and commit these changes. ## How Search Works Algolia [DocSearch](https://docsearch.algolia.com/) provides search for this site. * Docusaurus v2's classic preset theme provides an integration with Algolia DocSearch v3 as a built-in plugin. The public search API key and application ID are properties in `docusaurus.config.ts`. That plugin provides [the site's `/search` URL](/search) and a search widget in the main navigation ribbon that's visible on all pages. The Javascript running in these elements returns search results from the DocSearch API. * The DocSearch API fetches records from an Algolia index that is populated by [an Algolia crawler](https://crawler.algolia.com/). Search results may be tuned by adjusting the crawler config. The crawler is specifically configured for the Docusaurus theme. If the theme changes structurally then it will be necessary to adjust the crawler config to suit. Changes to the site will not be reflected in the search results if the crawler config does not match the theme. The current config is based on [this configuration template for Docusaurus v2](https://docsearch.algolia.com/docs/templates/#docusaurus-v2-template). ## Check For Broken Links [A CI job](https://github.com/openziti/ziti-doc/actions/workflows/check-links.yml) periodically detects broken links in the GH Pages site and incoming links from external sites but doesn't make any changes to the site source files. An alarm issue is raised and auto-resolved based on the result of the check. With these scripts, you can test all the links in the site's pages and popular incoming request paths. * [crawl-for-broken-links.sh](./check-links/crawl-for-broken-links.sh): uses `docker` to run `muffet` which crawls the given base URL looking for broken links * [check-links.sh](./check-links/check-links.sh): uses `curl` to try a list of URL paths from a file ### Running the Checkers You will need to run `yarn serve` to crawl for broken links locally because the webpack server (`yarn start`) is not crawlable, and you will probably have to deploy to Vercel or GH Pages to test comprehensively for broken links. The `docusaurus` CLI's built-in development server preempts any request for a path ending `.html` with a permanent redirect (HTTP 301) to the same path without the suffix. This prevents the redirects plugin from placing effective redirects as files with `.html` suffixes and employing the meta refresh technique for redirecting user agents to the new location of a page. Also note that the tests are run inside docker so you **must** provide the --host to use when running the serve command. To run the tests, first start docusaurus: ```text yarn build yarn serve --host 192.168.253.239 ``` With docusaurus running the built site, then run the tests. Run the link crawler: ```text ./check-links/crawl-for-broken-links.sh http://192.168.253.239:3000 ... ... http://192.168.253.239:3000/learn/core-concepts/zero-trust-models/ztha [200] http://192.168.253.239:3000/assets/images/client_to_host_a_deploy-7f12913d21af9895978bdbcaf5d6a485.png [SKP] https://github.com/openziti/ziti-doc/tree/main/docusaurus/docs/learn/core-concepts/zero-trust-models/03-ztha.mdx [200] http://192.168.253.239:3000/assets/images/router_to_host_deploy-be9be23f7637508450f80535494f1ac6.png [200] http://192.168.253.239:3000/assets/images/client_to_host_b_deploy-de7416ca10da9162a3a56f86a065b957.png [200] http://192.168.253.239:3000/assets/images/client_to_router_deploy-5dc3be4d91266c234523ad145daf9745.png http://192.168.253.239:3000/learn/core-concepts/zero-trust-models/ztna [SKP] https://github.com/openziti/ziti-doc/tree/main/docusaurus/docs/learn/core-concepts/zero-trust-models/04-ztna.mdx [200] http://192.168.253.239:3000/assets/images/router_to_router_deploy-f2f27b8febfe6594a1aea487003db7ba.png 🤖 Successfully scanned 589 links in 4.645 seconds. ``` Run the popular links check: ```text ./check-links/check-links.sh http://192.168.253.239:3000 check-links/popular-docs-links.txt 0 ... ... INFO : delay set to 0 checking: http://192.168.253.239:3000/blog/zitification/prometheus/part1 checking: http://192.168.253.239:3000/docs/reference/developer/sdk/android checking: http://192.168.253.239:3000/docs/category/deployments Process completed at Tue Aug 5 14:59:47 EDT 2025. Total entries: 133. Elapsed time: 1 seconds. INFO: all 133 links succeeded! ``` ### Update the List of Popular Incoming Links Use Google Analytics to build a "detail" report by modifying the standard report "Pages and screens." Add a column "Page path" to show the URL path for each record. Add a filter for the production hostname `openziti.io` to exclude test instances from the data. Click the "Share" button to export the data as a CSV file. Use a spreadsheet program to filter out redundant and irrelevant records. ## How openziti.io Works The `openziti.io` domain name resolves to GitHub Pages which hosts the static HTML that's output by the Docusaurus build that runs in a GitHub workflow. ## How docs.openziti.io Works This redirector's purpose is to preserve incoming links for this domain. The `docs.openziti.io` domain name resolves to a CloudFront distribution that only responds with an HTTP redirect. If the request path is `/` then it responds with `https://openzit.io/docs` (the docs landing page). Otherwise, it responds with the same path that was requested at `https://openziti.io{path}`. The redirector's behavior is controlled by a Javascript CloudFront function that's deployed automatically when it is changed in this repository. The function is defined in [cloudfront-function-docs-openziti-io.js](./cloudfront/cloudfront-function-docs-openziti-io.js). The function is deployed by [a GitHub workflow](./.github/workflows/deploy-cloudfront.yml) that runs when the file is changed in the `main` branch.