Skip to content

Tunnel client mode: jss --tunnel-connect to dial out to an existing relay (expose a pod behind NAT/mobile) #527

@melvincarvalho

Description

@melvincarvalho

Summary

Add a tunnel client mode so a JSS pod can dial out to an existing JSS relay and be exposed publicly — the inverse of the relay we already ship.

src/tunnel/index.js (tunnelPlugin, enabled by jss start --tunnel) is the relay/server half: it accepts WebSocket clients at wss://<pod>/.tunnel, proxies public HTTP (https://<pod>/tunnel/{name}/…) down the socket, and relays responses back. Registration is WebID-authenticated and the wire protocol is fully specified in the file header.

What's missing is the client half: a mode (e.g. jss start --tunnel-connect wss://<relay>/.tunnel --tunnel-name <name>) where, after the local pod is listening, JSS opens an outbound WS to the relay, registers, and forwards each proxied request to its own http://localhost:<port>.

Why

  • Exposes a pod behind CGNAT / on mobile (no public IP, no port-forwarding) — it's a single outbound connection.
  • The public URL is HTTPS (a secure context), which retires the localhost-only + mixed-content/crypto.subtle constraints that currently force same-origin-only sign-in (see js-pod/android Add Git support for containers #5/Phase 2: Link did:nostr to WebID profiles (owl:sameAs) #6). External Solid apps could then reach a phone-hosted pod.
  • Operator already runs a JSS --tunnel relay in the cloud; only the client is needed to connect to it.

Scope (the client)

Against the protocol already defined in src/tunnel/index.js:

  1. Open a WS to the relay's /.tunnel (with auth — see below).
  2. Send register with a name → receive the registered public path.
  3. On each request frame ({ id, method, path, headers, body }) → fetch('http://localhost:<podport>' + path, …) against the local pod → reply with a response frame ({ id, status, headers, body }).
  4. Reconnect with backoff (mobile networks flap); re-register on reconnect.
  5. New option(s): --tunnel-connect <wss-url>, --tunnel-name <name>, plus the auth credential.

Open questions

  • Relay auth for the client. /.tunnel requires a WebID today (getWebIdFromRequestAsync on the upgrade). The outbound client needs a credential the relay accepts:
    • Pre-shared tunnel token configured on both ends — simplest for a self-hosted relay; likely needs a small relay-side addition to accept it.
    • Solid-OIDC token — client authenticates to the relay's IdP and presents the bearer. Cleaner, more moving parts.
  • Path vs subdomain. Relay is path-based today (/tunnel/<name>/), so the tunneled pod's WebID becomes https://<relay>/tunnel/<name>/profile/card#me — works for reachability but trips Solid apps that assume a root-hosted pod. Subdomain (https://<name>.<relay>/) is cleaner for identity but needs wildcard DNS + TLS on the relay.
  • Stable name so the public URL / WebID / issuer doesn't churn between sessions.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions