# Web Tiles Explainer > User agents act as intermediaries between a service and the end user; rather than > downloading an executable program from a service that has arbitrary access into the users' > system, the user agent only allows limited access to display content and run code in a > sandboxed environment. End users are diverse and the ability of a few user agents to > represent individual interests properly is imperfect, but this arrangement is an > improvement over the alternative — the need to trust a website completely with all > information on your system to browse it.\ > [*RFC 8890: The Internet is for End Users*](https://www.rfc-editor.org/rfc/rfc8890#section-4.2-3), > Mark Nottingham Trust has been the defining constraint on the web's evolution towards more powerful, more applicative capabilities. In a web context, the user must be able to safely load any arbitrary URL, to safely click on any arbitrary link. The way in which this is achieved is that the runtime places strict limits on what a web page can do, which in turn necessarily limits powerful capabilities. App stores shift the trust around: apps get more powerful capabilities but there is a review and enforcement bottleneck at the store level which is load bearing for trust. This can open up more powerful capabilities but at the cost of producing a chokepoint for rent extraction and self-serving policies. The system cannot work without trust (user would face excessive harm and abandon it) and that trust must be anchored somewhere. Offloading trust to the user is not a promising option either. Fifteen years of W3C workshops on permissions and consent on the web (and many other similar efforts) have succeeded in only one thing: establishing that permissions and consent are not a promising avenue through which to extend the web. More generally, installation ceremonies do not provide the means for people to reason about different security models and, in some cases, introduce undesirable differences between a page/app running locally or online. To make matters worse, the web's trust model is anchored in [the same-origin security policy](https://www.w3.org/Security/wiki/Same_Origin_Policy). While this provides a relatively natural boundary for user agents to reason about, it makes it difficult to *compose* web services safely, which is to say to have two or more web pages cooperate to work for the user. Pages are not composable because novel threats emerge when two origins are allowed to communicate with one another. This puts stringent limits on the web's ability to allow people to combine two services together, which in turn limits the web's usefulness. Tiles provide a new approach to the problem of trust. A tile is a set of content-addressed web resources that, once loaded, cannot communicate further with the network. This additional safety makes it possible to grant tiles some powers with the trust that they cannot exfiltrate data (or at least not without deliberate user interaction). Tiles can additionally compose on the client side, to build a user-orchestrated application web (rather than monolithic web apps). - Tiles are **local-first and location agnostic**. Because a tile cannot exfiltrate content, its content, once loaded, has to be sufficient to operate without touching the network. And thanks to the fact that a tile is content-addressed, saving or "installing" a tile simply means ensuring that it remains cached locally. *Publishing* a tile can happen offline. - Tiles are **multi-device** from the get-go. You can move a tile from your laptop to your mobile phone to your TV safely and effortlessly. - Tiles are designed to be **composable**. Having two or more tiles communicating together should not produce a threat profile different from that of any of those tiles in isolation. (Depending on where we land *exactly* in terms of security models, it is possible that the threat profile may worsen some but it should be only in ways that the user agent can reason about.) This is thanks in part to the fact that tiles cannot hit the network, and all their other interactions are user-mediated. - While the technology isn't ready yet, the fact that tiles build on content addressing gives them a good hook to rely on some forms of [**private retrieval**](https://research.protocol.ai/blog/2023/private-retrieval-grant-2023-roundup/) that benefit from this. - By essentially removing servers from the equation and moving the intelligence to the agent, tiles **shift power to the user**. Anyone who has built anything on the web should worry about the client-side nature of tiles however: servers are useful for quite a few things, or at least that's the common practice. In order to make up for that loss, we complement tiles with a number of key APIs and with a specific client-side composition mechanism that makes it easy for tiles to work with one another to create sophisticated, user-centric experiences. This can be achieved with relatively minimal changes to how web pages are built and in a way that supports better user experiences. Trying to build web apps by copying native apps is like trying to build web pages by emulating PDF. Sure, you do need some capabilities like arbitrary fonts and colours, but it's a mistake to inherit the straightjacket and the antiquated design decisions. The applicative capabilities that go with tiles are: - **Wishes and local composability**: wishes are an iteration on intents/activities that enables any tile to call upon other tiles (based on user preference) to handle specific requests and conversely to provide services of its own to other tiles in an interoperable, decoupled way. Modern primitives like UCANs align well with wishes when authorization is desirable. - **Gated access to specific protocols**: while tiles cannot `fetch` from protocols, they can interact with them (e.g. post to ATProto or ActivityPub) in browser-gated ways. - **Sophisticated local storage and provision**: tiles can write to a sophisticated local storage system that can then be selectively shared (by the user) with other parties. Submitting a form, for instance, is (at the conceptual level, the UX need not change) editing an object in that storage and then sharing that object with the intended party. There is growing interest in tile or tile-like approaches as noted at IPFS Thing 2023 where Capyloon, Peergos, and Protocol Labs all presented variations on this idea. *This document is a batch of early notes and should not be considered to be stable or definitive.* ## What's a Tile A tile is a DAG-CBOR of metadata and content, available over IPFS. The metadata is similar to that found in [Web Application Manifests](https://www.w3.org/TR/appmanifest/) and the content is all of the web resources needed to run this tile. We don't use the exact appmanifest format because tiles aren't a direct port of the notion of native app to the web, and there is mismatch — but reuse applies where possible. The purpose of the metadata is to provide functionality equivalent to embedding cards (title, description, image, etc.) because tiles are expected not only to be rendered and run in full, but also to be listed for instance in search results or social feeds. This provides a hook into pages-as-objects that the web has struggled to hack together consistently and that is particularly useful in a content-addressed world. It is also a place to sign and assert authorship (and possibly other similar properties like peer review), to declare usage rights or child-appropriate flags, to capture declarative wishes, icons, and more. Tiles load over the `tile://` scheme and the authority is a CID, e.g. `tile://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/`. The CID is that of the tile's metadata document which, in JSON, looks like: ```json { "name": "Fx Magic", "description": "Fx Magic is a great image editor with plenty of great effects!", "icons": [ { "src": "fx.svg" } ], "resources": { "/": { "src": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", "mediaType": "text/html" }, "/fx.svg": { "src": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", "mediaType": "image/svg" }, "/pretty.css": { "src": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", "mediaType": "text/css" }, "/app.js": { "src": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", "mediaType": "application/javascript" } }, "wishes": [ { "name": "Fx magic it!", "can": "edit", "what": ["image/*"] } ] } ``` Both `name` and `description` should be self-explanatory. They are used in contexts in which the tile is listed but not rendered, for instance in feeds, search results, social media timelines. All `icons` must be listed in `resources` if they are to be resolved and used. The `resources` entry maps URL path components (in full) to CIDs and media types required to load and render them. These essentially mint URLs under the tile's authority, e.g. `tile://bafy…/pretty.css` and are resolved as such at runtime. The `/` resource is shown by default. :::info IPFS supports directories for instance in UnixFS but that requires significantly more work to consume and produce, and has impedance mismatch with web requirements. This approach of having a single map of all resources keyed by path is more web-like. ::: The `wishes` entry lists wishes that this tile can grant; in this case it is able to edit images. The loading context imposes a CSP equivalent to: ``` Content-Security-Policy: default-src 'self' style-src 'self' 'unsafe-inline' script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; img-src 'self' blob:; media-src 'self' blob:; ``` :::warning **issues** - We probably want to specify an interoperable set of CID options (v1, which multicodec types) that are supported across the board. - That CSP isn't exactly the one in cosmopolis, e.g. it doesn't have `data:`. - We should look at WACZ: https://specs.webrecorder.net/wacz-ipfs/latest/#wacz-and-zip-file-chunking ::: ## Composing Tiles: Wishes/Intents/Activities Users want to accomplish tasks more than they want to use any given app. The app-centric model is limiting and the web can do better. Web apps have so far been modelled on native apps: a blueprint for silos. Instead, we focus on dynamically linking tasks together to create a seamless flow of interactions. The existing technology matching this approach is [Web Intents](https://www.w3.org/TR/web-intents/). Web Intents were developed (and abandoned) by the W3C's Device APIs Working Group as a way to enable precisely the kind of composition described here between Web pages. They were inspired by Android Intents. A number of alternative designs were proposed at the time, one of which being Mozilla's [WebActivities](https://wiki.mozilla.org/WebActivities) which is still in use in B2G. The system used in tiles is tentatively named "wishes" so as not to conflict with an existing design, but it's possible that we will end up adopting an existing one. (Capyloon simply uses Web Activities for instance.) A wish is a verb applied to a type of thing. A tile's metadata describes which wishes it can grant. ```json wishes: [ // this can pick images and return them { "can": "pick", "what": ["image/*"], "name": "Select an image from our cat memes collection" }, // this can create a social post which the user can post { "can": "post", "what": "com.atproto.repo.create", "name": "Post a cat meme" } ] ``` Whereas hyperlinks are nouns — they *name* things — wishes are verbs. In this sense, they are comparable to HTTP's verbs (methods) but offer richer semantics that sit closer to user applications and that can be fulfilled by the user agent. When a verb is invoked, the agent typically needs to ask the user what they want to invoked it on or with (e.g. which source pick images from, what to edit with). The tile making a wish has a simple API: ```javascript button.onclick = async () => { const blob = await window.makeWish('pick', { what: ['image/*'] }); if (!blob) return message('Cancelled picking image.'); image.src = URL.createObjectURL(blob); }; ``` A wish can also be passed data that it can act on. This doesn't require trusting the party that made that wish since that data has to remain on the client. ```javascript const res = await fetch(`pic.jpg`); // from inside the tile itself const blob = await res.blob(); const editedBlob = await window.makeWish('edit', { what: ['image/*'], data: blob }); ``` A tile can handle a wish by listening to the `wish` event on its `window` and by interacting with `currentWish`. (The API will almost certainly change some.) To make this less abstract, we can walk through a couple of examples. We start with a very basic tile that wants to obtain an image — it doesn't care about the source, that's up to the user: ![Basic UI to pick an image, with a "Pick Pic" button](https://hackmd.io/_uploads/SykY-qnK2.png) Clicking "Pick Pic" causes that tile to wish to `pick` and `image/*`. The user has a set of image sources of their own depending on which tiles they have "installed": ![Same basic UI with also a list of potential sources for images](https://hackmd.io/_uploads/S1O-zc2Yn.png) If the user selects "Pick random colour", this loads a tile the sole function of which is to return an image made of a random colour. Not fascinating, perhaps, but we're not here to judge: the web provides. ![A random shade of burgundy which can be picked or regenerated](https://hackmd.io/_uploads/BJhFf93K2.png) Accepting that lovely shade of burgundy returns an image to the wishing tile: ![The original basic UI with an embedded block of burgundy](https://hackmd.io/_uploads/SJ9aGq2Yn.png) Wishes can nest arbitrarily — the user ought to just flow from action to action. If instead of going for the random colour we had chosen the "Pack of pics!" (just a tile containing a curated list of pictures), we'd have seen: ![A list of nine pictures, each of which can be picked or edited](https://hackmd.io/_uploads/S1X_79nth.jpg) Those images can be picked, in which case they would be returned to the wishing tile, but each can also be edited first. Clicking "edit" with make a wish to edit that image: ![Pick an editor: Fx Magic](https://hackmd.io/_uploads/rkECQq2K2.jpg) This user only has one image editor (the system also requires a way to discover wishes if you don't have one): Fx Magic. Picking that, we get an advanced image manipulation system provided by a completely different party — we use it to invert the blowfish: ![Image editing UI with a few filters and an inverted paper blowfish](https://hackmd.io/_uploads/BkVUNcnF3.jpg) Accepting the edit returns the result to the pack of pic which in turn returns it to the original calling tile: ![Initial basic UI with the edited blowfish](https://hackmd.io/_uploads/Syv3N93F2.jpg) This is of course a trivial example, but we have made multiple unrelated parties compose and collaborate safely, without any prior knowledge of one another (only the user and agent connect them), in a way that presents no risk to the user of having their data shared with arbitrary parties. More involved interactions can be required when the wish is not about a simple request/response action. For instance, we can think of a wish the purpose of which is to produce recommendations for the user. The way it works when installed and active is that it receives lists of items of the kind that it knows how to produce recommendations for, it filters out anything that it knows should not be in the list (e.g. I've said I never want to read that author again), it ranks the rest according to whatever applicable criteria (eg. using its own ML model or simply chronologically), and returns that ranking to whatever tile is rendering the list. A wish can be a very thin wrapper around a piece of WASM, for this kind of purpose. Eventually, the difference between a tile and an extension becomes limited. ## Built-in Protocols This section is just a sketch, more detailed work is needed but it can happen progressively. The fact that a tile cannot touch the network means that a number of protocols must be supported directly by the user agent. While this constrains the system, it also creates a point of enforcement for open standards (if the agent itself is governed well). These protocols must generally be activated by the user and not trivially exposed to tiles lest they acquire the ability to exfiltrate data. To begin with, **search must be a protocol**. This does not depart from earlier hypertext systems (e.g. WAIS). People can configure multiple search sources, which can be queried together, can have contextual uses, knobs, differentiated ranking options, etc. and the user agent calls them via a protocol, collates the responses, and renders them. This approach has great potential to improve user agency in search (and defend against capture of the search market). It has consequences in terms of funding (and of the potential funding of browsers themselves) that should be explored separately. In a similar vein, **social must be a protocol**. Two things distinguish a social network from another: one's identity and the graph of connections with others, and the format of posts. Having multiple identities ought to be managed by the browser and the attached social graphs should be part of the protocol (e.g. in the ATProto PDS that you use). The format of posts is an unnecessary constraint: if the content of social feeds is itself made of tiles, then since those tiles are safe when they load and run, the content of a social feed can be essentially arbitrary. Better: using wishes, a tile can effectively be a tile factory. So if I invent a new social publishing format, I can make a tile that handles wishes to post by producing tiles matching that format. Tiles also need **local storage with permissioned sharing**. Interacting with a form should conceptually be using that form to edit the object it describes and then sharing that with the intended party. (This means the form can be saved locally and reused, too.) One option here is WNFS+UCAN. Another may be Solid, even though every time I've looked there I've found the RDF to be beyond my reading level. Rather than having every site that connects people together reinvent end-to-end chat poorly, **it should be easy to put two endpoints in a Signal chat together**. The list goes on: protocols for ads, for shopping cart management, for purchase, etc. The idea is not that we need *all* of that for tiles to be useful; however we can use tiles as a stepping stone to a world in which the core functions of the are handled by protocols that give people real agency (and stop reinventing wheels poorly). There is a careful balance so that we don't end up bundling every API in the world into the agent, but we should be relatively expansive in terms of making tiles a batteries-included system. A *lot* of web development is a repetitive invocation of a small number of primitives; we can make both users' and developers' lives easier by adding core primitives here. ## Feeds and Tile Modes Tiles support multiple rendering modes: card and active. The reason for this is to make it possible to embed an arbitrary number of tiles in a feed and show them without triggering the run of a potentially unbounded number of separate rendering processes. The card rendering mode does not load the content or cause the runtime to run anything. It simply exposes the content as a card that summarises it, as is often done in search results or social media. A lot of useful content on the web is lists: search results, social timelines, curated RSS feeds. Instead of having each of these contexts reinvent which metadata matters and come up with incompatible ways of producing and rendering it, the built-in card view of a tile means that we can get a uniform behaviour across all kinds of feeds. This makes it possible to treat tiles as objects consistently across the UI. You can "repost" a tile to your social timeline directly from a search result listing because a card can always have that affordance. You can save a tile from a social timeline to a feed that you produce in your local library, that may or may not be followable by others. You can just drag a tile to the browser folder in which you keep the references you use for an ongoing project. It's all uniform, browser-directed UI. (Tiles do assume that we reinvent browser UIs beyond tabs.) ## Privacy Considerations An important promise is that tiles can be more powerful by default because they are safer by default: they cannot exfiltrate data, at least not easily. This means that a tile can only load the resources under its root, but once that is done it cannot touch the network again. This is a very stringent constraint. The idea is to start stringent and then see what can be opened. ### Considering the threat We know from experience that privacy attackers are highly motivated. Notably: they can dedicate significant resources to tracking behaviour, they can be very inventive, they do not need perfect data and are comfortable with probabilistic matching. The threats of opening up arbitrary HTTP access are well-known (see: the web). It's tempting to think that content-addressed loading solves that. It does help, but it is not sufficient on its own, at least not in its current state. We look at a few threats to show how an attacker might proceed given IPFS-only loading. #### Behavioural tracking Here the goal is to produce an event stream about a person's behaviour, presumably in order to profile that person and target them with advertising. An attacker could create CIDs for events of interest (which would typically be a relatively restricted set) and cause those to be loaded as a stream, observing the DHT as the requests are made and identifying the person using timing and IP address. This would not be perfect as the events would get cached, but it would be possible to introduce enough variance to avoid that. There is a balance between exposing an excessive number of CIDs and how much caching may defeat you, but there is little doubt that an attacker would figure out the most effective approach. Note that in theory a tile's resources list could be created such that it contains similarly crafted CIDs for events of interest. An implementation that loads resources dynamically such that it fetches only the blocks which are needed will be susceptible to this kind of attack. #### Geolocation tracking If you make a GPS navigation app, the user can be tracked (coarsely, but effectively enough) by paying attention to which map segments the app loads (assuming the list of resources is the full geo tiling). Since map segments are presumably known in advance (eg. from OpenStreetMap), double-hashing doesn't help. This is a special case of BT. #### Large Files (audio & video) Another special case is tiles containing large data, notably audio and video. You cannot expect the UA to load everything and so range requests need to be included. But range requests can be used to signal bits back to whoever is serving the content, a process which can be driven by programmatically seeking around an audio or video resource. This could be prevented by severely throttling just how much seeking can happen. ### Escape hatch It's important to remember that *not everything needs to be a tile*. Ideally, a user agent would mix tiles and non-tile web content, the difference being that tiles would have access to more features and to composition. But we should recall that traditional HTTP web pages are available and users' experience need not be negatively impacted if they can navigate to both. The difficult resides in ensuring that the UI clearly distinguishes the two and makes it clear what functionality is available where. A key issue is linking and navigation tracking prevention here. If a tile links to an HTTP page, opening it in a separate UI helps but the browser needs to strip nav tracking too. ### Mitigations Some mitigations can be considered for a number of cases. Generally, privacy need not be perfect, we primarily need to make sure that tracking attacks are too costly or too unreliable. One option would be that the initial top level resources can be loaded directly (as being harder to track) and additional IPFS loads can be triggered inside the tile but have to go through slower but more private mechanisms such as Tor+IPFS. Similarly, we could direct additional loads to trusted services, for instance Saturn nodes on the edges that could offer guarantees of privacy. We could consider additional mitigations such as rendering such nodes observable and subject to slashing if they participate in tracking. We could also limit additional data loading to some publicly curated datasets that we can guarantee sufficient distribution for. This could include maps, weather data, or dependencies for JS, CSS, icons, and fonts. ## Differences from Previous Proposals Finding primitives and systems to support a more application-oriented (rather than document-oriented) web has been a persistent problem of the past decades. Some additions have proven useful (e.g. workers) while many others have floundered. Without attempting to compare tiles with all previous approaches (there have been many), it is useful to describe what sets tiles apart. Three things are worth teasing apart here: first, the primitive itself, which is a relatively simple and constrained; second, some consequences in terms of what becomes possible based on that primitive; and third some broader (but brief) philosophical considerations about the overall approach in which this primitive fits and which it furthers. First, the key primitive underlying tiles is a strong sandboxing model that limits the extraction of data much more severely than any prior proposals, once tiles are loaded they cannot communicate back to the network, not even to their own origin. (In fact, the notion of origin becomes at least partly moot.) We don't claim 100% extraction-proof sandboxing because tiles can have links to non-tile web pages and data can be extracted that way. (Those links have to be user-activated, but that only affords so much protection even if [nav-tracking protection](https://privacycg.github.io/nav-tracking-mitigations/) can help.) However, for a great number of uses the sandboxing is effective. This makes tiles composable in that the security and privacy properties of multiple tiles communicating with one another are the same as those of any given individual tile. Second, these properties have positive consequences. The sign of a good web primitive is that it may not do much on its own but when composed with others things start happening. The sandboxing means that we can consider exposing some more powerful capabilities out of the box (notably in the class that creates privacy risk). It also means that we can enable tiles to communicate with one another more. Given that they are composable without additional risk, the properties of a system like Web Intents/Web Activities/Wishes match tiles very well and open the door to strong client-side UI/task composability. Finally, this primitive has desirable philosophical implications. By moving composition from the server to the client, with loose joints that empower people to easily choose how they wish to compose services, this system significantly increases user agency and shifts power from servers to people. The user agent can also monitor interactions between tiles and intervene when necessary (or at the very least render those interactions available for auditing). Additionally, monolithic bundled apps have been a deeply ingrained user-hostile pattern for a very long time. This approach may finally allow us to move to a new model that aligns with people's expectations better. One thing that is worth noting is that, because tiles are content addressed, we can get a stronger and more predicatable sandboxing as well as a path towards privacy mitigations in content loading since content need not be obtained from its origin. We also get a more permanent web, and "installing" a tile is as simple as just keeping it around locally — something that is very hard (impossible in the general case) over HTTP. You can just pin your tiles, back them up, etc. Arguably the biggest lift with tiles is in user interface: they probably do not work in a classic tabbed browser UI and we probably shouldn't try to make that happen. While it is key for the web to rely on the notion of user agents, there is nothing to say that the current UI paradigm is right. ## Appendix: The Name We are not in love with the name "tiles". It sort of works because they compose with one another on the client, horizontally, and so in a sense they *tesselate*. Other options include: - offgrids - mycelia - sensory deprivation - tessel Mood board: * private * can't phone home * powerful + isolated * composable * networked with one another, horizontally We further have the issue that the composability layer has been called both "intents" and "activities", and it's tricky to pick between them, especially since both map to existing APIs which we are certain to at least extend and probably change. (Even if Web Intents aren't alive any more, on Android browsers can still invoke intents via the `intent:` scheme.) The name we propose to use instead is "wish". You *wish* to pick a picture or contact, you *wish* to share some content, you *wish* to edit a video, etc. We can backronym it on demand, eg. *Web Intent System for Hyperapps*. One option for tiles that would pair well with "wish" would be "mish". Basically, web+mushrooms $\rightarrow$ mesh $\rightarrow$ mish (to get something different). Modular Isolates {Soldered,Strapped,Strung,Sewn,Set up,Spread,Speaking,goSsinpping} Horizontally. ## Appendix: Notes from IPFS Thing, 2023-04-19 * not able to phone home is key property * can't have composability if can phone home * tracking/exfiltration is core threat model * need to link to pages on the regular web, but how to not exfiltrate data * block navigation entirely in proof of concept * require user activation? * use-case for activation: blog that links to individual post * eg: ship arbitrary content to social media * post cool app in tile, click on "install" to install the app * social is tiles generating other tiles * how to unpublish? * you're fucked * tombstoning, revocation, etc * does need to be content addressed at start? * sxg! * start a new CA? * no. * ok ip tiles vs web tiles * ip tiles -> content addressed * maybe can sign w/ wallet * (killer app format for dapps?) * CBOR42 CAR * metadata * name * icons * cards * CID (filesystem - winfs) * index.html * (MUST HAVE MIME TYPES) * search * tile * tile * tile * tile rendering modes * metadata mode * active mode * when fetching tiles for a list * just get metadata blocks * index just metadata blocks * cannot just be links * back to http problems * tiles that create tiles * intent handler * click install * instagram is a way of creating social that generates tile format * browser tab model locks into certain models * security model * limited composability * must break out of tabs * use a tile to describe how to work w/ other tiles * can't have tiles that load tiles * tracking vector * but tile that controls other tiles? * what if tile supported configurable layouts * intents system that marries tiles together via opaque blob-like urls * that gives you composability * for things like 3 pane ui where 1 tile configures 3 tiles (each pane) in a way that they work together * common api (i have 3 things which support the X api) * tile templates * locally installed/pinned 'applications' * ok fine yeah this is smalltalk * goal is composability on the web * power + composability is the leapfrog recipe * challenge: don't have an installed tile that supports an intent, how do you find one? * ipni: indexing network data by data type * pubsub * subscribe to a data model * implementation in capyloon, peergos, electron * no need to standard/spec yet * developer workflow * dev mode * "unpacked" * f5/refresh dev cycle * ipfs add . | pubsub topic | cids -> hot reload * things to get from browsers sooner rather than later: * fix the RTC hole in CSP