inlet
The field guide

Get connected. Or run the whole thing.

Start with the public service in a few commands, or bring inlet to your own server and manage it yourself.

01

Use inlet

Install the client, create a token, and give your local app a public URL. The public demo supports GitHub login and open signup.

Install the client

On macOS or Linux, install with Homebrew or curl.
Choose a method:

brew install ynui/tap/inlet
Manual download & Windows: Standalone binaries for Windows (amd64), macOS (amd64/arm64), and Linux (amd64/arm64). Extract the archive and place inlet on your PATH.
Download binary ↗

Create a token and start a tunnel

Start your app first. Replace 3000 with the port your app listens on.

inlet login
inlet http 3000

Use inlet login to link device tokens to GitHub. It opens a browser for approval, then returns to the terminal and saves an inlet token and server URL locally. Use inlet signup for an open token under the server’s per-IP limit. The client prints the public link; share it to let someone reach your app. Press Ctrl-C to stop the tunnel. Inlet does not save your GitHub access token.

Using a private inlet server? Ask its operator for an invite, then run inlet signup --server https://inlet.example.com --invite <invite-code>. The URL is saved with your token; https:// is converted to the secure WebSocket connection automatically.

Client commands

Setup and checks

inlet config showShow the saved server, token status, and inspector port (from ~/.config/inlet/config.json).
inlet config set-server https://inlet.example.comSwitch servers. Use https:// for a TLS site or http://localhost:8080 for local development.
inlet versionShow the installed client version.
inlet doctor [--port 3000]Check the configured inlet server and, optionally, your local app.

Sign in and tokens

inlet login [--server URL] [--label NAME] [--no-browser]Sign in with GitHub and create an account-linked token for this client.
inlet signup [--server URL] [--invite CODE]Create an open-signup token or claim a one-time invite code.
inlet auth <token>Save an existing client token supplied by the server operator.
inlet token listSee and manage tokens for your GitHub account (or signup IP).
inlet token label <prefix> "work laptop"Name a token so it is easier to recognize in the list. Clear with "".
inlet token revoke <prefix>Revoke a token. It cannot open new connections after revocation.

Run a tunnel

inlet http 3000Start a tunnel to a local port. Flags may appear before or after the port.
inlet http 3000 --new-urlRotate the 12-character tunnel ID and get a new public link.
inlet http 3000 --inspect 4041Choose another local inspector port. The default is 4040.
The inspector at http://127.0.0.1:4040 keeps the last 200 requests and is only available locally on loopback. Public URLs route to https://<domain>/<tunnel-id>/.... Inlet forwards HTTP only; WebSocket upgrades from your public app URL are not supported.

Troubleshooting

  • Start with inlet doctor --port 3000. It checks the configured server and your local app.
  • The public link does not load. Confirm your app is running on the port you passed to inlet http, and that the tunnel is still running. Ctrl-C stops it.
  • Connection or token errors. Run inlet config show to check the saved server and token status. A revoked token cannot open new connections; run inlet login again, or inlet auth <token> with a token from the operator.
  • The browser does not open during login. Run inlet login --no-browser and finish approval in any browser.
  • See what your app received. Open the inspector at http://127.0.0.1:4040. If that port is taken, choose another with --inspect.
  • WebSocket features fail through the public URL. Inlet forwards HTTP only; WebSocket upgrades from the public app URL are not supported.
02

Self-host inlet

Run one server behind your domain and TLS proxy. Clients and public requests share one inlet port.

1. Prepare the server

Point a DNS name such as inlet.example.com at your server. Install Docker with the Compose plugin, then clone inlet and set the public URL in its environment file.

git clone https://github.com/ynui/inlet.git
cd inlet
cp .env.example .env

Then set these values in .env:

INLET_PUBLIC_BASE=https://inlet.example.com
INLET_OPEN_SIGNUP=false
INLET_ADMIN_TOKEN=<long-random-secret>
INLET_GITHUB_CLIENT_ID=<oauth-app-client-id>
INLET_GITHUB_CLIENT_SECRET=<oauth-app-secret>

Start the server and check its logs:

docker compose up -d --build
docker compose ps
docker compose logs -f inletd

Set INLET_PUBLIC_BASE to the HTTPS address users will open. To offer GitHub login, create a GitHub OAuth App, enable Device Flow, and set both GitHub credentials.

Set INLET_OPEN_SIGNUP=true to let anyone create an account. When it is false, only invited users can sign up; existing users can still add tokens. Otherwise, keep signup invite-only. Create a strong admin secret with openssl rand -hex 32 and put its output in INLET_ADMIN_TOKEN.

Keep the database. Compose stores it in ./data/inlet.db by default; set INLET_DB to a Postgres or MySQL URL to use an external database instead. Back up wherever the data lives — it contains token and invite records. Keep .env private and out of Git.

Prefer not to use Docker? Download the inletd archive from Releases, or build from source: install Go 1.23+, clone the repository, run make build, then start ./inletd. Put server settings in .env or export the matching INLET_* variables before starting it.

