-
Notifications
You must be signed in to change notification settings - Fork 2
fix(tlsnotary): bake wstcp into the image + publish the proxy port range #949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Shitikyan
wants to merge
9
commits into
stabilisation
Choose a base branch
from
fix/tlsn-wstcp-proxy-reachability
base: stabilisation
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
f6c3acc
fix(tlsnotary): bake wstcp into the image + publish the proxy port range
Shitikyan 4ecc70b
fix(tlsnotary): single PORT_CONFIG source so the env range drives all…
Shitikyan b56b20c
chore(tlsnotary): pin wstcp version + --locked for reproducible builds
Shitikyan 2fa26f7
fix(tlsnotary): drop --locked from wstcp install (breaks the build)
Shitikyan 81b8fe9
fix(tlsnotary): publish the wstcp proxy range on the devnet stack too
Shitikyan 7471b5b
fix(tlsnotary): route /tlsn/<port>/ for notary + dynamic wstcp proxies
Shitikyan c6611b7
docs(tlsnotary): reverse-proxy runbook — /tlsn/<port>/ route + the ca…
Shitikyan c49fab9
fix(tlsnotary): restrict /tlsn/ proxy route to the 55000-57999 window
Shitikyan fcc246f
fix(tlsnotary): pass TLSNOTARY_PORT to the caddy container + runbook …
Shitikyan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| --- | ||
| type: runbook | ||
| title: TLSNotary reverse-proxy routing (/tlsn/<port>/) | ||
| status: active | ||
| --- | ||
|
|
||
| # TLSNotary reverse-proxy routing | ||
|
|
||
| The browser SDK opens `wss://<node-domain>/tlsn/<port>/` for both the notary and | ||
| each per-target **wstcp** proxy. The node's public reverse proxy must route | ||
| `/tlsn/<port>/` to the matching local port **as a WebSocket** (HTTP upgrade). If | ||
| it doesn't, the attest websocket closes with `CloseEvent: { code: 1006 }`. | ||
|
|
||
| This is required on **every** node that serves TLSN to a browser. The node image | ||
| (this is baked in — see the Dockerfile `wstcp` stage) and the published proxy | ||
| port range are repo-managed; the reverse proxy is the only piece an operator may | ||
| need to touch by hand. | ||
|
|
||
| ## ⚠️ The one non-obvious gotcha | ||
|
|
||
| **`wstcp` 0.2.1 checks the `Connection` header case-sensitively** — it requires | ||
| `Connection: Upgrade` (capital U). The common nginx snippet uses | ||
| `proxy_set_header Connection "upgrade";` (lowercase), which `wstcp` rejects: | ||
|
|
||
| ``` | ||
| Invalid WebSocket handshake request: assertion failed: `values.any(|v| v.trim() == "Upgrade")`; value="upgrade" | ||
| ``` | ||
|
|
||
| → the handshake never completes, every `/tlsn/<port>/` 502s, and the browser sees | ||
| `CloseEvent 1006`. Use **capital `Upgrade`**. | ||
|
|
||
| ## Caddy (repo-managed — automatic) | ||
|
|
||
| Deploying with the `proxy` compose profile applies | ||
| `monitoring/caddy/tlsnotary-modes/subpath.caddy`, which routes `/tlsn/<port>/` | ||
| for the notary and the wstcp proxies. Caddy's `reverse_proxy` performs the | ||
| upgrade with the correct casing — no manual step. Just redeploy. | ||
|
|
||
| ## nginx (hand-maintained nodes) | ||
|
|
||
| Add this `location` inside the node's `listen 443 ssl; server_name <domain>;` | ||
| block, then `sudo nginx -t && sudo systemctl reload nginx`: | ||
|
|
||
| ```nginx | ||
| # Dynamic wstcp TLSNotary proxies: /tlsn/<port>/ -> 127.0.0.1:<port> | ||
| # The 5[567]\d{3} class restricts to the 55000-57999 allocation window so this | ||
| # can't be used to reach arbitrary node ports (RPC 53550, MCP 3001, ...). | ||
| location ~ ^/tlsn/(5[567]\d{3})/?(.*)$ { | ||
| proxy_pass http://127.0.0.1:$1/$2$is_args$args; | ||
| proxy_http_version 1.1; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| proxy_set_header Connection "Upgrade"; # capital U — wstcp is case-sensitive | ||
| proxy_set_header Host $host; | ||
| proxy_read_timeout 120s; | ||
| } | ||
| ``` | ||
|
|
||
| If the notary port is outside `55000-57999` (e.g. `7047`/`7147`), give it its own | ||
| `location = /tlsn/<notary-port>/ { ... }` block, or widen the class accordingly. | ||
|
|
||
| Prerequisites (both already handled by deploying this branch): | ||
|
|
||
| 1. **wstcp baked into the image** — `docker exec <node> ls /app/.cargo/bin/wstcp` | ||
| must exist. | ||
| 2. **Proxy port range published on loopback** so nginx can reach it — e.g. | ||
| `127.0.0.1:55000-55063` (mainnet) / `127.0.0.1:55100-55163` (devnet), matching | ||
| `TLSNOTARY_PROXY_PORT_MIN/MAX`. Confirm with | ||
| `docker port <node> | grep 55` and `sudo ss -ltn | grep ':55'`. | ||
|
|
||
| ## Verify | ||
|
|
||
| ```bash | ||
| # expect HTTP 101 (Switching Protocols) | ||
| curl -s -o /dev/null -w '%{http_code}\n' -k \ | ||
| -H "Connection: Upgrade" -H "Upgrade: websocket" \ | ||
| -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ | ||
| https://<node-domain>/tlsn/<a-live-proxy-port>/ | ||
| ``` | ||
|
|
||
| `101` → routing + upgrade are correct. `502` → the location is missing, the port | ||
| isn't published/reachable, or `Connection` is lowercase. | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Note: HTTPS RPC | ||
|
|
||
| The browser client (HTTPS page) cannot call a plain-HTTP node RPC (mixed | ||
| content). The node RPC must be reachable over HTTPS at the URL the client is | ||
| built with. Production nodes already terminate TLS at the public domain; only | ||
| custom-port dev setups (`...:53650` with no TLS) need a dedicated `listen <port> | ||
| ssl` block in front of the RPC. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,33 @@ | ||
| # TLSNotary route — subpath mode (default). | ||
| # Mount at https://${PROXY_DOMAIN}/tlsnotary/. Caddy strips the prefix | ||
| # before forwarding so the notary container stays at root. Works when | ||
| # upstream notary has no sub-path assumptions in its WS framing. | ||
| # TLSNotary routes — subpath mode (default). | ||
| # | ||
| # The node advertises EVERY TLSN endpoint under /tlsn/<port>/ (buildWsUrl's | ||
| # default path), and two upstreams live behind that prefix: | ||
| # - the notary websocket — `tlsnotary` container on $TLSNOTARY_PORT | ||
| # - the per-target wstcp proxies — spawned in the `node` container on a | ||
| # dynamic port in TLSNOTARY_PROXY_PORT_MIN..MAX (wstcp binds 0.0.0.0, | ||
| # so it's reachable by service name on the compose network) | ||
| # Both must be routed or the SDK's attest() websocket closes with 1006. | ||
| # Caddy upgrades the websocket automatically; no extra headers needed. | ||
| # | ||
| # Selected when TLSNOTARY_PROXY_MODE=subpath (or unset). | ||
|
|
||
| # Notary — legacy /tlsnotary/* and the advertised /tlsn/<notary-port>/. | ||
| # Honour $TLSNOTARY_PORT: the dev stack remaps it (e.g. 7147), so the | ||
| # hard-coded 7047 here is what made the notary leg 502 on remapped hosts. | ||
| handle_path /tlsnotary/* { | ||
| reverse_proxy tlsnotary:7047 | ||
| reverse_proxy tlsnotary:{$TLSNOTARY_PORT:7047} | ||
| } | ||
| handle_path /tlsn/{$TLSNOTARY_PORT:7047}/* { | ||
| reverse_proxy tlsnotary:{$TLSNOTARY_PORT:7047} | ||
|
coderabbitai[bot] marked this conversation as resolved.
greptile-apps[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| # Dynamic per-target wstcp proxies: /tlsn/<port>/ -> node:<port>. | ||
| # The literal notary path above is more specific, so it wins for the | ||
| # notary port; everything else under /tlsn/<digits>/ lands here. | ||
| # Restrict to the 55000-57999 wstcp allocation window so this can't proxy | ||
| # arbitrary node ports (RPC 53550, MCP 3001, metrics 9090, ...) publicly. | ||
| @tlsnproxy path_regexp tlsnport ^/tlsn/(5[567]\d{3})(/.*)?$ | ||
| handle @tlsnproxy { | ||
| uri strip_prefix /tlsn/{re.tlsnport.1} | ||
| reverse_proxy node:{re.tlsnport.1} | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.