Distribution from browser to host
To be able to support the community debugging tools, specifically Observer, we'll need to be able to expose the Lumen runtime running in a browser tab to the host. Because the host wants to connect to nodes it can find by first talking to EPMD, we will need a proxy that shows up as another node on the host, but then gets forwarded to the browser.
- [ ] Make the distribution transport pluggable in
lumen_runtime. This may be the same interface asnet_kernelor something specific to Lumen using Rustfnsimilar to the time source.- [ ] Use @mobileoverlord's
phoenix_clientto connect to theHOST- [ ] Use wrapper around
Lumen.Web.WebSocketto connect toHOSTin place ofwebsocket_clientthatphoenix_clientuses by default.
- [ ] Use wrapper around
- [ ] Use @mobileoverlord's
- [ ] A
Lumen.BeamSplitterElixir project that runs on theHOST- [ ] A dedicated
socket, similar to howphoenix_live_reloadis on a separate socket prefix from the app's default one - [ ] Bind a port on the host that can act as a secondary node on the host (
PROXY_PORT) - [ ] Negotiate with EPMD so it thinks
PROXY_PORTis another node. - [ ] Pass messages received on
PROXY_PORTto the Phoenix Channel
- [ ] A dedicated
With this setup it should be possible for :observer.start() launched from iex -S mix phx.server on HOST to then connect to the localhost:PROXY_PORT and talk to the Lumen runtime in the browser tab.
Alternatives
net_kernel on HOST
Overriding net_kernel on HOST so that it natively understand websockets, similar to how other developers have overridden net_kernel on Heroku and other restricted environments.
Raw WebSockets
Instead of going over Phoenix Channels, we could use a raw websocket. This makes it easier to talk to things that aren't Phoenix, but makes it hard to integrate with Phoenix as we couldn't use the Endpoint socket DSL.
I’m a bit confused by the hang up on EPMD, with BEAM it is possible to provide your own EPMD backend, this is often used to remove the dependency on EPMD from environments where it is not supported. This combined with a custom distribution transport should allow avoiding EPMD entirely. See epmdless as an example.
I get you can go without EPMD, but I want to support still having EPMD so that potentially this could run in a normal host project, similar to how Phoenix Live Reload does for Phoenix project and not need to enforce going EPMDless on the entire project.
I'd like to understand the benefit of supporting EPMD here, at least initially. I'm not super convinced that there is anything to be gained.
We definitely can't support remote shells in non-local/non-dev settings (for the browser target), since it would absolutely overwhelm distribution, so the general case of wanting the app to use EPMD in production isn't relevant. So we're really talking specifically about getting a remote shell node to connect via a single host node, to a single client-side node. In that setting we don't have a need for EPMD, and removing it as a requirement involves less moving parts, as far as I can tell.
To give you the idea of how I see it working:
- Start the app, this is configured to use the custom WebSocket distribution transport and a EPMD backend that is aware of the client-side node; as the client-side Lumen node is connected via WebSockets to the app. The client-side node uses a special node name that is always the same (e.g.
[email protected]). The EPMD backend hosts a WebSocket server which relays messages to the client-side node. - Start local debug node with
[email protected] EPMD_MODE=client iex -S mix --erl="-proto_dist websocket_dist -epmd_module custom_epmd" --hidden --cookie foo --name [email protected] --remsh [email protected]
The debug node is running the same EPMD backend, but configured to run as a client rather than server. It connects to the [email protected] name by connecting to the node in $EPMD_APP_NODE as a WebSocket proxy.
NOTE: The invocation of iex above wouldn't actually need all of those flags since it would already be provided as part of app configuration, it is more to illustrate what's needed.
To be clear, I'm not saying your solution isn't viable, or unacceptable, I think it is both; I'm just hoping that there is a simpler solution possible. If what I'm suggesting above isn't simpler, or if there are other reasons it isn't viable or less than ideal, that's fine, I don't see any reason why your proposal isn't viable.
Note to reassess if the WASM debug support is good enough to replace this effort