每日文摘
深挖 · portless:Vercel 用一张文件路由表,让本地端口号消失


portless 是 Vercel Labs 今年 2 月发布的本地开发工具,一句话:portless myapp next dev,然后浏览器打开 https://myapp.localhost——端口号消失了。6 个多月 11.8k star,自封 “For humans and agents"。这次我把源码 clone 下来读到函数级,画了上面这张架构图,并从自托管视角评估它对我们有没有用。
是什么
它解决的是「端口地狱」:本地开发时 localhost:3000、5173、8000……端口随机漂移、没人记得住;更糟的是 AI agent 起 dev server 时总要猜端口,猜错就白测。portless 把这一切换成稳定的命名 URL——人读得懂,agent 不用猜,进程退出名字自动回收(SKILL.md、HN 讨论)。
日常体验就是一条命令:
portless myapp next dev # → https://myapp.localhost
portless api.myapp pnpm start # 子域名
portless # monorepo:每个包一个 <package>.<project>.localhost
细节很贴心:git worktree 的分支名自动做子域前缀(fix-ui.myapp.localhost),多工作区并行开发互不串台。项目 2026-02-18 发 0.1.0,最新 0.15.6(2026-08-24),README 明确自己是 pre-1.0。
核心机制(技术)
上图就是全貌,几个值得展开的点:
文件即协议。整个系统没有 IPC socket、没有 HTTP 控制面:CLI 把路由写进 ~/.portless/routes.json(目录式文件锁保护,原子性靠 mkdir),daemon 用 fs.watch + 100ms 防抖热加载进内存缓存。锁、watch、marker 文件就是全部协议——天然多 CLI 并发安全,出问题 cat 一下文件就能调试。这是我在源码里看到的最优雅的取舍(routes.ts:7-16,222-251)。
TLS 是重头戏。首次运行用 openssl CLI 自签一把 EC CA(10 年)装进系统信任存储;之后每个新子域在 TLS 握手时由 SNI 回调现场签一张证书、落盘缓存。为什么不能一张通配符搞定?RFC 2606 规定 .localhost 处于 public-suffix 边界、不允许 *.localhost 通配证书,只能逐域签(certs.ts:791-887)。HTTPS 的好处是默认 HTTP/2——绕开了浏览器对 HTTP/1.1 每 host 6 连接的上限,Vite/Nuxt 这类多文件 dev server 反而更快。
单端口分流 + WebSocket 桥接。443 上只有一个 net.Server,偷看首字节:0x16(TLS ClientHello)进 HTTP/2 server,否则 302 跳 HTTPS。HMR 的 WebSocket 在 HTTP/2 下走 RFC 8441 extended CONNECT,portless 自己合成 Sec-WebSocket-Key、校验后端 Accept 哈希,把 h2 流桥接回对 dev server 的 HTTP/1.1 Upgrade——注释可追溯到 RFC 条款(proxy.ts:562-708)。环路检测也有:自增 x-portless-hops 头,超 5 跳返回 508 并提示「Vite 代理请加 changeOrigin」。
第五层:进程编排。这是它和所有竞品拉开差距的地方——不只是反代,还 spawn 子进程:从 4000-4999 挑随机空闲端口(排除 WHATWG 规范里浏览器禁用的 80+ 个端口),注入 PORT/HOST/NODE_EXTRA_CA_CERTS 等 env;Vite/Astro/Angular/Expo 这些不读 PORT 的框架自动补 --port/--host(能穿透 pnpm exec 间接层,且遇到 &&、管道等复杂脚本会安全跳过)。僵尸进程有三层防线:进程组整树击杀、路由表按 PID 探活过滤、portless prune 用 lsof 收尸。
工程质量配得上 Vercel 门面:零运行时依赖(YAML 解析和 glob 都是手写的),strict TS,测试代码约 9.6k 行对源码 17k 行,e2e 覆盖 12 个框架 fixture 加 zombie/parallel 场景(Zenn 的独立源码解读可交叉印证)。
怎么跑起来(环境)
npm i -g portless,要求 Node 24+,macOS/Linux/Windows 都支持。macOS 上的真实摩擦是三次提权:绑 443 要 sudo(可用 portless proxy start -p 1355 高位端口规避)、自动写 /etc/hosts 要 root、CA 信任弹一次 GUI 授权。portless service install 能把 daemon 装成开机自启服务(launchd/systemd/Task Scheduler,官方文档)。
安全面要如实说:默认只绑 127.0.0.1/::1 不出局域网(0.15.4 才收紧的行为);CA 私钥 ~/.portless/ca-key.pem 权限 0600,但任何能读它的进程都可以给任意域名签发本机信任的证书——这是 mkcert 同款模型的原生风险。更值得留意的是安全响应面:GitHub 0 条安全公告,但有两个社区提交、自评 high 的安全修复 PR(#308 hosts 注入、#342 clean 任意删除)至今无维护者回应,还有一个安全披露 issue #248 同样石沉大海。
数据说话(评测)
- 增长:首周 2.2k → 两月 7k → 7 月破万 → 9 月初 11.8k(Wayback 快照链),曲线已平台化。
- 舆论:有趣的是,portless 自己在 HN 的帖子全部 0 评论沉底;真实讨论发生在竞品帖《I Got Sick of Remembering Port Numbers》(144 分/156 评论)里——「I use it for all my apps now」的好评和最硬的批评都在那里。
- 最硬的批评是两个:OAuth(
.localhost被 Google/Apple 严格 OAuth 拒收,官方用--tld dev.example.com真实域名部分补救,但这是文档化的天生限制)和 Windows 体验(issue 聚簇)。品类级嘲讽也有:「这就是 /etc/services with extra steps」(415 赞 top comment)、「我不爱需要 root 的 dev 工具」。 - 弃坑疑虑:Vercel Labs 的实验项目口碑在 HN 一直是争议话题,portless 恰好被双方同时引用——既是「没被弃坑」的反例(持续发版到 8 月底),又被增长放缓拿来当信号。
- 一个调研插曲:一条信源称「最新版停在 7 月、7 周无发版」,我用 npm registry 和 git tag 双核实为误报——latest 就是 0.15.6(8-24),最新 commit 8-29。有争议的数据,动手验证比转发重要。
- 性能方面没有任何实测基准(Zenn 全文无 benchmark),「多一层代理会不会慢」目前只有架构推断:HTTP/2 的收益可能对冲掉代理开销。
值不值得跟
对我们:现在不引入。 我们的 自建服务是「内网常驻 Docker 服务 + frp/ECS 门面」拓扑,服务是常驻的、域名在 caddy 层解决、有 sun-panel 做导航——portless 针对的痛点(本地 dev server 多实例、端口漂移、HMR/OAuth 调试)在我们的拓扑里不存在。它是 dev-time 工具,不是 service-time 工具。
什么时候重新评估:某台机器上出现「多个本地 dev server 并行开发 + agent 频繁起 dev server」的场景(比如带前端界面的本地 AI 项目多开)。届时两个已知坑都有解法:--tld test 避 OAuth、-p 1355 避 sudo。
真正值得带走的是设计:文件即协议的 daemon 架构(比大多数 IPC 方案好调试一个量级)、SNI 按需签证书加缓存、hosts-sync 端点的 HMAC challenge 双向认证(防 DNS rebinding)、以及 SKILL.md + llms.txt 的 agent 分发方式——最后一项和 agent 时代工具链的方向同频。
同类 repo 关注清单(按层):
| 层 | repo | 状态 |
|---|---|---|
| 同定位直接竞品 | vibber-ai/portmap(~26★,主打不破坏 OAuth)· steveclarke/outport(确定性端口+.env 注入)· Akryum/portless(~130★,同名先行者) | 都还很小 |
| 本地域名反代 | peterldowns/localias(~1.5k,Caddy 内嵌)· basecamp/pow(2011 鼻祖,已归档) | localias 活跃 |
| 通配 DNS | cunnie/sslip.io(~1.1k,现同时运营 nip.io) | 活跃 |
| 通用反代 | caddyserver/caddy(~75k)· traefik/traefik(~65k) | 极活跃 |
| 证书 | FiloSottile/mkcert(~59.5k,事实标准) | 近乎冻结 |
| 隧道(互补) | tailscale(~36k,portless 做成了 –tailscale flag) | 极活跃 |
参考阅读
- vercel-labs/portless README — 官方真源,平台差异与 TLD 建议都在
- HN: I Got Sick of Remembering Port Numbers — 品类集中讨论帖,正反声音都有
- Stabilizing Local Development URLs with a Reverse Proxy (Zenn) — 独立源码级解读
- Replace Port Numbers with Named URLs (OpenReplay) — portless vs DIY 三方案对照
- portless.sh/commands — service/doctor/clean 官方文档
- Stop Juggling Port Numbers (Grizzly Peak) — 早期第三方评测
- nip.io — 通配 DNS 路线的历史与传承(xip.io→nip.io→sslip.io)