每日文摘

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

portless 工作原理(bytebytego 风格架构图)

题图:端口号变成稳定域名

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 同样石沉大海。

数据说话(评测)

值不值得跟

对我们:现在不引入。 我们的 自建服务是「内网常驻 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 活跃
通配 DNScunnie/sslip.io(~1.1k,现同时运营 nip.io)活跃
通用反代caddyserver/caddy(~75k)· traefik/traefik(~65k)极活跃
证书FiloSottile/mkcert(~59.5k,事实标准)近乎冻结
隧道(互补)tailscale(~36k,portless 做成了 –tailscale flag)极活跃

参考阅读