mezctl CLI Reference
mezctl is the administrative CLI for a Mezite cluster. It connects
to the auth service (default localhost:3025, override with --auth-server) and operates on every long-lived cluster resource: users, MFA
factors, roles, locks, join tokens, agents and agentless nodes, audit
logs, recordings, access requests, SSO connectors, trusted clusters,
workload identities, agent identities, the CA, and cluster config.
Every command authenticates via a session token. Get one with
mezctl login, then pass it with --token or export
it as MEZITE_AUTH_TOKEN.
login
Authenticate against the auth service and print a session token. The common pattern is to capture the token into an environment variable so subsequent commands inherit it.
# Local username/password
export MEZITE_AUTH_TOKEN=$(mezctl login \
--auth-server=mezite.example.com:3025 \
--username=admin \
--password="$MEZITE_ADMIN_PASSWORD")
# Use the captured token for subsequent commands
mezctl users list --username and --password fall back to
MEZITE_ADMIN_USERNAME and
MEZITE_ADMIN_PASSWORD, which keeps the password off the
process table in scripts and unit files.
users
Create, list, delete, and reset MFA for cluster users.
# Create a user with roles
mezctl users create alice --roles=developer,viewer
# List users
mezctl users list
# Delete a user
mezctl users delete --username=alice
# Reset an enrolled second factor
mezctl users reset-totp alice
mezctl users reset-webauthn alice
# Inspect a user's enrolled MFA factors
mezctl users list-mfa alice
# Print enrollment instructions for a new MFA factor
# (currently only --type=webauthn is supported)
mezctl users add-mfa alice --type=webauthn roles
Manage the RBAC roles that govern SSH access. See the RBAC guide for the role schema.
# Create a role from a JSON document ({"metadata": {...}, "spec": {...}})
mezctl roles create --from-file=role-developer.json
# Or create one inline
mezctl roles create --name=developer --spec='{"allow":{"logins":["ubuntu"]}}'
# Update an existing role (--from-file only)
mezctl roles update --from-file=role-developer.json
# List roles
mezctl roles ls
# Render a role
mezctl roles get developer
# Delete a role. Refuses if the role is still assigned to a user;
# re-run with --force to confirm.
mezctl roles delete developer
mezctl roles delete developer --force --from-file parses JSON only. Both
create and update read the same document
shape; create additionally accepts
--name plus --spec for a one-liner.
tokens
Manage one-time join tokens used by mezd agents and bots to enroll
into the cluster.
# Create a static node join token (the default type; TTL 1h)
mezctl tokens create --roles=node --ttl=1h
# Reusable static token — 0 means unlimited
mezctl tokens create --roles=node --ttl=24h --max-uses=50
# List active tokens
mezctl tokens list
# Delete a token by name or id
mezctl tokens delete <name-or-id> --type selects how a joining node proves who it is:
static (default — the token value itself is the secret), or
iam / gcp / azure, where the node
instead attests with a cloud-provider identity and the token names which
identities are allowed. Cloud-join tokens are reusable and long-lived,
which is what makes them suitable for autoscaling groups.
# AWS: allow any instance in one account whose role ARN matches a glob
mezctl tokens create \
--type=iam \
--name=asg-web \
--roles=node \
--allow-account=123456789012 \
--allow-arn="arn:aws:sts::123456789012:assumed-role/web-node/*"
# Multi-account: repeat --allow-iam-rule instead
# (mutually exclusive with --allow-account / --allow-arn)
mezctl tokens create \
--type=iam --name=asg-multi --roles=node \
--allow-iam-rule="account=123456789012,arn=arn:aws:sts::123456789012:assumed-role/web-node/*" \
--allow-iam-rule="account=210987654321"
# GCP / Azure
mezctl tokens create --type=gcp --name=gke-nodes --roles=node --allow-project=my-project
mezctl tokens create --type=azure --name=vmss --roles=node --allow-subscription=<sub-id> nodes
List, register, update, and remove SSH nodes. Mezite supports two kinds
of nodes: agent-based nodes (running
mezd, which dials out a reverse tunnel) and
agentless OpenSSH nodes (no mezd; the
proxy reaches the node's existing sshd directly).
# List all nodes
mezctl nodes ls
# Show the full record for one node
mezctl nodes get web-01
# Update labels on a node
mezctl nodes update web-01 --labels env=staging,role=web
# Remove a node from the cluster
mezctl nodes rm web-01 # Pin a host key from a file on disk
mezctl nodes add \
--openssh \
--name=db-legacy-01 \
--host=db-legacy-01.internal \
--port=22 \
--labels=env=production,role=db \
--host-key-file=/etc/ssh/ssh_host_ed25519_key.pub \
--verify-host-key
# Or paste the key inline (authorized_keys format)
mezctl nodes add \
--openssh \
--name=db-legacy-01 \
--host=db-legacy-01.internal \
--port=22 \
--host-key="ssh-ed25519 AAAA..." \
--verify-host-key
# Rotate (or pin) the host-key fingerprint on an agentless node
mezctl nodes rotate-host-key db-legacy-01 \
--host-key-file=/etc/ssh/ssh_host_ed25519_key.pub
Only agentless nodes are registered through mezctl nodes add; an agent-based node joins itself by calling
GenerateHostCerts + RegisterAgent on first start,
using a token from mezctl tokens create.
apps
Register and manage the internal applications exposed through the proxy. Each application maps a public hostname to a private internal address served by one or more bound agents. See the Application Access guide for the end-to-end flow.
# Register an internal HTTP app, bound to two serving agents
mezctl apps register \
--name=grafana \
--uri=http://10.0.3.12:3000 \
--public-addr=grafana.apps.example.com \
--labels=env=prod,team=platform \
--agent-hostname=node-1 \
--agent-hostname=node-2
# Register a TCP app (databases, caches, custom daemons)
mezctl apps register \
--name=redis-internal \
--protocol=tcp \
--uri=tcp://10.0.3.20:6379 \
--public-addr=redis-internal.apps.example.com \
--agent-hostname=node-1
# Inspect
mezctl apps ls
mezctl apps get grafana
# Update an app's config (replaces all non-agent fields)
mezctl apps update grafana \
--uri=http://10.0.3.12:3000 \
--public-addr=grafana.apps.example.com \
--labels=env=prod,team=platform
# Manage the serving set without re-registering
mezctl apps add-agent grafana node-3
mezctl apps rm-agent grafana node-2
# Take an app offline temporarily (config + bindings preserved), or remove it
mezctl apps disable grafana
mezctl apps enable grafana
mezctl apps rm grafana -
--uriis the internal address the agent dials; it never appears in DNS. -
--public-addrmust be a subdomain of the configuredproxy.apps_domain. -
--protocolishttp(default) ortcp.--agent-hostnameis repeatable for HA. -
--labelsdrive RBAC viaapp_labelson roles;--custom-headers,--strip-path-prefix, and--inject-identitytune HTTP forwarding.
auth export
Export public trust material for offline verification. Today it exports the application-identity JWT public key set (JWKS), which a backend uses to verify the signed identity assertion the proxy attaches to forwarded application requests — useful for backends with no outbound internet to fetch the proxy's published JWKS endpoint.
# Write the JWKS to a file (default --type is app-jwt)
mezctl auth export --type=app-jwt --out=app-jwt-jwks.json
# Or print to stdout
mezctl auth export --type=app-jwt locks
Locks are the cluster-wide kill switch for an identity. A lock takes
effect immediately on the auth service and propagates to active sessions
on the next interceptor pass — they are how you stop a compromised user,
role, agent, or agent identity from doing further damage without waiting
for cert expiry. Supported target types are
user, role, agent, and
agent_identity.
# Lock a user (any active sessions and any new cert issuance are denied)
mezctl locks create --user=alice --message="Security review"
# Lock a role across all its holders
mezctl locks create --role=on-call-prod --message="Pager freeze"
# Lock a specific agent identity (e.g. a compromised bot)
mezctl locks create --type=agent_identity --name=ci-bot --message="Rotating credentials"
# List active locks
mezctl locks list
# Remove a lock
mezctl locks delete --target-type=user --target-name=alice access-requests
Review just-in-time access requests for elevated roles. Verbs are
approve, deny, cancel (used by the
requester before approval), and revoke (used after approval to
terminate an active grant).
# List pending and historical requests
mezctl access-requests ls
mezctl access-requests ls --state=pending
# Show everything about one request, including any requested resources
mezctl access-requests show <request-id>
# Reviewer side (--reason is recorded on the review)
mezctl access-requests approve <request-id> --reason="on-call escalation"
mezctl access-requests deny <request-id> --reason="use the runbook"
# Revoke an already-approved grant (requester or admin)
mezctl access-requests revoke <request-id>
# Requester side: walk back an unanswered request
mezctl access-requests cancel <request-id>
A request can ask for roles, for specific SSH nodes, or both.
mezctl access-requests show renders requested resources as
ssh-node:<name>, or
ssh-node:<cluster>/<name> for a node in a
trusted cluster. Requesters create them with
msh request create.
access-requests is also reachable as ar.
sessions
Inspect and terminate active SSH sessions. Termination is enforced by the proxy and the agent; the session ends immediately on the operator side.
# All active and pending sessions
mezctl sessions ls
# Only sessions held open waiting for moderators to join
mezctl sessions ls --state=pending
mezctl sessions terminate <session-id> --state takes pending, running, or
terminated.
recordings
List and download session recording metadata and the recording bytes
themselves. The download verb pulls the recording from the configured
storage backend (local or s3) and writes it to
disk so it can be played back with msh play.
# List recent recordings (paginated, newest first; default 50 rows)
mezctl recordings ls
# Filter by user (admin only — non-admins always see only their own)
mezctl recordings ls --user=alice
# Bound by start time, or match a session-ID prefix
mezctl recordings ls --from=2026-07-01T00:00:00Z --to=2026-07-02T00:00:00Z
mezctl recordings ls --session-prefix=abc123 --limit=200
# Download to disk (defaults to ./<session-id>.cast)
mezctl recordings download <session-id> --output=/tmp/<session-id>.cast audit
Query the audit log. See the Audit Logging guide for the event schema.
# --since defaults to 1h
mezctl audit ls
mezctl audit ls --type=session.start
mezctl audit ls --type=access.denied --since=1h
mezctl audit ls --user=alice --since=24h
# NDJSON, one event per line, with the full Details map
mezctl audit ls --since=24h --format=json --output is an alias for --format. The default
text format prints a table and elides the
Details map; use json when you need the whole
event.
For long-term retention, configure the S3 audit-export sink
(audit_export.*) on the
Configuration page.
ca
Manage the cluster's certificate authorities. Mezite runs three CAs — User, Host, and SPIFFE — and exposes a multi-phase rotation state machine so a CA key can be swapped without invalidating in-flight certificates. The state machine is described in the architecture page.
# Show the current phase and certificate expiry for every CA
mezctl ca status
# Or narrow to one CA
mezctl ca status --type=user
# Export a CA's public material (e.g. for trust bundles or audits)
mezctl ca export --type=host # default
mezctl ca export --type=user
# Pick a single encoding; omit --format to print both
mezctl ca export --type=host --format=ssh # authorized_keys line
mezctl ca export --type=user --format=pem # x509 PEM ca export handles the Host and
User CAs only. The SPIFFE CA can be rotated (below) but
is not exported through this subcommand — workloads fetch the SPIFFE
trust bundle from the proxy at
/v1/webapi/spiffe/bundle.json.
# Begin rotation on the User CA: a new key is generated alongside the
# existing key. Both keys are trusted; existing certs continue to validate.
# --type accepts 'host' (default), 'user', or 'spiffe'.
mezctl ca rotate --type=user
# Advance to the next phase once you've verified clients are pinning the
# new trust bundle. Phases: init -> update_clients -> update_servers ->
# complete.
mezctl ca advance --type=user
# Roll back to the previous phase if a phase advance broke a population of
# clients you can't redeploy quickly.
mezctl ca rollback --type=user connectors
Manage SSO authentication connectors (OIDC, SAML, GitHub OAuth, LDAP / Active Directory). See the SSO guide for per-IdP setup instructions.
# Create an OIDC connector (Okta example)
mezctl connectors create \
--name=okta \
--type=oidc \
--issuer-url=https://dev-12345.okta.com/oauth2/default \
--client-id="$OKTA_CLIENT_ID" \
--client-secret="$OKTA_CLIENT_SECRET" \
--redirect-url=https://mezite.example.com/v1/webapi/oidc/callback \
--claims-to-roles="groups:engineering:access,ssh-production;groups:platform-team:admin"
# Create a SAML connector
mezctl connectors create \
--name=corp-saml \
--type=saml \
--entity-id=mezite \
--idp-metadata-url=https://idp.example.com/metadata \
--acs-url=https://mezite.example.com/v1/webapi/saml/acs \
--attributes-to-roles="groups:engineering:access"
# Create a GitHub OAuth connector
mezctl connectors create \
--name=github \
--type=github \
--client-id="$GITHUB_CLIENT_ID" \
--client-secret="$GITHUB_CLIENT_SECRET" \
--redirect-url=https://mezite.example.com/v1/webapi/github/callback \
--teams-to-roles="acme:platform:access,ssh-production"
# Create an LDAP / Active Directory connector
mezctl connectors create \
--name=corp-ldap \
--type=ldap \
--server=ldaps://dc01.corp.example.com:636 \
--bind-dn="CN=svc-mezite,OU=Service,DC=corp,DC=example,DC=com" \
--bind-password="$LDAP_BIND_PASSWORD" \
--user-search-base-dn="OU=Users,DC=corp,DC=example,DC=com" \
--group-search-base-dn="OU=Groups,DC=corp,DC=example,DC=com" \
--groups-to-roles="engineering:access;platform-admins:admin"
# List configured connectors
mezctl connectors list
# Delete a connector
mezctl connectors delete --name=okta
The callback URL you register with the identity provider must match the
route the proxy actually serves:
/v1/webapi/oidc/callback for OIDC,
/v1/webapi/saml/acs for SAML, and
/v1/webapi/github/callback for GitHub.
trusted-clusters
Establish proxy-to-proxy trust between two Mezite clusters so a user in the root cluster can SSH into nodes registered with a leaf cluster. The flow is:
-
On the root cluster, mint a single-use trust token with
mezctl trusted-clusters create-token. -
On the leaf cluster, redeem the token with
mezctl trusted-clusters join. -
On the root cluster, the new leaf shows up in
mezctl trusted-clusters listand can beenabled,disabled, ordeleted.
# On the root cluster: mint a token and hand it to the leaf operator.
# Exactly one of --role-map or --no-role-map is required.
mezctl trusted-clusters create-token \
--allow-cluster=leaf-eu \
--role-map="access:leaf-access,admin:leaf-admin" \
--ttl=30m
# Or pass caller roles through to the leaf unchanged
mezctl trusted-clusters create-token --allow-cluster=leaf-eu --no-role-map --ttl=30m
# On the leaf cluster: redeem the token
mezctl trusted-clusters join \
--root-proxy=root.example.com:443 \
--cluster-name=leaf-eu \
--join-token=<paste-the-token>
# Back on the root cluster: inspect and manage
mezctl trusted-clusters list
mezctl trusted-clusters get leaf-eu
mezctl trusted-clusters disable leaf-eu
mezctl trusted-clusters enable leaf-eu
mezctl trusted-clusters delete leaf-eu
# Token housekeeping
mezctl trusted-clusters list-tokens
mezctl trusted-clusters delete-token <token> agent-identities
Manage the identities used by non-human workloads — CI/CD pipelines,
services, and automation — that need cluster credentials without a
human login. add creates the identity and returns a
single-use bootstrap token; the workload redeems it with
mezd identity start and thereafter renews its own
certificates automatically using a renewal token.
# Create an identity and mint its one-time bootstrap token
mezctl agent-identities add --name=ci-agent --roles=node-access --token-ttl=1h
mezctl agent-identities ls
mezctl agent-identities status ci-agent
mezctl agent-identities rm ci-agent
The returned token can only be used once, so re-running
add is also how you recover an identity whose host lost its
credentials. --token-ttl defaults to 1h. To
stop a compromised identity immediately, lock it — see
mezctl locks create --type=agent_identity above.
workload-identities
Manage SPIFFE workload identities. Each identity binds a workload role
(encoded in the SVID URI SAN) to a node or set of nodes. Workloads on
those nodes then call the
mezd identity Unix socket to fetch short-lived X.509-SVIDs or
JWT-SVIDs signed by the SPIFFE CA.
# Attestation is deny-by-default: at least one allow rule is required.
# Repeat a flag to build alternative rules (OR'd together).
mezctl workload-identities create \
--name=builder \
--spiffe-id=workload/builder \
--uid=1500 \
--binary=/usr/local/bin/builder
# For AND within a single rule, hand-author the full rule set as JSON
mezctl workload-identities create \
--name=builder \
--spiffe-id=workload/builder \
--rules-file=workload-rules.json
mezctl workload-identities ls
mezctl workload-identities get --name=builder
mezctl workload-identities rm --name=builder --rules-file takes a JSON document of the form
{"allow": [...]} and overrides the individual
--uid / --gid / --binary /
--binary-sha256 flags. Note that
get and rm identify the identity with
--name, not a positional argument.
devices
Manage trusted devices that the cluster uses for device-trust
enforcement. A role's device_trust_mode decides what the
enrolment state actually buys:
required rejects a login from an un-enrolled device,
optional lets the login succeed but omits the device ID
extension from the issued certificate, and off (the
default) skips the check entirely. Enrollment
is a two-step, token-mediated flow: an operator issues a single-use
enrollment token for a named user, and the device is then registered
against that token. The enrolled device is owned by the user the token
was issued for — never by the caller.
# 1. Issue a single-use, time-limited enrollment token for a user.
# The token is shown once and cannot be retrieved again.
mezctl devices enroll-token --user=alice --ttl=15m
# Optionally bind the token to one specific machine up front
mezctl devices enroll-token --user=alice --serial=C02XY1234567 --os=darwin
# 2. Redeem it. Users can do this themselves with 'msh device enroll';
# an operator enrolling on their behalf passes the device details.
mezctl devices add \
--enrollment-token=<token> \
--serial=C02XY1234567 \
--os=darwin \
--os-version=15.3 \
--hostname=alice-mbp \
--attestation-type=secure_enclave
# Inspect
mezctl devices ls
mezctl devices ls --user=alice --status=trusted --os=darwin
# Revoke trust (record kept), or remove the record entirely
mezctl devices revoke --device-id=<id>
mezctl devices rm --device-id=<id> -
--attestation-typeis one ofnone(default),secure_enclave,tpm2, ormanual. -
ls --statusfilters ontrusted,untrusted, orrevoked. -
revokeandrmtake the device ID via--device-id, not as a positional argument.
integrations
Configure outbound third-party integrations such as the
aws-oidc federation used by managed deployments to mint short-lived
AWS credentials from a Mezite identity. Today only the
aws-oidc configure flow is implemented; in script mode it prints
the AWS CLI commands and trust policy to apply by hand, and in live mode (when
--aws-profile and/or
--aws-region is supplied) it calls AWS IAM directly.
# Script mode: print the aws-cli commands and IAM trust policy
mezctl integrations configure aws-oidc \
--account=123456789012 \
--spiffe-id="spiffe://mezite.example.com/*" \
--role-name=MeziteWorkloadRole
# Live mode: actually create / converge the OIDC provider in AWS
mezctl integrations configure aws-oidc \
--aws-profile=mezite-prod \
--aws-region=us-east-1 \
--account=123456789012 \
--spiffe-id="spiffe://mezite.example.com/*" \
--role-name=MeziteWorkloadRole config
Read and update the per-cluster runtime config record (distinct from the YAML/env-var bootstrap config). Useful when changing a setting that's intended to be cluster-wide and observable from any node, e.g. the access-request reviewer policy.
mezctl config get <key>
mezctl config set <key> <value> Global flags
-
--auth-server <addr>— Auth service gRPC endpoint (defaultlocalhost:3025). -
--token <session-token>— Session token. Also honoured via theMEZITE_AUTH_TOKENenvironment variable. -
--ca-cert <path>— PEM CA certificate used to verify the auth server. Also honoured viaMEZITE_CA_CERT. -
--insecure— Use an insecure (unverified) gRPC connection. Dev-only. -
--no-alpn— Disable ALPN single-port negotiation. Also settable withMEZITE_ALPN_MODE=false. Needed only for servers that reject unknown ALPN names; leave it off for cloud-deployed or firewalled clusters.