Skip to main content
Version: 2.0

zrok v2.0.0 - Namespaces and Names

Overview

zrok v2.0.0 represents a major evolution of the platform, introducing a fundamental paradigm shift in how sharing and naming work. This release replaces the reserved sharing model with a more powerful and flexible namespaces and names system, adds a new distributed dynamicProxy frontend architecture, and includes significant improvements to the zrok Agent.

breaking changes

This is a major version release with breaking changes. The zrok reserve, zrok release, and zrok share reserved commands have been removed. Please review the Migration Guide section below and the comprehensive v2 Migration Guide for details.

What's New in v2.0

  • Namespaces and Names: logical grouping of share names similar to DNS zones, replacing reserved shares
  • Dynamic Proxy Frontend: new AMQP-based distributed frontend with real-time mapping updates
  • Enhanced Agent: automatic retry with exponential backoff, improved error handling, and persistent shares
  • Private Share Tokens: vanity tokens for private shares using --share-token
  • Name Modification: ability to "upgrade" ephemeral names to reserved names on the fly
  • Improved CLI: human-readable default output for zrok overview, better error messages
  • Modernized Logging: migrated from logrus to structured logging with slog

Breaking Changes

Removed Commands

The following commands no longer exist in v2.0:

zrok reserve public <target>           # use: zrok create name + zrok share public -n
zrok reserve private <target> # use: zrok share private --share-token
zrok share reserved <token> # use: zrok share public/private -n <namespaceToken>:<name>
zrok release <token> # use: zrok delete name
zrok overview public-frontends # use: zrok list namespaces

See the Command Migration Reference below for detailed replacements.

Configuration Changes

  • defaultFrontenddefaultNamespace: the configuration key has been renamed and now specifies the default namespace token rather than frontend
    • Update with: zrok config set defaultNamespace <namespaceToken>
    • Environment variable: ZROK_DEFAULT_NAMESPACE

API Endpoint Changes

For developers using the zrok API:

  • /reserve endpoint removed
  • /release endpoint removed
  • /share endpoint now accepts nameSelections array instead of shareMode and frontendSelection
  • /overview endpoint enhanced with names and namespaces information
  • /metadata/publicFrontendsForAccount removed (replaced with namespace system)

See the API Changes section for complete details.

Major Features

Namespaces and Names System

The core architectural change in v2.0 replaces "reserved shares" with a two-level naming hierarchy:

Namespaces

Namespaces are logical groupings for names, analogous to DNS zones. Each namespace:

  • Has a token (identifier) like public or custom-domain
  • Has a name (DNS zone) like share.zrok.io or example.com
  • Can be open (anyone can create names) or closed (requires grants)
  • Maps to one or more frontend instances

Names

Names are unique identifiers within a namespace, analogous to DNS A records. Each name:

  • Exists within a namespace (e.g., myapp in the public namespace)
  • Can be reserved (persistent across shares) or ephemeral (temporary)
  • Can be assigned to shares using the -n <namespace>:<name> flag
  • Supports multiple names per share

Example Workflow

# list available namespaces
$ zrok list namespaces
╭───────────────────────┬─────────────────┬─────────────╮
│ NAME │ NAMESPACE TOKEN │ DESCRIPTION │
├───────────────────────┼─────────────────┼─────────────┤
│ share.zrok.io │ public │ │
╰───────────────────────┴─────────────────┴─────────────╯

# create a reserved name
$ zrok create name -n public myapp
name 'myapp' created in namespace 'public'

# share using the name (the name persists across share restarts)
$ zrok share public http://localhost:8080 -n public:myapp
your reserved name is: http://myapp.share.zrok.io

# list your names
$ zrok list names
╭───────────────────────────┬────────┬───────────┬─────────────┬──────────┬─────────────────────╮
│ URL │ NAME │ NAMESPACE │ SHARE TOKEN │ RESERVED │ CREATED │
├───────────────────────────┼────────┼───────────┼─────────────┼──────────┼─────────────────────┤
│ myapp.share.zrok.io │ myapp │ public │ │ true │ 2025-10-23 10:15:42 │
╰───────────────────────────┴────────┴───────────┴─────────────┴──────────┴─────────────────────╯

# modify name to toggle reserved status
$ zrok modify name -n public myapp -r=false
name 'myapp' will be released when associated share terminates

