CLI reference: mt

mt is one native binary for macOS, Linux and Windows (amd64 and arm64) with nothing else to install. It enrolls a workload and keeps its certificate renewed, calls services over mTLS, runs the Mutual Trust enforcer and the integration adapters, and installs itself as a system service. Keys are generated locally and never leave the machine.

Documentation menu

Install

The install scripts detect the operating system and architecture, download the release archive, verify the Ed25519 signature on the release's SHA256SUMS with the Mutual Trust release key built into the script, check the archive's SHA-256 against the signed list, and install mt. No runtime, package manager or administrator rights are required.

macOS and Linux
curl -fsSL https://mutualtrust.io/install.sh | sh
Windows (PowerShell 5.1 or 7)
irm https://mutualtrust.io/install.ps1 | iex
Installer settings
VariableDefaultMeaning
MT_VERSIONlatestVersion to install.
MT_INSTALL_DIR/usr/local/bin when writable, else ~/.local/bin; on Windows %ProgramFiles%\MutualTrust when elevated, else %LOCALAPPDATA%\Programs\MutualTrust (added to PATH)Target directory.
MT_REQUIRE_SIGNATURE=1offinstall.sh only: refuse to install when the signature cannot be checked.
MT_OPENSSLfound automaticallyinstall.sh only: path to an OpenSSL 3 binary.

Both installers verify the release signature before installing. install.sh checks the Ed25519 signature (SHA256SUMS.sig) when OpenSSL 3 is present, and otherwise the ECDSA P-256 signature (SHA256SUMS.p256.der.sig) with any openssl, including the LibreSSL that macOS ships. Set MT_REQUIRE_SIGNATURE=1 to refuse an install that cannot be verified. install.ps1 verifies the P-256 signature using .NET.

Packages and archives

Every release is published at https://mutualtrust.io/downloads/mt/VERSION/; /downloads/mt/latest holds the current version.

Release files
FileUse
mt_VERSION_OS_ARCH.tar.gz, mt_VERSION_windows_ARCH.zipThe mt binary with the systemd units, for darwin, linux and windows on amd64 and arm64.
mt_VERSION_linux_ARCH.deb, .rpmInstalls /usr/bin/mt, the mt-renew and mt-enforcer systemd units (not enabled), /etc/default/mt, /etc/mutualtrust/enforcer.yaml and /var/lib/mutualtrust (mode 0700).
mt-enforcer_VERSION_OS_ARCH.tar.gz, .zipThe standalone enforcer, for hosts that run only the enforcement plane.
SHA256SUMS, SHA256SUMS.sigSHA-256 of every file, and its raw Ed25519 signature with the release key.
Debian or Ubuntu (amd64)
curl -fsSLO https://mutualtrust.io/downloads/mt/1.0.0/mt_1.0.0_linux_amd64.deb sudo apt install ./mt_1.0.0_linux_amd64.deb
RHEL, Fedora or Amazon Linux (amd64)
sudo dnf install https://mutualtrust.io/downloads/mt/1.0.0/mt_1.0.0_linux_amd64.rpm

A container image (distroless, nonroot, mt renew --watch by default, state in the /var/lib/mutualtrust volume) builds from services/mt/Dockerfile: docker build -f services/mt/Dockerfile -t mt services.

Verify a download

The release public key is published at mutualtrust.io/downloads/mt-release.pub and is built into mt self-update and both install scripts. With OpenSSL 3:

Verify the signature, then the checksum
B=https://mutualtrust.io/downloads/mt/1.0.0 curl -fsSLO $B/SHA256SUMS -O $B/SHA256SUMS.sig -O $B/mt_1.0.0_linux_amd64.tar.gz curl -fsSL https://mutualtrust.io/downloads/mt-release.pub -o mt-release.pub openssl pkeyutl -verify -pubin -inkey mt-release.pub -rawin -in SHA256SUMS -sigfile SHA256SUMS.sig sha256sum --ignore-missing -c SHA256SUMS # macOS: shasum -a 256 --ignore-missing -c SHA256SUMS

Global options

