Skip to content

Reach shared hosts through the cloud's relay, without Tailscale - #61

Merged
enowdev merged 2 commits into
mainfrom
feat/team-relay
Nov 12, 2026
Merged

enowdev merged 2 commits into
mainfrom
feat/team-relay

Conversation

@enowdev

@enowdev enowdev commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Team members reach a shared host through the cloud's relay, without Tailscale.

  • Relay (cloud/server/relay.go): the host keeps an outbound WebSocket to /v1/relay/host; a member connects to /v1/relay/connect with its ticket; the relay checks the ticket without consuming it, calls the host, which opens /v1/relay/accept/{id}, and copies bytes. Per team: 32 connections, 4 MiB/s (burst 16 MiB); per IP: 30 connects a minute. Env ENOWX_CLOUD_RELAY_TEAM_CONNS/RATE/BURST.
  • End to end (cloud/relay/e2e.go): the host's X25519 key is announced in the sealed team space; member and host run an NK-like handshake (two DHs, HKDF-SHA256) and ChaCha20-Poly1305 records with counter nonces. The relay sees who, when and byte counts.
  • Host: the relay and the optional Tailscale listener feed the same team listener, so ticket redemption, roles, the 2-minute recheck, per-address limits and first-frame rules are unchanged.
  • Member: tries the relay first, then the Tailscale address (kept for older apps). Error texts no longer mention Tailscale.
  • Protocol grows by HostShare.relay and TeamHost.key.

Tests: pairing, refused tickets (forged, removed member, reused), team limit, relay can't read payloads, host reconnect, and an end-to-end visitor through the relay with removal dropping the connection.

Not verified: behind the production proxy, across two real machines, the UI by eye.

🤖 Generated with Claude Code

enowdev and others added 2 commits November 11, 2026 20:01
Members will reach shared hosts through the cloud's relay, which must not
read what passes. The member's app opens a channel to the key the host
announced in its team's sealed space: fresh X25519 keys both ways, a proof
the host holds the announced key, and ChaCha20-Poly1305 records under a
counter, so a relay can't read, change, drop or replay them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A member reached another's shared host only over Tailscale, so teammates
outside the owner's tailnet couldn't. The host now keeps a connection to
the cloud's relay while it shares anything; a member connects with their
ticket, the relay checks it without using it, pairs the two and passes
bytes, end to end encrypted to the host's announced key. The host redeems
the ticket and applies roles, the 2-minute recheck, per-address limits and
the first-frame rules as before, as relay connections feed the same team
listener. The relay holds each team to a number of connections and a byte
rate. Hosts with a Tailscale address keep listening there for members of
an older enowX, which knows no relay; the app tries the relay first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@enowdev
enowdev merged commit 00183bc into main Nov 12, 2026
4 checks passed
@enowdev enowdev mentioned this pull request Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant