Portal - 面向 localhost 的自托管中继隧道

通过自托管或公共中继公开本地服务。
无需端口转发。无需入站防火墙规则。无需手动 DNS 配置。无需账户。
为什么选择 Portal?
Portal 是一个本地隧道运行时和中继网络。它通过自托管或公共中继发布本地服务,把路由策略保留在隧道进程中,并避免依赖托管式厂商账户。
自托管,完全开源 - 用一条命令运行你自己的中继。中继采用 MIT 许可证,没有企业版层级,没有功能门槛,也不会回传遥测。你的中继,你的规则。
匿名中继网络 - 无需托管账户或中心化运营方即可连接公共中继。你可以把自托管中继和公共中继组合到一个池中,把信任拆分给你选择的多个独立运营方。
端到端租户 TLS 和 ECH - 因为中继是不可信的,Portal 会在用户端点而不是中继处终止租户 TLS。Portal 还提供 ECH,避免真实主机名以明文 SNI 暴露。
内置 MITM 检测 - Portal 会在真实流量开始后主动自探测自己的连接。它会比较两端导出的 TLS 密钥材料,并把不匹配视为疑似中继侧 TLS 终止。
多跳中继路由 - 将多个中继串联起来,使单个中继无法同时知道来源和目的地。使用
--multi-hop-depth 3可以自动选择三跳路由。无账户,无 API Key - 身份认证使用本地生成的 secp256k1 密钥对进行 SIWE 兼容签名。无需邮箱,无需注册,也没有厂商锁定。
原生 x402 支付 - Routed HTTP 路径可以在代理前要求 Sui gasless USDC x402 支付。浏览器应用可以导入
/x402/client.js,原生客户端可以直接调用/x402/prepare并发送X-PAYMENT。
对比
| Portal | ngrok | Cloudflare Tunnel | frp | |
|---|---|---|---|---|
| 公共 localhost URL | 是 | 是 | 是 | 是 |
| 可自托管 | 是 | 仅企业版 | 否 | 是 |
| 开源 | MIT | 否 | 仅客户端 | Apache 2.0 |
| 自定义域名 | 是 | 付费套餐 | 是 | 是 |
| 端到端租户 TLS | 是 | 否 | 否 | 否 |
| SNI 隐藏 (ECH) | 是 | 否 | 否 | 否 |
| MITM 自探测 | 内置 | 否 | 否 | 否 |
| 多中继故障切换 | 是 | 托管 | 内置 | 否 |
| 多跳路由 | 是 | 否 | 否 | 否 |
| 需要账户 | 否 | 是 | 是 | 否 |
| 原生 x402 支付 | 是 | 否 | 否 | 否 |
快速开始
公开本地服务
macOS / Linux:
curl -fsSL https://github.com/gosuda/portal-tunnel/releases/latest/download/install.sh | bash
portal expose 3000
Windows (PowerShell):
$ProgressPreference = 'SilentlyContinue'
irm https://github.com/gosuda/portal-tunnel/releases/latest/download/install.ps1 | iex
portal expose 3000
Portal 会立即为你的本地应用打印一个公共 HTTPS URL。更多示例:
# 自定义名称和中继
portal expose 3000 --name myapp --relays https://portal.example.com --discovery=false
# 把前端和 API 挂到同一个 URL 后面
portal expose --name myapp \
--http-route /api=http://127.0.0.1:3001 \
--http-route /=http://127.0.0.1:5173
# 在代理某个路由前要求 Sui USDC x402 支付
portal expose --name paid-app \
--http-route "/paid=http://127.0.0.1:3001 GET:0.01" \
--http-route /=http://127.0.0.1:5173 \
--x402-pay-to 0x...
# 原始 TCP 端口(Minecraft、数据库、SSH)
portal expose localhost:25565 --name minecraft --tcp
# 三跳路由,获得更高匿名性
portal expose 3000 --multi-hop-depth 3
对于付费路由,支付策略运行在隧道进程内,而不是中继上。默认使用 Sui mainnet;加上 --x402-testnet 可切换到 Sui testnet,这个选择与中继自身的支付设置无关。隧道会在同一个公共 origin 上提供 /x402/client.js 和 /x402/prepare。浏览器前端可以导入 /x402/client.js 并调用 x402Fetch();原生客户端可以直接调用 /x402/prepare,用自己的 Sui 运行时签名返回的交易,并发送签名后的 X-PAYMENT。
完整路由语法请参阅 CLI Reference,x402 helper endpoint 请参阅 API Reference。
使用 Portal Agent 持续运行隧道
当隧道需要在终端之外持续运行时,使用 portal agent run。它会作为本地 OS 服务运行,在一个 TOML 配置中保持所有隧道在线,并提供用于中继和多跳管理的 dashboard。
portal agent run --config config.toml
portal agent dashboard --config config.toml
portal agent restart
portal agent stop
# 前台模式会跳过 OS 服务安装。
portal agent run --config config.toml --foreground
配置格式请参阅 Portal Agent。
运行你自己的中继
git clone https://github.com/gosuda/portal-tunnel
cd portal-tunnel && cp .env.example .env
docker compose up
关于带 DNS 自动化(ACME)、TCP/UDP 端口范围和中继策略的公网部署,请参阅 Deployment。
端到端加密如何工作
Browser
-> Relay SNI router (只读取路由 token,转发原始字节)
-> Reverse session
-> Portal tunnel (在本地执行 TLS 握手,派生 session key)
-> Local service
- 中继接受传入连接,并只读取 TLS ClientHello 中用于 SNI 路由的信息。
- 中继通过反向 session 转发原始加密流,而不终止 TLS。
- 你这边的 Portal 隧道在本地完成 TLS 握手。Session key 在你的机器上派生。
- 对于中继托管域名,隧道会通过
/v1/sign获取证书签名,把中继仅用作 keyless signing oracle。中继签署握手摘要,但永远不会接收 session key。 - 握手完成后,中继继续转发密文,无法访问明文。
启用 ECH 时,中继也看不到真实租户主机名。它会通过从隧道身份派生出的不透明 token 进行路由,而真实 SNI 保留在 ECH 保护的 ClientHello 中。
多跳路由如何工作
Browser
-> Entry relay (只看到不透明 route hostname)
-> Middle relay (只看到 next-hop token)
-> Exit relay (只看到 reverse session token)
-> Portal tunnel
-> Local service
链中的每个中继只知道自己的直接相邻节点。没有任何单个中继掌握完整路径。租户 TLS 仍然只在你这边终止,因此链中的任何中继都不会收到租户 TLS 明文。
公共中继 Registry
Portal 官方公共中继 registry 是:
https://raw.githubusercontent.com/gosuda/portal-tunnel/main/registry.json
隧道客户端默认包含这个 registry。如果你运营公共 Portal 中继,可以提交 pull request,把你的中继 URL 添加到 registry.json。
文档
- CLI Reference
- Concepts
- Portal Agent
- Wallet and ENS
- Security Model
- Architecture
- Deployment
- Configuration Reference
贡献
- Fork 这个仓库。
- 创建功能分支(
git checkout -b feature/amazing-feature)。 - 用聚焦的测试或文档完成修改。
- 打开 pull request。
许可证
MIT License - see LICENSE.