Global options
FlagEnvironmentDefaultMeaning
--api URLMT_APIhttps://api.mutualtrust.io, or the control plane the identity was enrolled withControl-plane base URL.
--home DIRMT_HOMEsee belowState directory: the workload identity in DIR/workload, gateway pairings in DIR/gateway.
--jsonoffOne JSON document on stdout; progress goes to stderr.
--no-colorNO_COLORcolors on a terminalPlain output.
Default home directory
PlatformDefaultAs root or a service
Linux$XDG_CONFIG_HOME/mutualtrust (~/.config/mutualtrust)/var/lib/mutualtrust
macOS~/Library/Application Support/MutualTrust/Library/Application Support/MutualTrust
Windows%ProgramData%\MutualTrust%ProgramData%\MutualTrust

Flags may appear before or after positional arguments. mt help COMMAND (or mt COMMAND --help) prints a command's flags.

Upgrading from the JavaScript CLI. When the default home has no identity and ~/.mt does, mt copies ~/.mt/workload and the gateway pairings into the default home on first use and leaves a MIGRATED.txt note in ~/.mt; the old files are left in place for anything that still reads them. With --home ~/.mt (or MT_HOME), mt uses that directory as it is: both CLIs read and write the same files.

mt gateway

Starts the sample Orders API and runs the enforcer, which is built into mt, in proxy mode in front of it. It pairs the gateway (first run only) and keeps running. mt quickstart is an alias.

Usage
mt gateway [--port 8443] [--origin-port 9001 | --origin URL] [--admin-port N] mt gateway pair --print-credentials [--out FILE] [--connector cloudflare-worker]
mt gateway flags
FlagDefaultMeaning
--port N8443HTTPS (mTLS) listener on 127.0.0.1.
--origin-port N9001Port of the sample Orders API on 127.0.0.1. It rejects requests that did not come through the gateway.
--origin URLsample APIPut the gateway in front of your own origin instead of starting the sample Orders API.
--admin-port Nport + 1Enforcer admin listener on 127.0.0.1: /metrics, /healthz, /readyz.
--log-format FtextEnforcer log format: text or json.
--enforcer PATHbuilt inRun this standalone mt-enforcer binary instead of the built-in enforcer.
  • On first run the enforcer generates a P-256 gateway key, prints a pairing code and approval URL, and waits until the code is approved in the portal. The approved credential and pinned policy-verification key are saved to DIR/gateway/gateway-state.json (mode 0600); later runs reuse them.
  • A self-signed localhost server certificate is created at DIR/gateway/server.pem; mt call trusts it automatically.
  • The gateway refreshes policy every 15 to 17 seconds, sends signed acknowledgments and receipts, and logs every decision with its request ID, verdict, reason, principal and policy revision.
  • Ctrl-C flushes pending receipts and stops. To pair again, delete gateway-state.json.

mt gateway pair --print-credentials pairs an enforcer that runs elsewhere (the Cloudflare Worker) and prints its credentials as JSON for wrangler secret bulk; the pairing code goes to stderr. To enforce in front of a real API, see the standalone mTLS proxy guide or the guide for your gateway under Gateways.

mt enroll

Usage
mt enroll --token mtenr_… [--api URL] [--key-store file|keychain] mt enroll --workload wl_… --grant gr_… --oidc-token-file FILE [--api URL]
mt enroll flags
FlagMeaning
--token mtenr_…Sandbox enrollment token from the portal (single use, 30 minutes).
--oidc-token-file FILEFile containing an OIDC workload JWT (for example a projected Kubernetes service-account token). Requires --workload and --grant.
--workload wl_…, --grant gr_…Workload and grant IDs (OIDC enrollment).
--key-store STOREWhere the private key is kept: file (default) or keychain (macOS). Also MT_KEY_STORE. See Key storage.
--timeout DHow long to wait when signing is pending (default 10m).

Generates a P-256 key, sends a CSR and proof of possession, polls while signing is pending, and writes the identity. See Enrollment.

mt renew