# delete the name
$ zrok delete name -n public myapp

Benefits over v1.x reserved shares:

  • Decouples external names from environments and backends
  • Supports multiple names per share
  • Easier to move shares between hosts
  • More flexible namespace management and access control
  • Better aligns with DNS concepts most users understand

Dynamic Proxy Frontend

A new distributed frontend architecture replaces the legacy publicProxy (zrok access public) with dynamicProxy (zrok access dynamicProxy).

Architecture

The dynamic proxy system consists of three components:

  1. dynamicProxyController (in zrok controller)

    • gRPC service for mapping queries from frontends
    • AMQP publisher for broadcasting mapping updates
    • Manages namespace/name to share mappings
  2. AMQP Message Broker (RabbitMQ)

    • Distributes real-time mapping updates
    • Enables synchronization across multiple frontend instances
    • Topic exchange for efficient routing
  3. dynamicProxy Frontend (via zrok access dynamicProxy)

    • HTTP/HTTPS listener serving dynamic mappings
    • AMQP subscriber receiving updates
    • gRPC client for initial and on-demand mapping queries
    • OAuth integration support

Key Improvements

  • Real-time updates: frontends receive mapping changes via AMQP immediately
  • Horizontal scalability: multiple frontend instances can serve the same namespace
  • Namespace-based routing: supports complex multi-domain setups
  • Better isolation: different namespaces can use different frontend instances
  • Consistent OAuth: full OAuth support integrated into the new architecture

Configuration Example

v: 1
frontend_token: KMmfE0VXO7Pp
identity: public
bind_address: "0.0.0.0:8080"

amqp_subscriber:
url: amqp://guest:guest@localhost:5672
exchange_name: dynamicProxy

controller:
identity_path: /home/zrok/.zrok/identities/public.json
service_name: dynamicProxyController

# optional: OAuth, TLS, interstitial page
oauth:
bind_address: "0.0.0.0:8181"
endpoint_url: "https://oauth.example.com"
# ... provider configuration

Migration note: the legacy publicProxy remains available for backward compatibility but will be deprecated in a future release. See the Dynamic Proxy Migration Guide for detailed setup instructions.

Agent Improvements

The zrok Agent received significant enhancements in v2.0:

Retry Manager

Automatic retry with exponential backoff for failed processes:

  • Failed shares/accesses automatically retry
  • Exponential backoff prevents rapid failure loops
  • Configurable maximum retry attempts
  • Errored processes receive transient err_XXXX tokens for management
# view agent status showing failed process with retry state
$ zrok agent status
...

# release the errored process
$ zrok rm err_abc123

Boot Handler

Unified handler for persistent shares and accesses:

  • Shares with reserved name selections automatically restart after agent restart
  • Private shares with --share-token persist across agent restarts
  • Boot handler rebuilds the process tree on startup
  • Improved subprocess lifecycle management
# when agent running, this share persists across agent restarts
$ zrok share public http://localhost:3000 -n public:myapp

# private share with --share-token also persists
$ zrok share private http://localhost:3000 --share-token myapi

Improved Status Display

The zrok agent status command now shows:

  • Detailed error states and failure information
  • Frontend endpoints for public shares
  • Retry attempt counts and next retry time
  • Active, pending, and failed processes
  • Process uptime and restart counts

Private Share Vanity Tokens

Private shares now support custom "vanity" tokens via the --share-token flag:

# create a private share with a memorable token
$ zrok share private http://localhost:8080 --share-token myapi-prod

# access from another environment
$ zrok access private myapi-prod

Benefits:

  • Memorable, meaningful share names
  • Persistent tokens when using the agent
  • Easier sharing with team members
  • Better than random generated tokens for long-lived services

Note: unlike reserved names in namespaces (which are global), share tokens are scoped to your zrok account.

Name Modification Command

New zrok modify name command allows toggling the reserved status of a name:

# upgrade an ephemeral name to reserved (persist it)
$ zrok modify name -n public myapp -r

# downgrade a reserved name to ephemeral (release when share terminates)
$ zrok modify name -n public myapp -r=false

