Docs

Everything the mt CLI and the mTunnel edge can do: tunnels, named subdomains, project config, custom domains, organizations, and the limits that apply to your organization.

Install & update

$curl https://makarima.xyz/install.sh | sh

Installs a single static mt binary to ~/.local/bin (override with INSTALL_DIR). Linux and macOS, amd64 and arm64. mt update re-runs the installer to fetch the latest release; mt version prints the installed version.

Sign in

$mt login

Starts a WorkOS device-authorization flow: the CLI prints a URL and code, you approve it in the browser with your Google account, and tokens are stored in the CLI config. Access tokens refresh automatically while a tunnel is running. Each account belongs to an organization, which owns its tunnels and domains.

Tunnels

$mt http 3000# random subdomain
$mt http 3000 --name demo# stable subdomain
$mt http api# named tunnel from project config

mt http <port> exposes a local port on a *.makarima.xyz subdomain. Without --name you get a random name; with it, a stable one you can reconnect to. Passing a name instead of a port looks the tunnel up in the nearest mtunnel.config.json, which maps tunnel names to a port and optional upstream hostname, so a repo can check in its tunnel setup.

  • mt status [tunnel-id] — connection state and public URL
  • --hostname — local upstream host (default localhost)
  • --request-timeout — upstream request timeout (default 30s)
  • --idle-timeout — client-side idle shutdown (default 15m, 0 disables)

Config files

{
"tunnels": {
"api": { "port": 3000 },
"web": { "port": 5173, "hostname": "127.0.0.1" }
}
}

A mtunnel.config.json checked into your repo maps tunnel names to a local port and optional upstream hostname. The CLI searches from the working directory upward, so it works from any subdirectory. With the example above, mt http api exposes port 3000 as the named tunnel api — same stable subdomain every time, no flags to remember.

Credentials live separately in the CLI config at ~/.config/tunnel/config.json (or your OS config dir), written by mt login: the server URL and access/refresh tokens. Override the location with --config; never commit this file.

Custom domains

$mt domain add dev.example.com --name demo
$mt domain detail dev.example.com
$mt domain verify dev.example.com
$mt domain status dev.example.com

mt domain add registers a hostname for a named tunnel and prints the DNS records to create: a CNAME pointing at the service and a TXT record proving you control the name. mt domain detail prints those records again. Once the records exist, mt domain verify checks DNS and provisions the certificate; mt domain status shows provisioning progress. mt domain list shows your domains with tunnel, status, and last use; mt domain delete removes one.

Organizations

$mt org list
$mt org create "Acme Inc"
$mt org use org_123

Every account starts in a personal organization. Signing up with a verified work email auto-joins an existing organization that already claims that domain; free providers (Gmail, Outlook, etc.) never auto-join. mt org create makes a new organization and switches to it; mt org list shows every organization you belong to; mt org use <id> switches which one subsequent commands (tunnels, domains) act on.

Limits

LimitStandardUnrestricted
Concurrent tunnels3Unlimited
Custom domains1Unlimited
Idle timeout15 minutesNo timeout
Maximum tunnel lifetime1 hourNo timeout

Limits apply per organization. After a tunnel closes, reconnecting with the same --name restores the same subdomain.

How it works

The CLI opens a WebSocket to a Cloudflare Worker, which pins each tunnel to its own Durable Object. Public requests to your subdomain (or custom domain) hit the Worker, are routed to that Durable Object, streamed down the WebSocket to your machine, and the response streams back. Request and response bodies are not stored; the edge compresses responses for the browser.

Global flags: --server (override server URL), --config (config file path), --token (override stored auth), --log-level (debug, info, warn, error).