Usage
mt renew [--force] [--watch] [--on-renew COMMAND] [--signal SIG --pid PID | --pid-file FILE]
mt renew flags
FlagMeaning
(none)Renew once if renew_after has passed; otherwise print when it will be due (exit 0).
--forceRenew now regardless of the renewal window.
--watchRun continuously: sleep until the window opens (checking at least hourly), renew, reload, confirm, repeat. Failures back off from 5 s to 5 min with jitter.
--on-renew COMMANDRun after the new files are written, for example --on-renew "systemctl reload nginx". Also MT_ON_RENEW. Executed directly, without a shell; quotes group words.
--signal SIGSend HUP, USR1, USR2, TERM or INT to --pid PID or the PID in --pid-file FILE (Linux and macOS).
--hook-timeout DMaximum run time of the hook (default 60s).
--key-store STOREKeep the new key in this store (moves the identity between stores).

The new key, certificate, chain and identity.json are committed together: each file is staged and synced, a journal marks the commit, and the files are renamed into place, so a crash never leaves a key next to the wrong certificate. The hook receives MT_CERT_FILE, MT_CHAIN_FILE, MT_KEY_FILE (file store), MT_ROOT_FILE, MT_CERTIFICATE_ID and MT_SERIAL. Only when the hook succeeds does mt confirm the reload, signed by the new key; if the hook fails, the confirmation is held back and the next run (or the next --watch cycle) retries the hook and the confirmation. See Renewal.

Output
renewed 5c1e9a7b3d2f… → 3a7d1c9e5b2f… expires 2026-11-16T07:27:40Z; renew after 2026-11-15T23:31:02Z

mt call

Usage
mt call URL [--method GET] [--data JSON] [--header 'Name: value'] [--server-ca FILE] [--no-cert]
mt call flags
FlagDefaultMeaning
--method MGETHTTP method.
--data JSONNoneRequest body (sent with Content-Type: application/json).
--header HNoneExtra request header; repeatable.
--server-ca FILEfor localhost: DIR/gateway/server.pem if present; otherwise the system rootsCA to trust for the gateway’s server certificate.
--no-certoffDo not present the client certificate, useful to see no_client_certificate.
--timeout D30sRequest timeout.

Presents DIR/workload/chain.pem with the key from the identity's key store, and deliberately adds a spoofed x-mt-principal header so you can confirm the gateway strips it. Prints the verdict, request ID, policy revision and body.

mt status

Output
urn:mutualtrust:workload:sandbox:org_01jcqa7m3a2b7c9d4e6f8g1h3j:wl_01jcqb3p1y2z4a6b8c0d2e4f6g certificate crt_01jcqb5r0d2e4f6g8h0j2k4m6n serial 5c1e9a7b3d2f48a6b0c4e8d1f3a5b7c9 expires 2026-11-15T15:30:30Z (1310 min) renew after 2026-11-15T07:26:41Z renewals 0 key store file files /home/app/.config/mutualtrust/workload

Exits 5 when no identity is enrolled, and 1 when the certificate has expired.

mt service

Installs mt renew --watch (default) or mt gateway as a system service that starts at boot and restarts on failure, using this binary, the current --home and, when given, --api. Arguments after -- are appended to the service command.

Usage
mt service install [renew|gateway] [--user] [--name NAME] [--dry-run] [-- FLAGS] mt service uninstall [renew|gateway] [--user] [--name NAME] [--dry-run]
Examples
sudo mt service install renew -- --on-renew "systemctl reload nginx" mt service install gateway --user -- --origin http://127.0.0.1:3000 mt service install renew --dry-run # print the unit and the commands; change nothing
Service definitions
PlatformSystem-wide (root or elevated)Per user (--user)
Linux (systemd)/etc/systemd/system/mt-renew.service, enabled and started; logs: journalctl -u mt-renew~/.config/systemd/user/mt-renew.service (systemctl --user)
macOS (launchd)LaunchDaemon /Library/LaunchDaemons/io.mutualtrust.mt-renew.plist; logs in /Library/Logs/MutualTrustLaunchAgent in ~/Library/LaunchAgents (the default when not root); logs in ~/Library/Logs/MutualTrust
WindowsService mt-renew (automatic, delayed start; restarts after 10 s, 30 s, 1 min); logs in %ProgramData%\MutualTrust\logsNot available

