tunlite docs文档
A lightweight, cross-platform SSH tunnel manager. Define named tunnels once; a tiny zero-dependency daemon keeps them connected, restarts them at login, and helps set up passwordless access. Every command supports --json and stable exit codes.
轻量、跨平台的 SSH 隧道管理器。一次定义命名隧道,由一个零依赖的小守护进程保持连接、开机自启、帮你打通免密。每个命令都支持 --json 和稳定退出码。
Install安装
Prerequisite: Node ≥ 18 and the system ssh it wraps, both on your PATH. Pick one — no global npm required.前提:Node ≥ 18 和它封装的系统 ssh,都在 PATH 上。任选其一 —— 不依赖全局 npm。
npx tunlite install
curl -fsSL https://raw.githubusercontent.com/yuanyuanzijin/tunlite/master/bootstrap.sh | sh
irm https://raw.githubusercontent.com/yuanyuanzijin/tunlite/master/bootstrap.ps1 | iex
tunlite install copies the runtime to a fixed directory and writes a launcher that pins node's absolute path (switching nvm/fnm won't break it). Run interactively, it also offers to register login autostart and install the Agent skill. A short tun alias is added too (skipped with a notice if the name is already taken).tunlite install 把运行时拷到固定目录,并写一个钉死 node 绝对路径的 launcher(切换 nvm/fnm 也不受影响)。交互运行时还会问你是否注册开机自启、安装 Agent skill。也会顺带写一个短别名 tun(若已被占用则跳过并提示)。
.cmd launcher, PATH setup) works but is less battle-tested — please report rough edges.Windows 支持目前为 beta。 macOS 和 Linux 是主力、有 CI 覆盖的平台;Windows(任务计划自启、.cmd launcher、PATH 设置)能用,但验证较少 —— 遇到问题欢迎反馈。Quick start快速开始
Forwards use the standard ssh flags — -L / -R / -D, repeatable, so one tunnel can carry several (see Forwards).转发用原生 ssh 标志 —— -L / -R / -D,可重复,所以一条隧道可带多个(见转发模型)。
# reach the server's :80 on your local :8080 $ tunlite add web --to me@host -L 8080:localhost:80 # expose your local :3000 as server:9000 $ tunlite add rev --to me@host -R 9000:localhost:3000 # local SOCKS5 proxy (default :1080) $ tunlite add socks --to me@host -D 1080 $ tunlite status # aligned table of every tunnel $ tunlite logs web -f # follow logs $ tunlite doctor # why it won't connect (ssh/key/port/daemon)
Upgrading from 0.9.x? 0.10.0 leans into native ssh, so a couple of commands take a new shape — while your existing tunnel config keeps working, untouched. Forwards now speak ssh's own flags — add web --to me@host -L 8080:localhost:80 -D 1080 (repeatable; set <name> edits them in place), and the earlier add local/remote/dynamic form gives way to this. Switching a tunnel on and off is now enable / disable (it was up/down), each naming what it acts on — a name, --tag, or all; a retired or mistyped verb gently points you to the right one (tunlite up → "did you mean enable?"). Run tunlite update (or npx tunlite@latest install) and confirm with tunlite --version.从 0.9.x 升级?0.10.0 更拥抱原生 ssh,少数命令换了写法 —— 而你已有的隧道配置照常运行、纹丝不动。转发现在直接说 ssh 自己的标志 —— add web --to me@host -L 8080:localhost:80 -D 1080(可重复;用 set <名字> 就地编辑),早先的 add local/remote/dynamic 写法由此让位。开关一条隧道现在是 enable / disable(此前 up/down),每个都点明作用对象 —— 名字、--tag 或 all;打错或用了退役的词,会温和地指向正确的那个(tunlite up → "did you mean enable?")。跑 tunlite update(或 npx tunlite@latest install)并用 tunlite --version 确认。
When the target isn't passwordless yet, running tunlite enable in a terminal prompts for the password once and installs your key. Or do it explicitly: tunlite check user@server (exit 0 = already works) and tunlite setup-key user@server.目标还没免密时,在终端跑 tunlite enable 会让你输一次密码并装好公钥。也可显式执行:tunlite check user@server(退出 0 = 已通)、tunlite setup-key user@server。
Autostart (optional): tunlite install service registers the daemon to start at login — it also starts everything right away, so it replaces enable when you want tunnels up persistently.开机自启(可选):tunlite install service 把守护进程注册成登录自启 —— 它当场也会把一切启动起来,所以想让隧道持久常驻时它替代 enable,两者不用都做。
Update升级
$ tunlite update # upgrade to the latest $ tunlite update v0.9.0 # install / roll back to a tag $ tunlite update --check # compare vs latest, no change $ tunlite update --no-restart # swap files only
update upgrades to the latest release tag: it fetches that tag's tarball and re-anchors in place (no npm, no git), then restarts the daemon — so the installed version is always a real published release. It only self-updates an anchored install; from a git checkout it points you to git pull, and from an npm i -g install to npm i -g tunlite@latest.update 升级到最新的 release tag:拉那个 tag 的 tarball 就地重新锚定(不走 npm、不走 git)再重启 daemon —— 所以装上的永远是一个真实已发布的版本。它只自更新锚定安装;在 git 检出里提示你用 git pull,在 npm i -g 安装里提示你用 npm i -g tunlite@latest。
Uninstall卸载
$ tunlite uninstall # stop daemon + remove everything (confirms first) $ tunlite uninstall --force # skip the confirmation $ tunlite uninstall --purge # also remove config and logs $ tunlite uninstall service # remove autostart only
Interactive uninstall asks for confirmation and warns if any tunnels are currently up. --force (or --json for scripts/Agents) skips the prompt.交互式 uninstall 会先确认,并在有隧道正在运行时提示。--force(脚本/Agent 用 --json)可跳过确认。
How it works工作原理
Three roles, each with one job — think remote, engine, guard.三个角色,各司一职 —— 可以理解成遥控器、引擎、保安。
CLI tunlite …
The commands you type. Edits config.json, talks to the daemon, then exits.你敲的命令。改 config.json、和 daemon 通信,敲完即退。
daemon daemon run
A long-lived process. Keeps every tunnel connected, reconnects on drop, serves status & logs.一个常驻进程。把每条隧道连着、断了重连、对外提供 status 和日志。
service install service
A launchd / systemd / Task Scheduler entry. Keeps the daemon alive across boots and crashes.launchd / systemd / 任务计划里的一条注册项。开机拉起、挂了重启,保活 daemon。
you ── tunlite <cmd> (CLI) ──┬─ write config.json (add/rm/up/down)
├─ NDJSON IPC → daemon (status/logs/restart)
└─ one-shot ssh (check/setup-key)
OS service ── runs ──▶ tunlite daemon run ──spawn──▶ ssh -N (-L/-R/-D)
▲ created by `install service` │ supervise + reconnect (backoff)
└ keeps the daemon alive ▼
config.json ◀── reconcile on (re)start
config.json is the single source of truth. The OS service keeps the daemon alive; the daemon keeps every tunnel alive and reconciles against the config on every start. The daemon starts only on tunlite enable (on demand) or tunlite install service (at login) — other commands won't start it; if it's down they exit 5 and tell you how.config.json 是唯一事实来源。OS service 保活 daemon,daemon 保活每条隧道,每次启动还会与配置对账。daemon 只在 tunlite enable(按需)或 tunlite install service(开机)时启动 —— 其它命令不会启动它;它没在跑时会退出 5 并告诉你怎么启动。
Forwards转发模型
Forwards use the standard ssh flags, and they're repeatable — one tunnel can carry several over one connection. To change a tunnel's forwards, use set <name>: passing any -L/-R/-D replaces the whole forward set (set is the sole forward editor).转发用原生 ssh 标志,且可重复 —— 一条隧道可在一条连接上承载多个。要改一条隧道的转发,用 set <name>:带上任意 -L/-R/-D 就会替换整组转发(set 是唯一的转发编辑入口)。
-L [bind:]PORT:HOST:HOSTPORT— local forward: reach a remote service from your machine.-L [bind:]PORT:HOST:HOSTPORT—— 本地转发:在本地访问远端服务。-R [bind:]PORT:HOST:HOSTPORT— remote forward: expose a local service on the server.-R [bind:]PORT:HOST:HOSTPORT—— 远程转发:把本地服务暴露到服务器。-D [bind:]PORT— dynamic: a local SOCKS5 proxy.-D [bind:]PORT—— 动态:本地 SOCKS5 代理。
The optional bind: prefix is the listen address (default 127.0.0.1; use 0.0.0.0 to expose the listener to your LAN). Bracket IPv6 addresses: [::1].可选的 bind: 前缀是监听地址(默认 127.0.0.1;写 0.0.0.0 把监听暴露到局域网)。IPv6 地址要用方括号:[::1]。
The SSH port rides in the target — --to user@host:2222 (defaults to 22; must be an integer 1–65535 or it's a usage error). IPv6 literals with a port need brackets: user@[::1]:2222.SSH 端口写进目标里 —— --to user@host:2222(默认 22;必须是 1–65535 的整数,否则报用法错)。IPv6 字面量带端口要加方括号:user@[::1]:2222。
Daemon-less: run无守护进程:run
For containers and systemd entrypoints where a background daemon doesn't fit, run supervises one tunnel in the foreground (auto-reconnect, keepalive) and stays attached until stopped — no daemon, no config.json entry.容器、systemd 入口这类不方便跑后台守护进程的场景,用 run 在前台监管一条隧道(自动重连、keepalive),一直挂着直到被停止 —— 不起守护进程,也不写进 config.json。
$ tunlite run --to me@host -L 8080:localhost:80 $ tunlite run --to me@host -R 9000:localhost:3000 --name rev --json --exit-on-failure
--name labels the tunnel for status lines (defaults to the target host). --json emits NDJSON state on stdout (one JSON object per state change). --exit-on-failure exits non-zero instead of retrying — needs-auth → 4, blocked/failed → 1 — so a supervisor restarts it.--name 为该临时隧道设置标签(默认使用目标主机名)。--json 在 stdout 输出 NDJSON 状态(每次状态变化一行 JSON)。--exit-on-failure 失败即以非零码退出而非重试 —— needs-auth → 4、blocked/failed → 1 —— 方便上层 supervisor 重启。
Commands命令
| Command命令 | What it does作用 |
|---|---|
| add <name> --to user@host -L/-R/-D … | Define a tunnel (-L/-R/-D repeatable for multiple forwards).定义一条隧道(-L/-R/-D 可重复,带多个转发)。 |
| set <name> [--to …] [-L/-R/-D …] [-i key] [--jump …] | Edit a tunnel; passing -L/-R/-D replaces its whole forward set.改隧道;带 -L/-R/-D 会替换整组转发。 |
| rename <old> <new> | Rename, handing off the live connection.重命名,平滑交接活动连接。 |
| rm <name> · list [--tag T] | Delete (and stop) a tunnel · list defined tunnels.删除(并停掉)隧道 · 列出已定义隧道。 |
| run --to user@host -L/-R/-D … [--name LABEL] [--json] | Daemon-less foreground tunnel (container / systemd entrypoint).无守护进程的前台隧道(容器 / systemd 入口)。 |
| enable · disable · restart <name|--tag T|all> | Control. Name a target — a tunnel, --tag (every tunnel with that label), or all; bare is a usage error.控制。必须指明对象 —— 隧道名、--tag(带该标签的所有隧道)或 all;不带对象会报用法错误。 |
| status [name|--tag T] | Structured status (no name = aligned table; with name = detail).结构化状态(无名 = 对齐表格;有名 = 详情)。 |
| monitor [--interval s] [--tag T] | Full-screen live dashboard.全屏实时面板。 |
| logs <name> [-f] [-n N] | View / follow logs.查看 / 跟随日志。 |
| doctor [name] | One-shot health check: why it won't connect.一键体检:为什么连不上。 |
| check · setup-key <user@host> | Probe passwordless / install your public key.免密探测 / 安装你的公钥。 |
| webhook [set|on|off|events|test] | Drop alerts: view / set / toggle / pick events / test.掉线告警:查看 / 设置 / 开关 / 挑事件 / 测试。 |
| export · import <file> [--force] | Export config (no keys) · merge tunnels back.导出配置(不含密钥)· 合并导入隧道。 |
| update [version] | Update (or pin a tag) and restart the daemon.升级(或锁定标签)并重启 daemon。 |
| install [service|skill|completion] | Anchor runtime; add autostart / skill / completion.锚定运行时;加自启 / skill / 补全。 |
| uninstall [target] [--purge] [--force] | Uninstall (no target = everything; confirms first, --force skips; --purge drops config/state).卸载(不带目标 = 全部;先确认、--force 跳过;--purge 连配置/状态一起删)。 |
| daemon run|start|stop|status | Daemon lifecycle (run = foreground, for the service).daemon 生命周期(run = 前台,供服务调用)。 |
Naming convention: <purpose>-<port>, all-lowercase, hyphen-separated — e.g. tmux-prod-19999, db-staging-5432. That way status/logs read self-explanatory.命名规范:<用途>-<端口>、全小写、连字符分隔 —— 例如 tmux-prod-19999、db-staging-5432。这样 status/logs 一眼自解释。
Jump hosts跳板机
Reach a target through one or more bastions with --jump [user@]host[:port][,…] (ssh -J / ProxyJump) on add, set, check, and setup-key. It's stored per-tunnel and used for the auth probe too.用 --jump [user@]host[:port][,…](ssh -J / ProxyJump)经一个或多个堡垒机到达目标,add / set / check / setup-key 都支持。它按隧道保存,免密探测也走它。
$ tunlite add db-5432 --to me@db -L 5432:localhost:5432 --jump me@bastion $ tunlite set web-8080 --jump me@b1,me@b2 # chain multiple hops
Live monitor实时面板
tunlite monitor (alias mon) opens a top-style full-screen dashboard: daemon status and tunnel counts up top, a colour-coded tunnel table below.tunlite monitor(别名 mon)打开 top 风格的全屏面板:顶部 daemon 状态与隧道计数,下面是上色的隧道表。
↑/k↓/jselect,sstart,xstop,rrestart (y/Nconfirm).↑/k↓/j选择,s启动,x停止,r重启(y/N确认)。?help,qquit,--interval <seconds>sets the refresh rate (default 1s).?帮助,q退出,--interval <秒>调刷新间隔(默认 1s)。
Requires an interactive terminal; in scripts use tunlite status --json.需要交互式终端;脚本里用 tunlite status --json。
Exit codes & --json退出码与 --json
Add --json to any command for machine-readable output. Branch on the exit code rather than parsing prose:任意命令加 --json 得到机器可读输出。按退出码分支,而不是解析人话:
state values: idle, starting, connected, retrying, needs-auth, blocked, failed, stopped, disabled, daemon-stopped. Treat connected as success; retrying is normal during a transient outage.state 取值:idle、starting、connected、retrying、needs-auth、blocked、failed、stopped、disabled、daemon-stopped。connected 即成功;retrying 是短暂断网时的正常状态。
Webhooks告警 webhook
When a tunnel drops, the daemon POSTs an alert to a webhook you configure, formatted per channel (currently generic + WeCom wecom). The channel is detected from the URL, or set with --channel. Pure Node built-in http/https — no new dependencies.隧道掉线时,daemon 往你配的 webhook POST 一条告警,按渠道排版(目前 generic + 企业微信 wecom)。渠道从 URL 自动识别,或用 --channel 指定。纯 Node 内置 http/https,无新依赖。
$ tunlite webhook # show current config $ tunlite webhook set <url> # set + enable (channel auto) $ tunlite webhook events down,recovered # change events $ tunlite webhook off # disable (keeps the URL) $ tunlite webhook test # POST a test event
Events are edge-triggered (a reconnect storm fires once), in two scopes:事件按状态边沿触发(重连风暴只报一次),分两类:
- tunnel:
up,down,recovered,needs-auth,failed,stopped.隧道级:up、down、recovered、needs-auth、failed、stopped。 - daemon:
daemon-up,daemon-down,daemon-crash.daemon 级:daemon-up、daemon-down、daemon-crash。
webhook events accepts names, the groups tunnel/daemon, all, or none. Default: down, recovered, needs-auth, failed, daemon-crash. Payload: {scope, tunnel, host, event, state, lastError, restarts, ts, machine, version}.webhook events 接受名称、组 tunnel/daemon、all、none。默认:down, recovered, needs-auth, failed, daemon-crash。载荷:{scope, tunnel, host, event, state, lastError, restarts, ts, machine, version}。
Import / export导入导出
$ tunlite export > backup.json # dump config (no secrets) $ tunlite import backup.json # merge tunnels $ tunlite import backup.json --force # overwrite same-name
import merges tunnels only — it never changes your local settings/alerts. A missing file exits 3; a malformed file exits 1 and leaves the current config untouched.import 只合并隧道 —— 不会动你本地的 settings/告警。文件不存在退出 3;文件损坏退出 1,且不改动当前配置。
Shell completionShell 补全
$ tunlite install completion # auto-detect the shell $ tunlite install completion zsh # or name it: bash/zsh/fish $ tunlite uninstall completion # disable
Once on, it completes subcommands; for up/down/restart/status/logs/rm/rename it also completes defined tunnel names, for both tunlite and the short tun. Reopen the shell or exec zsh to take effect.开启后补全子命令;对 up/down/restart/status/logs/rm/rename 还会补全已定义的隧道名,tunlite 和短别名 tun 都能补。重开 shell 或 exec zsh 生效。
For Agents给 Agent 用
Agents are a first-class user. Every command takes --json and returns a stable exit code, so an Agent acts on results without scraping prose. The bundled ssh-tunnel skill — installed with tunlite install skill and shipped in the npm package — tells an Agent exactly how to drive tunlite: --json, branching on exit codes, and how to handle needs-auth.Agent 是一等用户。每个命令都接受 --json 并返回稳定退出码,Agent 拿到结果就能直接判断,不用解析人话。内置的 ssh-tunnel skill —— 用 tunlite install skill 安装、随 npm 包一起发布 —— 告诉 Agent 怎么驱动 tunlite:--json、按退出码分支、处理 needs-auth。
Config paths配置路径
- Config:
$XDG_CONFIG_HOME/tunlite/config.json·%APPDATA%\tunlite\config.json配置:$XDG_CONFIG_HOME/tunlite/config.json·%APPDATA%\tunlite\config.json - State/logs:
$XDG_STATE_HOME/tunlite·%LOCALAPPDATA%\tunlite状态/日志:$XDG_STATE_HOME/tunlite·%LOCALAPPDATA%\tunlite - Use
TUNLITE_HOMEto put everything under one root (handy for testing).用TUNLITE_HOME把所有东西归到一个根目录下(测试很方便)。
Versioning版本管理
Follows SemVer (vMAJOR.MINOR.PATCH). Release notes are in the CHANGELOG; the source lives on GitHub. Licensed MIT.遵循 SemVer(vMAJOR.MINOR.PATCH)。发布说明见 CHANGELOG;源码在 GitHub。MIT 许可证。