Skip to content

Commit 244f09f

Browse files
committed
feat: add IVNP relay overlay support
- Introduced RelayDescriptor field for IVNP destination in identity.go. - Added new relay overlay paths in paths.go. - Created documentation for IVNP Relay Overlay detailing configuration and usage. - Implemented tests for overlay admission and refresh logic. - Developed IVNP overlay handling for Unix systems, with fallback for unsupported platforms. - Established HTTP server for handling relay connections and leases. - Added relay binding management to ensure secure connections between local and remote leases. - Implemented tests for relay binding and overlay connection handling.
1 parent 9a43644 commit 244f09f

43 files changed

Lines changed: 1379 additions & 161 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828
# ─────────────────────────────────────────────────────────────────────────────
2929

3030
# Public HTTPS origin browsers and tunnel clients use. Must be publicly
31-
# resolvable when DISCOVERY=true; localhost and other local-only names are
31+
# resolvable when DISCOVERY=true
32+
33+
# Optional IVNP router config (Linux/macOS). Empty keeps relay traffic direct.
34+
# The destination key is stored beside this config as <path>.destination.
35+
IVNP_CONFIG=; localhost and other local-only names are
3236
# rejected by public discovery.
3337
PORTAL_URL=https://localhost
3438

@@ -63,6 +67,10 @@ MAX_PORT=0
6367
# reachable PORTAL_URL.
6468
DISCOVERY=true
6569

70+
# Optional IVNP router config (Linux/macOS). Empty keeps relay traffic direct.
71+
# The destination key is stored beside this config as <path>.destination.
72+
IVNP_CONFIG=
73+
6674
# Comma-separated relay API URLs to seed discovery from.
6775
BOOTSTRAPS=
6876

README.md

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ keeps routing and x402 payment policy in the tunnel process, and avoids requirin
3737
after real traffic begins. It compares TLS keying material exported on both
3838
sides and treats a mismatch as suspected relay-side TLS termination.
3939

40-
- **Multi-Hop Relay Routing** - Chain multiple relays together so no single
41-
relay knows both the origin and the destination. Use `--multi-hop-depth 3` to
42-
select a three-hop route automatically.
43-
4440
- **No Accounts, No API Keys** - Authentication uses SIWE-compatible signing
4541
with a locally generated secp256k1 key pair. No email, no registration, no
4642
vendor lock-in.
@@ -62,7 +58,7 @@ keeps routing and x402 payment policy in the tunnel process, and avoids requirin
6258
| SNI hiding (ECH) | **Yes** | No | No | No |
6359
| MITM self-probe | **Built-in** | No | No | No |
6460
| Multi-relay failover | **Yes** | Managed | Built-in | No |
65-
| Multi-hop routing | **Yes** | No | No | No |
61+
| Optional relay overlay | **IVNP** | No | No | No |
6662
| Account required | **No** | Yes | Yes | No |
6763
| Native x402 payments | **Yes** | No | No | No |
6864

@@ -127,8 +123,6 @@ portal expose --name paid-app \
127123
# Raw TCP port (Minecraft, databases, SSH)
128124
portal expose localhost:25565 --name minecraft --tcp
129125

130-
# Three-hop route for maximum anonymity
131-
portal expose 3000 --multi-hop-depth 3
132126
```
133127

134128
See [CLI Reference](cmd/portal-tunnel/README.md) for the full route syntax and
@@ -139,7 +133,7 @@ helper endpoints.
139133

140134
Use Portal Agent directly when tunnels should keep running outside your terminal.
141135
It runs as a local OS service, keeps every tunnel in one TOML config alive, and
142-
provides a dashboard for relay and multi-hop management.
136+
provides a dashboard for public relay management.
143137

144138
```bash
145139
portal agent run --config config.toml
@@ -190,20 +184,13 @@ When ECH is enabled, the relay also cannot see the actual tenant hostname. It
190184
routes by an opaque token derived from the tunnel identity, while the real SNI
191185
stays inside the ECH-protected ClientHello.
192186

193-
## How Multi-Hop Routing Works
194-
195-
```text
196-
Browser
197-
-> Entry relay (sees only the opaque route hostname)
198-
-> Middle relay (sees only the next-hop token)
199-
-> Exit relay (sees only the reverse session token)
200-
-> Portal tunnel
201-
-> Local service
202-
```
187+
## Optional Relay Overlay
203188

204-
Each relay in the chain knows only its immediate neighbors. No single relay
205-
holds the full path. Tenant TLS still terminates only on your side, so no relay
206-
in the chain receives tenant TLS plaintext.
189+
Relay operators can enable IVNP for authenticated relay exchanges. I2P owns
190+
internal routing; Portal owns relay admission and lease authorization. The
191+
standard tunnel client keeps its direct reverse backhaul. See the
192+
[overlay guide](docs/src/routes/ivnp-overlay/+page.md) for configuration and the
193+
lease binding API.
207194

208195
## Public Relay Registry
209196

README.zh-CN.md

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ Portal 是一个本地隧道运行时和中继网络。它通过自托管或公
2727

