PO Box -- communicating between elm apps

PO Box

PO Box is an experiment in setting up a mail box system of communication among Elm apps living in the same web page, e.g., different tabs thereof. Local storage is used as a post office, with a mail box for each app. Apps are distinguished by an identifier, e.g. "aaa123" for one app, and "xxx456" for another. Each app sets up an entry in local storage, where the key is the app identifier and the value is a string representing a list of JSON-encoded Elm values of type Message:

type alias Message =
    { to : String
    , from : String
    , subject : String
    , body : String
    , timeSent : Time.Posix
    }

The subject field can be something generic like “message,” of it can be used as a key for determining what action the receiving app takes on the information contained in the message body. Of course, the Message type can be customized according to need.

When app aaa123 sends a message to xxx456, that message is appended to the list with key xxx456. Thus a app xxx456 may receive messages from many others.

Ports are used both to send and receive messages. In the current version, an app implementing PO Box asks the post office to send any waiting messages once per clock tick (one second). When this operation is carried out, the app’s mail box is cleared out.

Demo

pobox

In the demo, all messages are sent with subject “message”. The source code for this experiment is at https://github.com/jxxcarlson/pobox

To play with PO Box, do sh make.sh, then click on index1.html and index2.html. Your browser should be configues so that these open up in different tabs of the same browser window. Try sending messages from each app, and see what happens in the other app.

In this demo, all the app does on receipt of a new message is add it to its list of messages.

In this demo, both apps are identical. They don’t have to be.

Comments

I’d very much appreciate comments, e.g., is this is viable concept?

1 Like

I’ve experimented with a similar approach using simple pubsub over window.postMessage instead of a postbox. I think using localStorage to buffer the messages is a really good idea - I had problems with race conditions. I also tried to make the message envelope bullet proof by having a Custom Type as the topic key and a JSON decoder/encoder pair. My goal there was to have a Platform.program doing all the domain work and the widets on the page just reacting to domain events and proxying user interactions back into the domain. A huge benefit is of course that your widgets can be written in anything, as long as it consumes and produces messages via pubsub the domain doesn’t care if it’s controlled by a React/Vue/Svelte/Elm/pick-your-poision frontend.

Regarding viability: although I find that this is a very neat setup where you can leverage the distinction between “app-external”/“api” and “app-internal” messages to protect the individual apps’ invariants it feels very verbose. It manages decoupling apps/widgets on the page fairly well. I guess it depends on how one is to utilize this: if you just want N decoupled apps on the page that you want to ping on certain events and that is all you really do this is totally viable - think adding something to a shopping cart and having the header display an incremented “items in cart” counter and maybe remove the item from a suggestionlist without additional server interaction. I will have to do some more digging in order to see if this is viable with a bigger app since it is essentially a distributed monolith which can be very hard to reason about.

What do you intend to use this for or is it just a thought experiment? Happy New Year!

1 Like

Thanks so much for the comments. This is partly thought experiment, partly experimentation, and partly one possible use case. Namely, I’ve written the authentication part of apps many times, and was thinking of using this to make little authentication app that could be reused. The idea is that its companion app(s) could query for the jwt token the authenticator gets from the server on login. However, I haven’t thought through the security problems that this could pose, even though the authenticator app and the app using it live in the same page.

Hmm, sharing auth tokens in localStorage is discouraged. Depending on the amount of claims you might be able to leverage http only cookies serverside. Maybe just pinging the rest of the page via PO box that auth was successful and having the companions themselves retrieve and store the token in memory would be a compromise worth considering?

Here is what I currently have in mind. There are two apps A and B, where A talks to the backend, can login there and get a JWT token. When A starts up, it sends a special token S to B that B must use whenever it needs to request sensitive info from A. The token S is a random string generated on the spot. This way A knows that it is B that is requesting info, not some malicious app.

So far so good, I hope. Now the user logs in using A, at which point A receives the JWT token J by HTTP and stores it in memory. Subsequently, if B needs the token J, it asks A for it, proving its identity by sending S. B replies with the token J, which B now has in memory.

The exposure of J in local storage is at most one tick – currently one second, This can be reduced by increasing the tick rate, or, better, by notifying B that there is a message to be retrieved whenever J is recceived by the post office.

Is this the sort of plan you were thinking of in your comments above? I agree that keeping an auth token in local storage is not a good idea,

@mfeineis, I’ve read the reference you sent me on storing tokens – very helpful. Thanks so much!

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.