The .deb and .rpm packages ship equivalent mt-renew and mt-enforcer units that read MT_API and MT_ON_RENEW from /etc/default/mt (or /etc/sysconfig/mt).

mt enforcer

The enforcer is built into mt: mt enforcer MODE takes the same commands and flags as the standalone mt-enforcer binary (proxy, envoy-grpc, forward-auth, lambda, run --config FILE, pair, credentials, bundle, healthcheck, version), described in the gateway guides.

Usage
mt enforcer proxy --api URL --state DIR --listen 0.0.0.0:8443 --origin http://127.0.0.1:9001 --server-cert C --server-key K mt enforcer install [--dir DIR] [--from URL] # download the standalone mt-enforcer (SHA-256 verified) mt enforcer which

The standalone mt-enforcer remains available for containers, Lambda and gateway sidecars: mt enforcer install, the mt-enforcer_* release archives, or services/enforcer/Dockerfile.

mt adapt

The integration adapter runtime is built in too: mt adapt COMMAND is mt-adapt COMMAND (list, capabilities, assess, inspect, desired, plan, render, apply, verify, rollback, observe, report, approval). See Integration adapters.

mt discover

Read-only migration discovery: does a connection depend on publicly issued client certificates? Sectigo published 10 February 2027 as the final cutoff for clientAuth in newly issued publicly trusted Sectigo SSL/TLS certificates, and inventory and renewal timing decide the effect on each deployment. Every finding is labelled verified, inferred or unknown and names its source. The workflow, delegation and the meaning of each label are in Migration discovery.

Usage
mt discover endpoint HOST[:PORT] [--sni NAME] [--http-probe] [--allow-private] [--timeout 10s] [--interval 250ms] [--out FILE] mt discover certs PATH... [--os-store] [--target HOST[:PORT]] [--cutoff 2027-02-10] [--renewal-lead 30] [--keytool FILE] [--out FILE] mt discover config FILE|DIR [--platform auto|nginx|envoy|haproxy|traefik|kong|f5|apache|k8s|java|aws|azure] [--redact-hosts] [--out FILE] mt discover report [--from FILE]... [--endpoint HOST[:PORT]]... [--certs PATH]... [--config PATH]... [--target HOST[:PORT]] [--redact-hosts] [--submit --workspace ID [--token-file FILE]] [--out FILE]
Examples
mt discover endpoint api.example.com --http-probe --out endpoint.json MT_DISCOVER_PASSWORD=... mt discover certs client.p12 keystore.jks --target api.example.com --out certs.json mt discover config /etc/nginx --out config.json mt discover report --from endpoint.json --from certs.json --from config.json MT_TASK_TOKEN=... mt discover report --from endpoint.json --from config.json --submit --workspace cws_...
mt discover subcommands
SubcommandWhat it does
endpointTLS handshakes without a client certificate: the server chain, whether the server sends a CertificateRequest and which CA names it accepts, whether it refuses a caller without a certificate, and the supported TLS versions. --http-probe adds one GET / without cookies or credentials to infer the TLS terminator from headers such as Server, Via, CF-RAY and x-amzn-*. At most 8 connections, --interval apart. Private, loopback and link-local addresses are refused unless --allow-private.
certsClient certificates in PEM, DER, PKCS#12 and JKS files (JCEKS through the JDK keytool), and with --os-store the macOS Keychain identities or the Windows CurrentUser and LocalMachine MY stores: subject, issuer, EKUs, serial, expiry, key type and size, and whether the issuer chains to a public root. A public certificate with clientAuth whose renewal falls after --cutoff is flagged public_clientauth_renewal_at_risk. Passwords come from $MT_DISCOVER_PASSWORD or a hidden prompt. Private keys are never printed or kept.
configThe adapter inspectors of mt adapt inspect, run offline on configuration files (no gateway binary runs): client-certificate mode, CA files, verification depth, authorization mapping, termination point and the gateway's documented limitations. The platform is detected from the content.
reportOne MigrationEvidenceV1 document per target from saved results (--from) or collectors run inline, with a plain-language summary (what was found, what it means, what is unknown) or --json. --submit posts the documents that describe the workspace endpoint to POST /v1/workspaces/{id}/assessments with Authorization: Bearer from $MT_TASK_TOKEN (a delegated task token), $MT_API_KEY or --token-file.

