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

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(若已被占用则跳过并提示)。

Windows support is beta. macOS and Linux are the primary, CI-tested platforms; Windows (Task Scheduler autostart, .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快速开始

One forward per add (a tunnel can carry several — see Forwards). --local is your machine's side, --remote is the server's side; the subcommand decides who listens.每个 add 一个转发(一条隧道可带多个,见转发模型)。--local 是你机器侧,--remote 是服务器侧;子命令决定谁监听。

# reach the server's :80 on your local :8080
$ tunlite add local   web-8080 --to user@server --remote 80 --local 8080
# local SOCKS5 proxy (default :1080)
$ tunlite add dynamic px-1080  --to user@server
# expose your local :3000 as server:9000
$ tunlite add remote  rev-9000 --to user@server --local 3000 --remote 9000

$ tunlite up                 # enable + start all, bring up the daemon
$ tunlite status             # aligned table of every tunnel
$ tunlite logs web-8080 -f   # follow logs
$ tunlite doctor             # why it won't connect (ssh/key/port/daemon)

When the target isn't passwordless yet, running tunlite up 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 up 会让你输一次密码并装好公钥。也可显式执行: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 up when you want tunnels up persistently.开机自启(可选):tunlite install service 把守护进程注册成登录自启 —— 它当场也会把一切启动起来,所以想让隧道持久常驻时它替代 up,两者不用都做。

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.三个角色,各司一职 —— 可以理解成遥控器引擎保安

the remote遥控器

CLI tunlite …

The commands you type. Edits config.json, talks to the daemon, then exits.你敲的命令。改 config.json、和 daemon 通信,敲完即退。

the engine引擎

daemon daemon run

A long-lived process. Keeps every tunnel connected, reconnects on drop, serves status & logs.一个常驻进程。把每条隧道连着、断了重连、对外提供 status 和日志。

the guard保安

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 up (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 up(按需)或 tunlite install service(开机)时启动 —— 其它命令不会启动它;它没在跑时会退出 5 并告诉你怎么启动。

Forwards转发模型

Each add defines one forward; a tunnel can carry several over one connection — manage them with tunlite forward list|add|rm <tunnel>.每个 add 定义一个转发;一条隧道可在一条连接上承载多个 —— 用 tunlite forward list|add|rm <tunnel> 管理。

  • local — reach a remote service from your machine (--remote required).local —— 在本地够到远端服务(--remote 必填)。
  • remote — expose a local service on the server (--local required).remote —— 把本地服务暴露到服务器(--local 必填)。
  • dynamic — a local SOCKS5 proxy (default port 1080).dynamic —— 本地 SOCKS5 代理(默认端口 1080)。

--local always means your machine's side and --remote the server's side; the subcommand decides who listens. Addresses are [host:]port: the listening side's host is the bind address (default 127.0.0.1, use 0.0.0.0 to expose); the target side's host is the host to connect to (default localhost).--local 永远指你机器侧、--remote 永远指服务器侧;子命令决定谁监听。地址写 [host:]port:监听那侧的 host 是绑定地址(默认 127.0.0.1,写 0.0.0.0 对外),目标那侧的 host 是要连的主机(默认 localhost)。

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

Commands命令

Command命令What it does作用
add local|remote|dynamic <name> --to user@hostDefine a tunnel (one forward; add more with forward).定义一条隧道(先一个转发;用 forward 加更多)。
set <name> [--to …] [-i key] [--jump …]Change a tunnel's host / key / jump / options / tags.改隧道的 host / 密钥 / jump / 选项 / 标签。
rename <old> <new>Rename, handing off the live connection.重命名,平滑交接活动连接。
rm <name> · list [--tag T]Delete (and stop) a tunnel · list defined tunnels.删除(并停掉)隧道 · 列出已定义隧道。
forward list|add|rm <tunnel>Manage a tunnel's forwards (it can carry several).管理某隧道的转发(可带多个)。
up · down · restart [name|--tag T]Control (no name/tag = all; --tag = every tunnel with that label).控制(不带名字/标签 = 全部;--tag = 带该标签的所有隧道)。
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|statusDaemon 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-19999db-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 local db-5432 --to user@db --remote 5432 --jump user@bastion
$ tunlite set web-8080 --jump user@b1,user@b2   # chain multiple hops

Tags标签

Label tunnels with --tag (repeatable) on add, edit with set --tag (replaces the set) or set --no-tags (clears). Then act on a whole group: up/down/restart/status/list/monitor all accept --tag <label> (multiple --tag = union). A name and --tag are mutually exclusive. Tags are metadata only — they never change the ssh command.--tag(可重复)在 add 时贴标签,用 set --tag(整组替换)或 set --no-tags(清空)修改。之后对一整组批量操作:up/down/restart/status/list/monitor 都支持 --tag <标签>(多个 --tag 取并集)。名字与 --tag 不能同时给。标签只是元数据,不改变 ssh 命令。

$ tunlite add remote api-9001 --to me@host --remote 9001 --tag prod
$ tunlite up --tag prod

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 ↓/j select, s start, x stop, r restart (y/N confirm).↑/k ↓/j 选择,s 启动,x 停止,r 重启(y/N 确认)。
  • ? help, q quit, --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 得到机器可读输出。按退出码分支,而不是解析人话:

0 ok成功 2 usage error用法错 3 not found找不到 4 needs auth需打通 5 daemon unreachable连不上 daemon 1 other error其它错误

state values: idle, starting, connected, retrying, needs-auth, failed, stopped, disabled, daemon-stopped. Treat connected as success; retrying is normal during a transient outage.state 取值:idlestartingconnectedretryingneeds-authfailedstoppeddisableddaemon-stoppedconnected 即成功;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.隧道级updownrecoveredneeds-authfailedstopped
  • daemon: daemon-up, daemon-down, daemon-crash.daemon 级daemon-updaemon-downdaemon-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/daemonallnone。默认: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

Read the agent skill (SKILL.md) →阅读 agent skill(SKILL.md)→

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_HOME to 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 许可证。