This guide describes security best practices for deploying CommandMate, especially when exposing it to external networks.
By default, CommandMate binds to 127.0.0.1 (localhost). In this mode:
- Only the local machine can access the server
- No authentication is required
- This is the recommended configuration for single-user development
When CM_BIND=0.0.0.0 is set, the server becomes accessible from external networks.
Without authentication, this exposes dangerous capabilities to anyone on the network:
| Risk | Description |
|---|---|
| File read/write/delete | Arbitrary file operations within the worktree |
| Command execution | Execute commands via Claude CLI / tmux sessions |
| Source code exposure | Read any file in the managed repositories |
| Data manipulation | Modify database, delete worktrees |
You MUST configure reverse proxy authentication before exposing CommandMate externally.
CommandMate includes a built-in token authentication system that does not require a reverse proxy. This is the simplest option for personal or small-team use.
mkcert creates locally-trusted certificates for development and LAN use.
brew install mkcert
mkcert -install
mkcert localhost 192.168.x.xReplace 192.168.x.x with your actual LAN IP address. This generates
localhost+1.pem (certificate) and localhost+1-key.pem (private key).
Install mkcert using one of the following methods:
# Option A: apt (Debian/Ubuntu, if available in your distro)
sudo apt install mkcert
# Option B: Go install
go install filippo.io/mkcert@latest
# Option C: Download binary from GitHub Releases
curl -L https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64 \
-o /usr/local/bin/mkcert
chmod +x /usr/local/bin/mkcertAfter installation, set up the local CA and generate a certificate:
mkcert -install
mkcert localhost <サーバーIP>Replace <サーバーIP> with your server's LAN IP (e.g., 192.168.1.10).
Clients must trust the mkcert root CA to avoid browser warnings.
- Find the CA file path on the server:
mkcert -CAROOT
# Example output: /root/.local/share/mkcert- Transfer
rootCA.pemto each client device:
# From the server, copy to a client
scp "$(mkcert -CAROOT)/rootCA.pem" user@client-device:/tmp/commandmate-rootCA.pem- Install the CA on the client:
# Ubuntu/Debian
sudo cp /tmp/commandmate-rootCA.pem /usr/local/share/ca-certificates/commandmate-rootCA.crt
sudo update-ca-certificates
# RHEL/CentOS/Fedora
sudo cp /tmp/commandmate-rootCA.pem /etc/pki/ca-trust/source/anchors/commandmate-rootCA.pem
sudo update-ca-trust
# For browsers that use their own trust store (Firefox, Chrome on some distros),
# import rootCA.pem via the browser's certificate settings.commandmate start --auth --cert ./localhost+1.pem --key ./localhost+1-key.pem--authenables the built-in token authentication--certand--keyspecify the TLS certificate and private key
The server will print a one-time token URL to the console on first start. Open the URL in your browser to authenticate and receive a session cookie.
Simple and effective for home/office LAN access.
- Install Nginx:
# Ubuntu/Debian
sudo apt install nginx apache2-utils
# macOS
brew install nginx- Create a password file:
sudo htpasswd -c /etc/nginx/.htpasswd your_username- Configure Nginx:
server {
listen 443 ssl;
server_name commandmate.local;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
auth_basic "CommandMate";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}- Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginxNote: The
proxy_set_header UpgradeandConnection "upgrade"directives are required for WebSocket support.
Zero-trust access control without exposing ports.
- Set up a Cloudflare Tunnel
- Configure Cloudflare Access policies
- Point the tunnel to
http://localhost:3000
Benefits:
- No open ports on your firewall
- SSO integration (Google, GitHub, etc.)
- Access logging and audit trail
Mesh VPN that creates a private network.
- Install Tailscale on your server and devices
- Access CommandMate via your Tailscale IP (e.g.,
http://100.x.y.z:3000)
Benefits:
- No configuration needed on CommandMate
- Encrypted end-to-end
- Works across NAT and firewalls
The CM_AUTH_TOKEN authentication mechanism was removed in Issue #179 because
the token was exposed in client-side JavaScript (NEXT_PUBLIC_CM_AUTH_TOKEN),
making it visible in browser DevTools and build artifacts. This rendered the
authentication ineffective (security theater).
- Remove AUTH_TOKEN from .env:
# Remove these lines from your .env file:
# CM_AUTH_TOKEN=...
# NEXT_PUBLIC_CM_AUTH_TOKEN=...
# MCBD_AUTH_TOKEN=...
# NEXT_PUBLIC_MCBD_AUTH_TOKEN=...-
If using localhost only (
CM_BIND=127.0.0.1):- No further action needed
- CommandMate is only accessible from the local machine
-
If exposing externally (
CM_BIND=0.0.0.0):- Set up one of the authentication methods described above
- Built-in token authentication (
--auth) is the simplest option for personal/LAN use - Nginx + Basic Auth is the recommended option when a reverse proxy is already in place
Note: When using
--auth, if an oldCM_AUTH_TOKEN(orNEXT_PUBLIC_CM_AUTH_TOKEN) variable is detected in the environment or.envfile, CommandMate will display a warning at startup reminding you to remove the obsolete variable. ExistingCM_AUTH_TOKENsettings otherwise have no effect on the new authentication system.
Before exposing CommandMate to external networks:
- Authentication is configured — choose one:
- Built-in token authentication (
commandmate start --auth --cert ... --key ...) - Reverse proxy authentication (Nginx/Cloudflare/Tailscale)
- Built-in token authentication (
- HTTPS is enabled — choose one:
- Built-in TLS (
--cert/--keyflags with mkcert-generated certificate) - Reverse proxy SSL termination
- Built-in TLS (
- Firewall rules are properly configured
- WebSocket upgrade headers are configured in proxy (if using reverse proxy)
- Access logs are enabled on the reverse proxy (if using reverse proxy)
-
CM_ROOT_DIRpoints only to intended repositories
# UFW (Ubuntu/Debian) - only allow HTTPS
sudo ufw allow 443/tcp
sudo ufw deny 3000/tcp # Block direct access to CommandMate
# firewalld (RHEL/CentOS)
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --remove-port=3000/tcp
sudo firewall-cmd --reloadFor additional security, run CommandMate on a separate VLAN or network segment and restrict access through firewall rules.
If you discover a security vulnerability, please report it via GitHub Security Advisories rather than a public issue.
- Deployment Guide - Production deployment instructions
- Trust and Safety - Trust and safety policies
- Production Checklist - Pre-deployment checklist
Last updated: 2026-02-21 (Issue #331: mkcert certificate generation, built-in token auth + HTTPS quick start)