2828
- **内置 MITM 检测** - Portal 会在真实流量开始后主动自探测自己的连接。它会比较两端导出的 TLS 密钥材料,并把不匹配视为疑似中继侧 TLS 终止。
2929

30-
- **多跳中继路由** - 将多个中继串联起来,使单个中继无法同时知道来源和目的地。使用 `--multi-hop-depth 3` 可以自动选择三跳路由。
3130

3231
- **无账户,无 API Key** - 身份认证使用本地生成的 secp256k1 密钥对进行 SIWE 兼容签名。无需邮箱,无需注册,也没有厂商锁定。
3332

@@ -88,8 +87,6 @@ portal expose --name paid-app \
8887
# 原始 TCP 端口(Minecraft、数据库、SSH)
8988
portal expose localhost:25565 --name minecraft --tcp
9089

91-
# 三跳路由,获得更高匿名性
92-
portal expose 3000 --multi-hop-depth 3
9390
```
9491

9592
对于付费路由,支付策略运行在隧道进程内,而不是中继上。默认使用 Sui mainnet;加上 `--x402-testnet` 可切换到 Sui testnet,这个选择与中继自身的支付设置无关。隧道会在同一个公共 origin 上提供 `/x402/client.js``/x402/prepare`。浏览器前端可以导入 `/x402/client.js` 并调用 `x402Fetch()`;原生客户端可以直接调用 `/x402/prepare`,用自己的 Sui 运行时签名返回的交易,并发送签名后的 `X-PAYMENT`
@@ -152,18 +149,9 @@ Browser
152149

153150
启用 ECH 时,中继也看不到真实租户主机名。它会通过从隧道身份派生出的不透明 token 进行路由,而真实 SNI 保留在 ECH 保护的 ClientHello 中。
154151

155-
## 多跳路由如何工作
152+
## 可选 IVNP 中继覆盖网络
156153

157-
```text
158-
Browser
159-
-> Entry relay (只看到不透明 route hostname)
160-
-> Middle relay (只看到 next-hop token)
161-
-> Exit relay (只看到 reverse session token)
162-
-> Portal tunnel
163-
-> Local service
164-
```
165-
166-
链中的每个中继只知道自己的直接相邻节点。没有任何单个中继掌握完整路径。租户 TLS 仍然只在你这边终止,因此链中的任何中继都不会收到租户 TLS 明文。
154+
IVNP/I2P 负责中继之间的连接和内部多跳路由,Portal 负责公开中继选择与租约授权。默认隧道客户端仍使用直接反向连接。参见 [IVNP 指南](docs/src/routes/ivnp-overlay/+page.md)
167155

168156
## 公共中继 Registry
169157

cmd/portal-tunnel/README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,7 @@ Common `portal expose` flags:
149149
--name Public hostname prefix; auto-generated when omitted
150150
--relays Additional relay API URLs, comma-separated
151151
--discovery Include registry relays and relay discovery expansion
152-
--max-active-relays Maximum auto-selected single-hop relays; multi-hop uses every eligible relay as an entry
153-
--multi-hop Ordered multi-hop relay API URLs, comma-separated
154-
--multi-hop-depth Automatically create this-depth multi-hop routes for every eligible entry relay
152+
--max-active-relays Maximum auto-selected public relays
155153
--ban-mitm Ban relay when the MITM self-probe detects termination
156154
--ech Enable ECH hostname privacy for TLS stream tunnels (disabled by default)
157155
--identity-path Identity JSON file path; created automatically when missing
@@ -186,7 +184,7 @@ portal agent stop
186184
portal agent restart
187185
```
188186

189-
The dashboard can edit basic tunnel settings, relays, and multi-hop routes. Add
187+
The dashboard can edit basic tunnel settings, relays, and public relay selection. Add
190188
Tunnel opens a small form for name, target or HTTP routes, x402 payment settings,
191189
relays, discovery, and max active relays. After creation, routed HTTP paths,
192190
route-level x402 amounts, payment network, and discovery mode are read-only in
@@ -206,8 +204,6 @@ restart the agent or tunnel to change them.
206204
`--x402-pay-to`. Sui is the default; Casper additionally requires
207205
`--x402-network casper:...` and the wCSPR contract in `--x402-asset`.
208206
The default CSPR.cloud facilitator also requires `CSPR_CLOUD_API_KEY`.
209-
- `--multi-hop` cannot be combined with `--multi-hop-depth`.
210-
- Multi-hop currently supports only the default SNI TLS stream transport.
211207
- `--tcp` and `--udp` require matching relay transport support.
212208

213209
## More Docs

cmd/relay-server/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func main() {
3434
}
3535

