Rendering shared avatars with privacy

Mark
3 min readOct 22, 2022

--

If you’re just tuning in, I’ve been making a privacy-focused alternative to big tech social media (e.g. Facebook/Meta).

It’s been awhile since I’ve posted, Metamorphic has gone through several re-designs and evolutions as I seek to get things ready for our beta MMP.

So I’m excited to share a little snippet of how we render avatars in the app for people while preserving the privacy, security, and performance. As a solo developer, I do not want to imagine doing this without the help of Elixir and Phoenix (and LiveView).

Problem

Everyone’s avatars are asymmetrically encrypted in Metamorphic, so when someone decides to share an avatar with someone they have to do so in a similar fashion to public-key cryptography.

This is great for security, but the question quickly became: how do we handle the multiple object storage calls, decryption calls, and temporary storage of these shared avatars?

Our current solution

Thanks to the enacl Elixir library, which relies on the incredible libsodiumlibary, encryption and decryption is wicked fast and we don’t have to worry about too much there (at least for the time being).

I noticed that the real bottleneck was the requests to object storage and back. So, the first thing was to offload those requests into an asynchronous task which enables us to load up the page without waiting for the avatars to come in.

As the avatars come in we store the encrypted blob (it’s an encrypted blob that’s stored in object storage) in ets which is an in-memory storage for Erlang (and thus Elixir). Then, each person is pulling each shared avatar out of ets and decrypting it on the fly.

Here’s a gif to see how it works:

Metamorphic shared avatars gif

Desired outcomes

As you can see from the gif above, I wanted people to be able to drop into their People page without any delay from waiting for the avatars to load from object storage, and I also didn’t want the loading avatars to require a page refresh (or re-mount of the socket) that would interrupt what a person might be doing on the page.

To sum, this is what I wanted to achieve:

  • quick page load
  • no page interruptions
  • preserve privacy and security

This is achieved by a helper function and a handle_info function to handle the Task.async/1 returns from the helper function:

GitHub gist for Metamorphic’s shared avatars handle_info/2 function.

This handle_info/2 is only invoked when someone has signed back into their account and currently does not have any shared avatars loaded into their ets table.

Once the initial object storage calls are made, the shared avatars are simply pulled from ets and decrypted on the fly.

Here is the helper function that we call with our avatar component in our page’s HTML:

GitHub gist for Metamorphic shared avatars helper function.

We also handle other invalid arguments passed to get_shared_avatar/3 and return "”. And inside our *.html.heex file:

GitHub gist for Metamorphic shared avatars html snippet.

This enables showing the spinner if a particular avatar is loading (sometimes it’s so quick you don’t see the spinner). It’s also taking advantage of LiveView 0.18 with the new :if syntax directly in the markup (inspired by Tailwind).

The .avatar and .spinner components are from the Petal Components library which is a great tool for any developer working with Elixir and Phoenix.

We also use the @avatar_loading_count and index to determine whether to display a spinner or display the avatar (this makes it so that it doesn’t reload all the avatars every time a new asynchronous message comes through).

Conclusion

And with that we’ve essentially enabled a pretty seamless experience for people when sharing their avatars without sacrificing the security, privacy, or performance.

Let me know if you have questions or feedback, I began my work with Elixir as I began Metamorphic (learning in public) and I’m always re-applying what I learn to continually improve and evolve Metamorphic.

Also, if you haven’t signed up, sign up now for invite codes to our beta when it launches!

💙 Mark

--

--

Mark

Creator @ Metamorphic | Co-founder @ Moss Piglet