Skip to content

Commit 2f8d0e9

Browse files
committed
feat(connect): add forward CONNECT mode + local dev helpers
1 parent 49a36ae commit 2f8d0e9

File tree

14 files changed

+1080
-44
lines changed

14 files changed

+1080
-44
lines changed

Cargo.lock

Lines changed: 54 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ iroh-base = { version = "0.95" }
2424
iroh-tickets = "0.2"
2525
iroh-metrics = "0.38"
2626
iroh-n0des = { version = "0.8", features = ["tickets"] }
27+
iroh-relay = { version = "0.95" }
2728
log = "0.4"
2829
open = "5"
2930
openidconnect = "4.0.1"

README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,154 @@ cd cli
1717
cargo run -- --help
1818
```
1919

20+
### Local forward-proxy demo (no GUI)
21+
This exercises the CONNECT-based gateway flow that Envoy will use in staging/prod.
22+
23+
#### 1) Start a local DNS dev server (out-of-band)
24+
Use a non-`.local` origin (e.g. `datumconnect.test`):
25+
26+
```
27+
cargo run -p datum-connect -- dns-dev serve \
28+
--origin datumconnect.test \
29+
--bind 127.0.0.1:53535 \
30+
--data ./dns-dev.yml
31+
```
32+
33+
#### 2) Start the listen node (connector side)
34+
This prints the endpoint id and the iroh UDP bound sockets you must publish:
35+
36+
```
37+
cargo run -p datum-connect -- serve
38+
```
39+
40+
Copy the printed `dns-dev upsert` example, but run it via `cargo run -p datum-connect -- ...`
41+
and make sure the origin matches `datumconnect.test`. Quote IPv6 addresses like `"[::]:1234"`.
42+
43+
#### 3) Verify TXT resolution
44+
Get the z-base-32 endpoint id:
45+
46+
```
47+
python - <<'PY'
48+
import binascii
49+
ALPH = "ybndrfg8ejkmcpqxot1uwisza345h769"
50+
def zbase32(data):
51+
bits = 0
52+
value = 0
53+
out = []
54+
for b in data:
55+
value = (value << 8) | b
56+
bits += 8
57+
while bits >= 5:
58+
out.append(ALPH[(value >> (bits - 5)) & 31])
59+
bits -= 5
60+
if bits:
61+
out.append(ALPH[(value << (5 - bits)) & 31])
62+
return "".join(out)
63+
endpoint="REPLACE_WITH_ENDPOINT_ID"
64+
print(zbase32(binascii.unhexlify(endpoint)))
65+
PY
66+
```
67+
68+
Then query the TXT record:
69+
70+
```
71+
dig +norecurse @127.0.0.1 -p 53535 TXT _iroh.<z32>.datumconnect.test
72+
```
73+
74+
#### 4) Start the gateway in forward mode
75+
76+
```
77+
cargo run -p datum-connect -- gateway \
78+
--port 18080 \
79+
--mode forward \
80+
--discovery dns \
81+
--dns-origin datumconnect.test \
82+
--dns-resolver 127.0.0.1:53535
83+
```
84+
85+
#### 5) Send a CONNECT request
86+
If your target TCP service is on `127.0.0.1:5173`:
87+
88+
```
89+
printf "CONNECT 127.0.0.1:5173 HTTP/1.1\r\nHost: 127.0.0.1:5173\r\nx-datum-node-id: REPLACE_WITH_ENDPOINT_ID\r\nx-datum-target-protocol: tcp\r\n\r\n" | nc 127.0.0.1 8080
90+
```
91+
92+
### GUI demo (browser tunnel)
93+
This mirrors the same flow, but uses the GUI to create the proxy entry.
94+
95+
If you want a one-shot experience, run:
96+
97+
```
98+
./scripts/try-ui-demo.sh
99+
```
100+
101+
It starts dns-dev, an HTTPS origin, the gateway, and the GUI, and waits for you to
102+
create a TCP proxy in the UI before visiting `https://localhost:5173` in the browser.
103+
104+
#### 1) Start `dns-dev`
105+
```
106+
cargo run -p datum-connect -- dns-dev serve \
107+
--origin datumconnect.test \
108+
--bind 127.0.0.1:53535 \
109+
--data ./dns-dev.yml
110+
```
111+
112+
#### 2) Start a local HTTPS origin (so the browser uses CONNECT)
113+
```
114+
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
115+
-keyout /tmp/iroh-dev.key -out /tmp/iroh-dev.crt \
116+
-subj "/CN=localhost"
117+
openssl s_server -accept 5173 -cert /tmp/iroh-dev.crt -key /tmp/iroh-dev.key -www
118+
```
119+
120+
#### 3) Run the GUI (share the repo with CLI)
121+
```
122+
export DATUM_CONNECT_REPO=$(pwd)/.datum-connect-dev
123+
cd ui
124+
dx serve --platform desktop
125+
```
126+
127+
#### 4) Create a proxy in the GUI
128+
Add a TCP proxy for `127.0.0.1:5173`.
129+
130+
#### 5) Start the listen node (uses the same repo)
131+
```
132+
cd ..
133+
export DATUM_CONNECT_REPO=$(pwd)/.datum-connect-dev
134+
cargo run -p datum-connect -- serve
135+
```
136+
Copy the printed `dns-dev upsert` example, but change the origin to `datumconnect.test`
137+
and run it via `cargo run -p datum-connect -- ...` (quote IPv6 addresses).
138+
139+
#### 6) Start the gateway in forward mode
140+
```
141+
export DATUM_CONNECT_REPO=$(pwd)/.datum-connect-dev
142+
cargo run -p datum-connect -- gateway \
143+
--port 18080 \
144+
--mode forward \
145+
--discovery dns \
146+
--dns-origin datumconnect.test \
147+
--dns-resolver 127.0.0.1:53535
148+
```
149+
150+
#### 7) Start a local entrypoint that always tunnels through the gateway
151+
This avoids any browser proxy configuration. It listens on `127.0.0.1:8888` and
152+
uses CONNECT under the hood to reach the target:
153+
```
154+
cargo run -p datum-connect -- tunnel-dev \
155+
--gateway 127.0.0.1:8080 \
156+
--node-id REPLACE_WITH_ENDPOINT_ID \
157+
--target-host 127.0.0.1 \
158+
--target-port 5173
159+
```
160+
Now visit:
161+
```
162+
https://localhost:8888
163+
```
164+
You should see the `openssl s_server` status page (cipher list + handshake info).
165+
That output is expected and means the CONNECT request tunneled through the gateway
166+
to the local origin.
167+
20168
### Running the UI:
21169

22170
to run the UI, make sure you have rust, cargo, and dioxus installed:

cli/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@ tracing-subscriber.workspace = true
1212
clap = { version = "4.5.50", features = ["derive", "env"] }
1313
tracing.workspace = true
1414
tokio-util.workspace = true
15+
async-trait = "0.1.89"
16+
humantime = "2.1.0"
17+
serde.workspace = true
18+
serde_yml.workspace = true
19+
hickory-server = "0.25.2"
20+
hickory-proto = "0.25.2"
21+
iroh-base.workspace = true
22+
z32 = "1.0.3"

0 commit comments

Comments
 (0)