Captured text (headers, certificate fields, CA names, configuration comments) is untrusted: control and markup characters are stripped, and text that reads like instructions is withheld and reported in an untrusted_instructions finding without being printed. Secrets are always redacted; --redact-hosts also masks internal hostnames, private addresses, upstreams and file paths, and is on by default with --submit.

mt version, mt self-update

Usage
mt version [--json] mt self-update [--check] [--version V] [--force]

mt self-update downloads SHA256SUMS and SHA256SUMS.sig for the release, verifies the Ed25519 signature with the release key built into the running binary, checks the archive's SHA-256 against the signed list, and replaces the executable atomically. It refuses to touch an install owned by a package manager (/usr/bin/mt from the .deb or .rpm, Homebrew, Scoop or winget) and names the command to use instead; --force overrides.

Key storage

Key stores
StoreWhere the private key livesNotes
file (default)DIR/workload/key.pem, PKCS#8 PEMMode 0600 in a 0700 directory; on Windows a protected ACL for SYSTEM, Administrators and the owner. Any TLS server or client can read it (NGINX, Envoy, curl).
keychain (macOS)A generic-password item in the macOS Keychain (service io.mutualtrust.mt)No key file is written. Set MT_KEYCHAIN to a keychain file, for example /Library/Keychains/System.keychain for a LaunchDaemon. mt call, mt renew and the reload confirmation read the key from the keychain.

The store is recorded in identity.json, so mt renew and mt call find the key without flags. mt renew --key-store STORE moves an identity to another store with its next key.

Files

Files written by mt
Path (under the home directory)Contents
workload/key.pemWorkload private key (PKCS#8, mode 0600; file store only). Replaced on each renewal.
workload/cert.pemLeaf certificate.
workload/chain.pemLeaf followed by the issuing CA. Present this as the client chain.
workload/root.pemThe space root.
workload/identity.jsonCertificate ID, grant, principal, serial, expiry, renew_after, root fingerprint, renewal history, key store and pending reload confirmation.
gateway/gateway-state.jsonGateway ID, scoped credential, pinned policy key, gateway key pair and applied high-water mark (mode 0600).
gateway/server.pemSelf-signed server certificate for localhost (and its key, server-key.pem).

The layout is the one the JavaScript CLI uses, so either CLI reads the other's files.

JSON output

With --json, enroll, renew, call, status, discover, service --dry-run, enforcer which, version and self-update print one JSON document on stdout. Errors print {"error": {"message", "exit_code", "status", "code", "request_id"}}. mt renew --watch --json prints one document per renewal.

Example
mt status --json | jq -r .identity.renew_after

Exit codes

Exit codes
CodeMeaning
0Success, including mt renew when renewal is not due yet; for mt call, an HTTP status below 400.
1The operation failed: control-plane error (the message includes the request ID), I/O, signature or checksum mismatch, reload hook failure.
2Invalid command line.
3mt call: HTTP status 400 or above (for example a 403 denial).
4mt call and mt discover endpoint: connection or TLS handshake failed.
5No identity is enrolled in the home directory.
6mt discover endpoint: the target resolves to a private, loopback or link-local address and --allow-private was not given.

mt enforcer MODE and mt adapt return the exit codes of mt-enforcer and mt-adapt.

JavaScript CLI (optional)

A single-file JavaScript build of the CLI, mt.mjs (SHA-256), remains available for environments that already standardize on Node.js 20 or later. It covers gateway, enroll, renew, call, status and enforcer install with the same wire formats and files, and keeps its state in ~/.mt. It has no service installer, reload hooks, key stores, built-in enforcer or self-update: use the native mt unless you need it.

Run
node mt.mjs enroll --token mtenr_… --api https://api.mutualtrust.io