Strategy for building a dweb-first web wallet (using

3 208
Avatar for sahidmiller
2 years ago

EDIT #1:

Q: The technical jargon was hard to follow. Can you ELI5? What problem does this solve and what is its use case?

A: Non-custodial web wallets live on a domain, a domain we don't control. The problem is that a domain owner could technically turn the non-custodial wallet into "a custodial" web wallet with one deployย without their users even knowing by changing the code that's loaded via URL.

IPFS solves that with because all content is fetched by it's hash... but it comes with some it's own UX problems...

The strategy laid out in the article attempts to turn this UX problem intoย a strength.

My best analogy in the cryptocurrency world might be the transparent ledger. Some might look at it as a weakness, due to needing to get new addresses off chain for security reasons. But some see it as a strength (as it originally was intended) for example, for auditability of coin supply and tokens, etc.

This is a slightly adapted excerpt from my branch's

This branch demonstrates a dweb first of using peer-to-peer technology (via IPFS/IPNS)

The approach taken by this project is to support security-focused dweb users FIRST by making basic IPFS usage its flagship feature (ex. /ipfs/Qm...)

On the other hand, IPNS usage is opt-in for those not as security-focused and expect certain UX norms.

In other words, this application treats CIDs as a feature rather than a bug.

๐Ÿ“ Strategy

The UX strategy is to provide a basic app shell that shouldn't change often, so we can open it up directly by CID.

Given the user is fetching from an honest gateway, any changes to the app shell code will be visible to the user via the url (since the CID is just a hash of the content being fetched).

Given the user is fetching from a gateway using subdomain redirects, new app shell code will not have access to storage data from the old code ๐Ÿ’ช๐Ÿฟ

That's why it's rarely changing. Data would have to be migrated purposefully to a new app shell CID that the user would need to recognize and expect/respect.


The app shell that's launched will handle fetching the more often changing code (ie. plugins) from IPFS.

By using IPFS as opposed to legacy http, the app shell can explicitly version, fetch and verify plugin code integrity all in one string, given an honest gateway like the one embedded in your Brave Browser.

The app shell will then store the last plugin CID the user gave permission to use so it can be fetched in later sessions... and it will only update that CID when the user gives permission, for security at a low cost.

Of course, this project doesn't use legacy http to fetch updated CIDs, it uses IPNS Records. So the user can be sure the updated CID was signed by the developers they expect and comes with p2p ready "offline-capabilities".

1) On first visit, the app shell will fetch and store the latest plugin CID using IPNS and display it immediately.
2) On subsequent visits, the app shell will check storage and use the stored CID to display. It will only fetch from IPNS to get available updates to display from now on, God willing.

Throw some cryptographic signatures into the mix and locking the version for all subsequent visits is a verifiable, offline-capable, piece of cake.

๐ŸŽ‰ Both users and developers gain autonomy... if the app shell works as expected!

So security focused users can and should evaluate IF the app shell meets their security needs. If it does, they can use the app shell exclusively until they choose to upgrade or use another CID that fits their needs. Nice.

๐ŸŽ Additional UX

For non-security focused users, each project in the repository will be published to IPNS for the following UX reasons:

1) When the provider app is embedded using the IPNS url: they can get the latest provider code and even latest app shell CID without changing HTML!
2) When the app shell is launched using the IPNS url: users can get the latest app shell code and even latest plugin CIDs without migrating data!

This should hopefully provide the UX necessary for both security and non-security focused users.

๐ŸŽ‚ Module federation

This project leverages Webpack Dynamic Module Federation to separate the often changing wallet code from the basic app shell. This allows us to split up the code in a way that's still friendly developers.

This is a game changer for front-end development especially in regards to IPFS.

In fact, due to the way the project is built with this, developers can create their own app shell and leverage our plugin code in their project without including it in their project!


This project leverages a custom webpack plugin to provide latest CIDs of dependent projects at build time. This essentially makes the current CID of sibling projects available to other sibling projects.

This is useful for a number of reasons:

1) When the provider app is embedded using IPNS: they will have the latest app shell CID automatically and so can opt out of fetching it via IPNS since it's already included with the code, God willing!

2) When the provider app embedded using IPNS or IPFS url: the embedder may prefer to only use the existing app shell by default until they upgrade the provider or whitelist versions.


Using Module Federation, a simple app shell, and IPFS/IPNS, this project supports quite a few different use cases and features for security-focused users, non-security focused users, and developers, allowing for autonomy, security, choice, and a harmony of competing needs.

Unfortunately, what I didn't get the chance to elaborate on is how the provider code works to launch the app shell the user expects and how it's intended to be embedded into HTML or imported into frontend code by website owners...

That's because I haven't worked on it yet!

There's a natural network effect where website owners and their users will gravitate towards app shells they mutually prefer, so a plugin archictecture on the provider side may be necessary to achieve that.

Website owners themselves may only launch app shells that are flexible enough to allow users to automatically redirect to the app shell URL they actually prefer since that would meet both their needs, God willing.

A more elegant may exist though!


Want to support Signup wallet? Send us a tip in Bitcoin Cash to this address. We spend all the funds for coffee and code! => bitcoincash:qqqes3ygxpx589wfn44kqlhqzda8zscf952xxxa2au

$ 17.17
$ 10.00 from @SIGNUP
$ 6.02 from @TheRandomRewarder
$ 0.50 from @Devalbo
+ 2
Avatar for sahidmiller
2 years ago


Thank you for this great work, the idea of accessing a web3 BCH wallet directly using the IPFS browser in Brave is really exciting. Webpack module federation seems like a fitting choice for the problem but we should investigate the verifiability of the final output. Also considering the browser extension port of the Signup, we need to make sure the app shell architecture works in both use cases.

Would be great to have the conversation and decision making in a public space, how about creating a RFC issue in the github repository of the Signup and linking it here for getting into more details?

$ 1.00
2 years ago

I'm thinking some extensibility points in the provider app would be enough for website owners to launch the extension as opposed to the IPFS based app shell.

3 options included (that embedders can add or remove to) that users can choose from: IPFS, Extension,, or domain of their choosing (as long as conforms to postMessage API)

It'd be nice to see what users think. I lean heavily towards IPFS since it's dweb + mobile friendly AFAIK and can be supercharged by using a go-ipfs gateway like this:

$ 0.00
2 years ago

Interesting enough, the "tech" behind module federation (and dynamic imports, in general) is really basic, just dynamically adding script tags to the HTML from the users perspective which is good for them for extensibility reasons.

And for us developers, this is useful because webpack can negotiate what modules to share between each other so we can loosely couple our code. It's really just a slightly more complex version of the existing "externals" webpack config prop!

$ 0.00
2 years ago