3636
type relayServerConfig struct {
37+
IVNPConfig string
3738
PortalURL string
3839
FrontendDir string
3940
IdentityPath string
@@ -99,6 +100,7 @@ func resolveRelayServerConfig(args []string) (relayServerConfig, error) {
99100
func registerRelayServerFlags(fs *flag.FlagSet, cfg *relayServerConfig) {
100101
utils.StringFlagEnv(fs, &cfg.PortalURL, "portal-url", "https://localhost", "portal base URL", "PORTAL_URL")
101102
utils.StringFlagEnv(fs, &cfg.FrontendDir, "frontend-dir", "", "custom SPA directory containing index.html; embedded frontend is used when empty", "PORTAL_FRONTEND_DIR")
103+
utils.StringFlagEnv(fs, &cfg.IVNPConfig, "ivnp-config", "", "IVNP router config path; enables the optional relay overlay (Linux/macOS, requires DISCOVERY)", "IVNP_CONFIG")
102104
utils.StringFlagEnv(fs, &cfg.IdentityPath, "identity-path", "./.portal-certs", "directory path for relay identity, policy state, and keyless materials", "IDENTITY_PATH")
103105
utils.StringFlagEnv(fs, &cfg.Bootstraps, "bootstraps", "", "bootstrap relay API URLs; merged with bootstrap relays when discovery is enabled", "BOOTSTRAPS")
104106
utils.BoolFlagEnv(fs, &cfg.DiscoveryEnabled, "discovery", false, "serve relay discovery endpoints and poll discovery peers", "DISCOVERY")
@@ -173,6 +175,7 @@ func runServeCommand(args []string) error {
173175
func runServer(ctx context.Context, cfg relayServerConfig) error {
174176
server, err := portal.NewServer(portal.ServerConfig{
175177
PortalURL: cfg.PortalURL,
178+
IVNPConfig: cfg.IVNPConfig,
176179
IdentityPath: cfg.IdentityPath,
177180
Bootstraps: utils.SplitCSV(cfg.Bootstraps),
178181
DiscoveryEnabled: cfg.DiscoveryEnabled,

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ services:
2525
PORTAL_FRONTEND_DIR: ${PORTAL_FRONTEND_DIR:-}
2626
BOOTSTRAPS: ${BOOTSTRAPS:-}
2727
DISCOVERY: ${DISCOVERY:-false}
28+
IVNP_CONFIG: ${IVNP_CONFIG:-}
2829
IDENTITY_PATH: ${IDENTITY_PATH:-/portal-certs}
2930

3031
API_PORT: 4017

docs/adr/0001-relay-overlay.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@ No compatibility implementation of Portal hop routing is retained.
2222
Overlay reachability does not establish Portal admission or public ingress
2323
health. Overlay latency must not become the public relay performance score.
2424
NATed intermediate capacity belongs to IVNP, not a Portal worker catalog.
25+
26+
The first overlay surface is the lease binding API, not a new SDK path planner.
27+
It links two already authorized leases and does not manage their renewal or TLS
28+
configuration. This is deliberately an API integration surface; ordinary CLI
29+
exposures keep their direct backhaul. Future convenience APIs must preserve the
30+
single destination boundary rather than recreate intermediate route state.
31+
32+
I2P's authenticated stream endpoints plus Portal's signed destination binding
33+
establish relay identity. Catalog admission is separate from public health.
34+
Live NAT participation and the narrower Portal-only transit restrictions from
35+
#358 are deployment/upstream questions, not guarantees of this implementation.

docs/architecture.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ Relay selection returns public relay priorities. Portal does not construct an
88
ordered list of intermediate relays. Explicit relay URLs, transport eligibility,
99
admission, expiry, health, and load remain Portal responsibilities.
1010

11+
The optional `portal/overlay` runtime embeds IVNP on Linux/macOS. It owns the
12+
destination, bounded authenticated exchanges, and stream shutdown. Catalog
13+
admission remains in `portal/discovery`; incoming I2P reachability cannot promote
14+
a public relay's health or affect its measured HTTPS latency.
15+
16+
An existing SNI lease may hold one `relayBinding` to a remote lease with the same
17+
tenant identity. The remote token bounds the attachment's lifetime. Incoming
18+
overlay traffic always claims a local reverse session and cannot follow another
19+
attachment. TLS endpoint configuration remains the API caller's responsibility;
20+
the standard SDK/CLI does not orchestrate bindings. See the
21+
[IVNP overlay guide](src/routes/ivnp-overlay/+page.md) for the complete contract.
22+
1123
See [ADR index](adr/README.md) for the relay overlay migration decision and
1224
[the site architecture documentation](src/routes/architecture/+page.md) for the
1325
rest of the system.

docs/src/lib/components/landing/CoreFeaturesGrid.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
},
2727
{
2828
eyebrow: 'Mesh',
29-
title: 'Relay pools and multi-hop',
29+
title: 'Relay pools and IVNP',
3030
description:
31-
'Use discovered relays for failover or chain relays to split path visibility.'
31+
'Select public relays for failover and optionally connect relays through IVNP.'
3232
},
3333
{
3434
eyebrow: 'Ops',

docs/src/lib/nav.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const guidesNavigation: NavSection[] = [
5656
title: 'Guides',
5757
items: [
5858
{ title: 'Self-Hosting', href: '/self-hosting' },
59+
{ title: 'IVNP Relay Overlay', href: '/ivnp-overlay' },
5960
{ title: 'Portal Agent', href: '/portal-agent' },
6061
{ title: 'TCP/UDP Tunneling', href: '/tcp-udp-tunneling' },
6162
{ title: 'Game Server Hosting', href: '/game-server-hosting' },

0 commit comments

Comments
 (0)