Server environment variables

Configuration precedence: startup defaults → ~/.config/inlet/server.json → CLI flags → environment variables. Remote setting changes made with inlet admin persist in the database and take precedence thereafter.

INLET_PUBLIC_BASEPublic base URL advertised to clients (e.g. https://inlet.example.com).
INLET_OPEN_SIGNUPAllow token creation without an invite (true or false, default false).
INLET_ADMIN_TOKENSecret to bootstrap remote operator administration; stored hashed in the database.
INLET_BINDPublic HTTP listener address (default :8080).
INLET_CONTROL_ADDROptional separate listener for control and signup (default: shares INLET_BIND).
INLET_GITHUB_CLIENT_IDGitHub OAuth App client ID with Device Flow enabled.
INLET_GITHUB_CLIENT_SECRETGitHub OAuth App client secret for token verification.
INLET_ACCESS_LOGLog request method, tunnel ID, status, and duration (default false).
INLET_DBDatabase DSN/path (default ./data/inlet.db). SQLite path, or sqlite://, postgres:///postgresql://, or mysql:// URL.
INLET_STALE_AFTERDelete inactive invite/admin tokens after this period (default 720h / 30d).
INLET_OPEN_STALE_AFTERDelete inactive open-signup tokens after this period (default 168h / 7d).
INLET_REVOKED_RETENTIONKeep revoked tokens before purging (default 168h / 7d).
INLET_INVITE_TTLDefault lifetime for new invites (default 24h).

2. Put HTTPS in front

Terminate TLS at Nginx and forward every path to inlet's listener. Add the map once inside Nginx's http block; put the server block in your site config. Replace the hostname, certificate paths, and upstream for your deployment.

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    listen [::]:80;
    server_name inlet.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name inlet.example.com;

    ssl_certificate /etc/letsencrypt/live/inlet.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/inlet.example.com/privkey.pem;

    # nginx defaults to 1m; raise it to your largest expected upload.
    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        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;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Keep long-lived client connections open and stream bodies unbuffered.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
        proxy_request_buffering off;
    }
}
If Nginx is on the host, set its upstream to the published inlet port and restrict direct access to that port with your firewall. If Nginx shares inlet's Compose network, use proxy_pass http://inletd:8080; and remove the host ports mapping. Inlet needs WebSocket upgrade headers for its client control connection.

3. Invite clients and administer the server

With INLET_ADMIN_TOKEN set and the server running, connect from your workstation with inlet admin.

inlet admin auth --server https://inlet.example.com <admin-token>
inlet admin invite create --ttl 24h --label teammate
inlet admin token list --limit 50 --offset 0

Send the one-time invite code to the client. They claim it with inlet signup --server https://inlet.example.com --invite <invite-code>; the client saves the token and server URL.

Remote administration uses HTTPS and a separate admin token. The bootstrap token appears as admin-token in token lists. Never share it with regular clients.

Admin command reference

Run from your workstation with inlet admin. Credentials are saved separately in ~/.config/inlet/admin.json and never touch client configs. Remote commands require HTTPS (or loopback in development).

Authentication and configuration

inlet admin auth --server https://inlet.example.com <admin-token>Save an operator admin token and target server URL.
inlet admin config showShow the configured server, admin token status, and remote open-signup status.
inlet admin config set-server https://inlet.example.comChange the remote server used by the admin CLI.
inlet admin config set open-signup <true|false>Enable or disable open signup remotely. Setting is persisted in the database.
inlet admin versionShow the installed admin CLI version.

Invites

inlet admin invite create [--ttl 24h] [--label NAME]Create a single-use invite code. The code is shown once.

Tokens

inlet admin token list [--limit 50] [--offset 0]View token metadata in pages. Maximum page size is 200.
inlet admin token create [--label NAME]Create a regular client token directly. Its secret is shown only once.
inlet admin token create [--label NAME] --adminProvision another remote administrator token.
inlet admin token label <prefix> "NAME"Rename a token shown by token list. Clear with "".
inlet admin token revoke <prefix> [<prefix> ...]Revoke one or more token prefixes in a single transaction (up to 100).

Local server commands

Run these inside the server container or from the working directory containing its database (default ./data/inlet.db). With Docker Compose, prefix each command with docker compose exec inletd.

inletd doctorCheck the running server health endpoint (/_inlet/healthz).
inletd invite [create] [--ttl 24h] [--label NAME]Create a one-time invite code for a client.
inletd token create [--label NAME]Issue an active client token straight to the database; printed once.
inletd token listList token prefixes, labels, tunnel IDs, last used time, and state.
inletd token revoke <prefix>Revoke a token directly in the server database.

Operate and troubleshoot

docker compose ps
docker compose logs -f inletd
docker compose exec inletd inletd doctor
docker compose exec inletd inletd invite create --ttl 24h --label teammate
docker compose exec inletd inletd token list
docker compose down

To update, pull the latest source and run docker compose up -d --build. For configuration options and the full proxy example, see the deployment guide on GitHub.