Use case: you start sharing something ephemerally, realize you want to keep the name, and can "upgrade" it without recreating the share. (https://github.com/openziti/zrok/issues/1066)

Human-Readable Overview

The zrok overview command now outputs a formatted, human-readable display by default:

$ zrok overview
* Namespaces

╭───────────────────────┬─────────────┬────────────────╮
│ NAME │ DESCRIPTION │ TOKEN │
├───────────────────────┼─────────────┼────────────────┤
│ names.staging.zrok.io │ │ public │
│ stack.dasl.dev │ │ stack.dasl.dev │
╰───────────────────────┴─────────────┴────────────────╯

* Names

╭──────────────────────────┬─────────────────┬──────────────┬──────────┬─────────────────────╮
│ URL │ NAMESPACE TOKEN │ SHARE TOKEN │ RESERVED │ CREATED │
├──────────────────────────┼─────────────────┼──────────────┼──────────┼─────────────────────┤
│ api.example.com │ example.com │ - │ true │ 2025-10-23 12:31:10 │
│ testing.example.com │ example.com │ jr6zuvicaun9 │ true │ 2025-10-23 11:52:21 │
╰──────────────────────────┴─────────────────┴──────────────┴──────────┴─────────────────────╯

* Environments

╔════════════════════════───────────────────────
> user@linux (envZId: LT.yW-YiYc)
Host: user; linux; linux; ubuntu; debian; 25.04; 6.14.0-34-generic; x86_64
Address: ...
Created: 2025-10-23 11:43:23

> Shares
╭──────────────┬────────┬─────────┬───────────────────────┬─────────┬─────────────────────╮
│ SHARE TOKEN │ MODE │ BACKEND │ TARGET │ LIMITED │ CREATED │
├──────────────┼────────┼─────────┼───────────────────────┼─────────┼─────────────────────┤
│ jr6zuvicaun9 │ public │ proxy │ http://localhost:8585 │ │ 2025-10-23 12:06:18 │
╰──────────────┴────────┴─────────┴───────────────────────┴─────────┴─────────────────────╯
> jr6zuvicaun9:
> testing.example.com

╚════════════════════════───────────────────────

╔════════════════════════───────────────────────
> user@otherhost (envZId: PE9JTm0iYc)
Host: user; otherhost; linux; ubuntu; debian; 25.04; 6.14.0-34-generic; x86_64
Address: ...
Created: 2025-10-23 12:30:04

╚════════════════════════───────────────────────

For the classic JSON output, use --json:

$ zrok overview --json

(https://github.com/openziti/zrok/issues/1064)

Technical Changes

This section provides detailed technical information for developers and self-hosters.

Database Migrations

Five new migrations added (034-038):

  1. 034_v2_0_0_namespaces.sql

    • Creates namespaces table (token, name, description, open mode, timestamps)
    • Creates namespace_grants table (namespace ↔ account many-to-many)
    • Tracks namespace access control for closed namespaces
  2. 035_v2_0_0_namespace_frontend_mappings.sql

    • Creates namespace_frontend_mappings table
    • Links namespaces to frontend instances
    • Enables multiple frontends per namespace for load balancing
  3. 036_v2_0_0_dynamic_frontends.sql

    • Adds dynamic boolean column to frontends table
    • Distinguishes dynamicProxy frontends from legacy publicProxy
  4. 037_v2_0_0_frontend_mappings.sql

    • Creates frontend_mappings table
    • Caches namespace/name to frontend mappings
    • Improves lookup performance for dynamicProxy
  5. 038_v2_0_0_frontend_public_name_unique.sql

    • Adds unique constraint on frontends.public_name (when not deleted)
    • Prevents duplicate frontend public names

Migration management:

# automatic migration on controller start (default)
zrok controller etc/ctrl.yml

# manual migration
zrok admin migrate

# rollback support (new in v2.0)
zrok admin migrate --down 1

API Changes

New Endpoints

Namespace Management (Admin):

  • POST /admin/namespace - create namespace
  • GET /admin/namespace - list all namespaces
  • PUT /admin/namespace/{namespaceToken} - update namespace
  • DELETE /admin/namespace/{namespaceToken} - delete namespace

Namespace Grants (Admin):

  • POST /admin/namespaceGrant - grant user access to closed namespace
  • GET /admin/namespaceGrant/{namespaceToken} - list grants for namespace
  • DELETE /admin/namespaceGrant - revoke namespace grant

Namespace-Frontend Mappings (Admin):

  • POST /admin/namespaceFrontendMapping - map namespace to frontend
  • GET /admin/namespaceFrontendMapping/{namespaceToken} - list frontends for namespace
  • GET /admin/frontendNamespaceMapping/{frontendToken} - list namespaces for frontend
  • DELETE /admin/namespaceFrontendMapping - remove mapping

Name Management (End Users):

  • POST /share/name - create name in namespace
  • GET /share/name - list user's names
  • PUT /share/name - update name (toggle reserved status)
  • DELETE /share/name/{nameToken} - delete name

Namespace Discovery (End Users):

  • GET /share/namespace - list available namespaces (based on grants and open namespaces)

Modified Endpoints

POST /share - create share:

  • Added nameSelections array parameter (replaces shareMode and frontendSelection)
  • Each name selection specifies: namespaceToken, name, reserved
  • Supports multiple names per share
  • Example:
    {
    "envZId": "abc123",
    "shareMode": "public",
    "nameSelections": [
    {"namespaceToken": "public", "name": "myapp", "reserved": true},
    {"namespaceToken": "public", "name": "myapp-staging", "reserved": false}
    ],
    "backendMode": "proxy",
    "backendProxyEndpoint": "http://localhost:8080"
    }

GET /overview - get account overview:

  • Added names array to share details
  • Added namespaces array to account details
  • Includes reserved status and namespace information
  • Enhanced with name-related metadata

Removed Endpoints

  • /reserve - replaced with /share/name (create name) + /share (create share with name selection)
  • /release - replaced with DELETE /share/name/{nameToken}
  • /metadata/publicFrontendsForAccount - replaced with /share/namespace (list available namespaces)

gRPC API (dynamicProxyController)

New gRPC service for controller ↔ dynamicProxy communication:

service DynamicProxyController {
// query mappings for a specific namespace
rpc GetNamespaceFrontendMappings(NamespaceRequest) returns (MappingsResponse);

// query mapping for a specific name
rpc GetNameMapping(NameRequest) returns (NameMappingResponse);
}

Implemented in controller/dynamicProxyController/grpc.go.

Ziti Automation Refactoring

The OpenZiti resource management code was completely rewritten:

Old: controller/zrokEdgeSdk/ (legacy, complex, hard to maintain)

New: controller/automation/ (streamlined, clear, maintainable)

Key Improvements

  • Consistent CRUD interfaces for all resource types
  • Resource managers: IdentityManager, ServiceManager, ConfigManager, etc.
  • Tag-based resource management for cleanup and filtering
  • Better error handling with AutomationError type
  • Centralized client creation and authentication
  • Clearer separation of concerns

Example Usage

// old way
edge, err := zrokEdgeSdk.Client(cfg)
// ... complex error-prone operations

// new way
ziti := automation.NewZitiAutomation(cfg)
envZId, err := ziti.Identities.Create(&automation.IdentityOptions{...})

Makes controller code much easier to understand and maintain. (https://github.com/openziti/zrok/issues/1054)

Logging Migration

Complete migration from pfxlog/logrus to df/dl/slog:

Before:

import "github.com/sirupsen/logrus"
logrus.WithFields(logrus.Fields{"foo": "bar"}).Info("message")

After:

import "github.com/michaelquigley/df/dl"
dl.Info("message", "foo", "bar")

Configuration

Environment variables control logging behavior:

  • DL_USE_JSON=true - force JSON output
  • DL_USE_COLOR=true - force colorized output
  • Channel-specific log levels via dl.Init()

Benefits:

  • Modern structured logging with log/slog
  • Better performance
  • Consistent formatting across the codebase
  • Easier integration with log aggregation tools

(https://github.com/openziti/zrok/issues/1078)

Configuration Changes

Controller Configuration

dynamicProxyController (new in v2.0):

dynamic_proxy_controller:
identity_path: /path/to/dynamicProxyController.json
service_name: dynamicProxyController
amqp_publisher:
url: amqp://guest:guest@localhost:5672
exchange_name: dynamicProxy

Removed:

  • defaultFrontend configuration (replaced with defaultNamespace in client config)

Frontend Configuration (dynamicProxy)

New configuration format for dynamicProxy frontends:

v: 1
frontend_token: <token>
identity: public
bind_address: "0.0.0.0:8080"
mapping_refresh_interval: 1m

amqp_subscriber:
url: amqp://guest:guest@localhost:5672
exchange_name: dynamicProxy

controller:
identity_path: /path/to/identity.json
service_name: dynamicProxyController

# optional
tls:
cert_path: /path/to/cert.pem
key_path: /path/to/key.pem

oauth:
# ... provider configuration

See the Dynamic Proxy Guide for complete configuration details.

Dependencies

Updated:

  • github.com/openziti/sdk-golangv1.2.4
  • github.com/TwiN/go-awayv1.8.0 (profanity checking for unique names)
  • golangv1.25.3 (build toolchain)

New:

  • github.com/michaelquigley/df (new logging framework)
  • github.com/rabbitmq/amqp091-go (AMQP client for dynamicProxy)
  • google.golang.org/grpc (gRPC for dynamicProxyController)

CLI Changes

New Commands

Name Management:

zrok create name [-n <namespaceToken>] <name>     # create reserved name
zrok delete name [-n <namespaceToken>] <name> # delete name
zrok list names # list your names
zrok list namespaces # list available namespaces
zrok modify name [-n <namespaceToken>] <name> -r # toggle reserved status

Admin Namespace Commands:

zrok admin create namespace <token> <name>                      # create namespace
zrok admin delete namespace <token> # delete namespace
zrok admin list namespaces # list all namespaces
zrok admin update namespace <token> # update namespace
zrok admin add namespaceGrant <namespaceToken> <email> # grant access
zrok admin remove namespaceGrant <namespaceToken> <email> # revoke access
zrok admin add namespace-frontend <nsT> <frontend> # map to frontend
zrok admin remove namespace-frontend <nsT> <frontend> # remove mapping
zrok admin list namespace-frontend <namespaceToken> # list frontends
zrok admin list frontend-namespace <frontendToken> # list namespaces

Dynamic Proxy:

zrok access dynamicProxy <config.yml>   # start dynamic proxy frontend

Admin Frontend Updates:

zrok admin create frontend --dynamic <identity> <urlTemplate>   # create dynamic frontend
zrok admin update frontend --dynamic <token> # enable dynamic mode
zrok admin list frontends --extra # show v1 properties

Modified Commands

zrok share public:

# v1.x
zrok share public --backend-mode web /path/to/files

# v2.0 - now accepts name selections
zrok share public --backend-mode web /path/to/files -n public:myapp
zrok share public http://localhost:8080 -n public:app1 -n public:app2 # multiple names

zrok share private:

# v2.0 - new --share-token flag for vanity tokens
zrok share private http://localhost:8080 --share-token myapi-prod

zrok overview:

# v2.0 - human-readable by default
zrok overview

# use --json for old JSON format
zrok overview --json

zrok agent status:

  • Enhanced output with error details, frontend endpoints, retry information
  • Shows both active and errored processes
  • Displays transient err_XXXX tokens for failed processes

zrok admin migrate:

# v2.0 - now supports rollback
zrok admin migrate --down 1 # rollback 1 migration
zrok admin migrate --down 5 # rollback 5 migrations

Command Migration Reference

v1.x Commandv2.0 Replacement
zrok reserve public <target>zrok create name -n <ns> <name> + zrok share public <target> -n <ns>:<name>
zrok reserve private <target>zrok share private <target> --share-token <name>
zrok share reserved <token>zrok share public <target> -n <ns>:<name>
zrok release <token>zrok delete name -n <ns> <name>
zrok overview public-frontendszrok list namespaces

Migration Guidance

For End Users

If you're using zrok.io or another hosted instance:

  1. Upgrade your zrok client to v2.0.0 or later
  2. Set default namespace (provided by your instance administrator):
    zrok config set defaultNamespace public
  3. Recreate reserved shares using names:
    # instead of: zrok reserve public 8080
    zrok create name myapp
    zrok share public localhost:8080 -n public:myapp
  4. Update scripts and automation to use new commands

Important: v1.x reserved shares will stop working after upgrading. You must recreate them using the v2.0 name system.

For Self-Hosters

If you run your own zrok instance:

  1. Review the Dynamic Proxy Migration Guide for detailed steps
  2. Decide on migration approach:
    • Side-by-side: run publicProxy and dynamicProxy simultaneously (no downtime, gradual migration)
    • Clean cutover: shut down publicProxy and start dynamicProxy (downtime required, simpler)
  3. Install RabbitMQ (required for dynamicProxy)
  4. Update controller configuration (add dynamicProxyController config)
  5. Create namespaces for your users
  6. Deploy dynamicProxy frontends
  7. Notify users of the migration and provide documentation

Database migrations run automatically by default when starting the controller. To disable auto-migration:

# controller config
store:
disable_auto_migration: true

Then run migrations manually:

zrok admin migrate

Migration Timeline Recommendation

For self-hosters with active users:

  • Weeks 1-2: set up dynamicProxy in parallel, test thoroughly
  • Weeks 3-4: communicate migration to users, provide documentation
  • Weeks 5-8: monitor adoption, provide support
  • Week 9+: deprecate and shut down publicProxy after sufficient adoption

Bug Fixes and Improvements

Name Validation

Split name validation for share tokens vs namespace names:

  • Share tokens: allow UUIDs and more flexible format
  • Namespace names: stricter validation aligned with DNS requirements
  • Support for UUID-based names

(https://github.com/openziti/zrok/issues/532)

Agent Process Management

Improved lifecycle management for subordinate processes:

  • Better error handling during agent reload
  • Exponential backoff retry for failed processes
  • Transient error tokens (err_XXXX) for management
  • Process tree cleanup on agent shutdown

(https://github.com/openziti/zrok/issues/1000)

OpenZiti SDK Integration

Fixed API session allocation issue:

  • Previous: new API session per /agent/* request
  • Fixed: reuse API sessions for agent remoting endpoints
  • Reduces load on Ziti controller

(https://github.com/openziti/zrok/issues/1023)

Striped session cookies for large authentication payloads:

  • Supports larger JWT tokens from OIDC providers
  • Splits large cookies across multiple smaller cookies
  • Prevents cookie size limit issues

(https://github.com/openziti/zrok/issues/1101)

Improved link detection in web UI and API:

  • Fixed broken documentation links
  • Updated icon/favicon in API console
  • Corrected URLs in invitation emails

(https://github.com/openziti/zrok/issues/1094, https://github.com/openziti/zrok/issues/1105)

Metrics Scope

Fixed metrics scope field in API responses:

  • Environment metrics now correctly report scope: "environment"
  • Share metrics now correctly report scope: "share"

(https://github.com/openziti/zrok/issues/1031)

Known Issues and Limitations

Legacy publicProxy Deprecation

The legacy publicProxy frontend (zrok access public) is still available in v2.0 for backward compatibility but is considered deprecated. Future versions will remove publicProxy entirely. All new deployments should use dynamicProxy.

Migration Complexity

Migrating from v1.x reserved shares to v2.0 namespaces/names requires recreating shares. There is no automatic migration path. Plan accordingly and communicate clearly with users.

RabbitMQ Requirement

The dynamicProxy system requires RabbitMQ (or another AMQP 0.9.1 broker). This adds infrastructure complexity for self-hosters. For small deployments, the legacy publicProxy may be simpler initially, but we strongly encourage migrating to dynamicProxy for long-term maintainability.

Upgrade Path

Minimum Version Requirements

  • zrok client: v2.0.0+
  • zrok controller: v2.0.0+
  • OpenZiti: v1.6.0+ (tested, earlier versions may work)
  • RabbitMQ: 3.x+ (for dynamicProxy)
  • Golang: 1.25.3+ (for building from source)
  • Node.js: 22+ (for building UIs)

Compatibility Matrix

Componentv1.x Clientv2.0 Client
v1.x ControllerFull supportNot compatible
v2.0 ControllerNot CompatibleFull support

Important: In order to maintain compatibility for both v1.x and v2.x clients, the intention is that both zrok controller versions are operated in parallel, on different API endpoint URLs. Database compatibility will be maintained for v1.x controllers through the v2.0.x series. Once v2.1.0 is released, the database compatibility for v1.x controllers will be removed.

Acknowledgments

This release includes contributions from:

  • NetFoundry engineering team
  • OpenZiti community
  • zrok users who provided feedback and testing

Special thanks to everyone who participated in the v2.0 beta testing program.

Get Support

If you encounter issues or have questions:


Next: Back to Changelog Index