9 ms·
How to build a plugin system on the web and also sleep well at night
- billconan 7y agoThank you very much! this is very helpful for me. I'm making https://epiphany.pub https://epiphany.pub , it also needs to run users' code. I thought iframe was the only viable way to run third parties' untrusted code, I have never heard of Realms shim. I will looking into it!
- tyleo 7y agoYeah, this technology looks great. I'm working on https://devev.com https://devev.com which will allow users to import JavaScript/TypeScript libraries and use their exported functions in flow charts. I'm pretty early stage but I got a desktop app running last night. I've got some documentation work to do but I have a PoC of Devev as a webapp which I'd like to deploy soon. I haven't resolved all of the security issues yet so this article is a goldmine for me.
- cameronbrown 7y agoYour product's concept looks great. I've used UE4 Blueprints for high-level prototyping while dropping down to C++ for stuff that needs tunes performance - it's insanely powerful combination. The web could use something like that.
- crdrost 7y agoAwesome! There is a way to avoid some of the pain discussed in the article with iframes as well, and it uses techniques from domain-driven design as applied to microservice architectures. So in this analogy the iframes are your "browser microservices" and the main app is also a sort of microservice and they all have to communicate with each other. The basic idea there is that most microservice architectures are actually subtly monolithic because they have direct communication via The Database. Basically whenever you have a shared database you have a form of tight coupling which defeats the point of microservices. So in DDD, you deal with communications difficulties by creating these Bounded Contexts where a word means one given thing; applying that here with a notion that "meaning" is controlled by The Database, you want to transition to "miniliths" where a set of services has its own local database. Then your two miniliths talk to each other by passing messages back and forward; generally you want these messages to be Events ("this happened over here and I thought you should know about it but I don't want a response") rather than Requests (which invite responses and then you have to ask questions about what if the response is not what you expected or it was not received, etc., what happened in the middle?). You don't have to go the whole way to an Event Sourced Architecture (where your model is entirely determined by a sequence of received Events which have been stored in a database and can be used to "rehydrate" that model from scratch at any given time) to get about 80% of the value of the message-passing. So translated to this context, basically what you have is a model of your system (possibly simplified) that you keep inside of the iframe and a model you keep in the app; the message bus communicates changes between the two but it seldom needs to serialize the whole structure at any given time. You allow plugins to interact with the in-iframe-model and it sends events "hey this happened" to the outside world, which has to respond to those events by updating its own data model. But fundamentally you have these two separate data models for the thing and they are being held together by a promise of eventual consistency through message-passing the diffs to the structure.
- lioeters 7y agoReally nice explanation of how domain-driven design can inform the architecture of browser-based plugins/microservices/miniapps. It makes sense to separate concerns by passing messages between isolated layers, and I can see how it could apply to other domains similarly. To summarize for my own understanding: - "Minilith" (love the term!) with a model of your system (possibly simplified) that you keep inside of the iframe - App with its own model - The message bus communicates changes between the two but it seldom needs to serialize the whole structure at any given time.. Eventual consistency through message-passing the diffs For some applications where I want to give users scripting capability, I've been thinking of a simplified DSL with its own "VM", passing some isolated objects from the host app to render results. The architecture you described, of "miniliths" with their own state (or in-memory database) passing messages to the host (or to each other) sounds like a sane and flexible approach.
- sverhagen 7y ago> Basically whenever you have a shared database you have a form of tight coupling which defeats the point of microservices. With all the different interpretations of what is a microservice, a common one is that having a shared database makes it not a microservices architecture. But yeah, people call them that regardless.
- rizky05 7y agoThis looks similar to node-red
- lioeters 7y agoThank you for sharing your project, Epiphany. The introduction article [0] does a great job of explaining its motivation and purpose. I found myself nodding along to the points made, and the whole concept of a social publishing platform for interactive content. [0] https://shi-yan.github.io/epiphany/ https://shi-yan.github.io/epiphany/
- billconan 7y agoThank you for liking it. I'm looking for feedback if you'd like to try it. I will appreciate it. here are some interactive examples: https://epiphany.pub/post?refId=2684bc94f9fcb9ffe637ebfbeba2af8c797c6ad9a66181026ee4bd3806b6f211 https://epiphany.pub/post?refId=2684bc94f9fcb9ffe637ebfbeba2... https://epiphany.pub/post?refId=3e123914a88818452d7f2c24fd8ad079ad621a5e94f514cca5598fb9479d5342 https://epiphany.pub/post?refId=3e123914a88818452d7f2c24fd8a... I'm trying to make more.
- lioeters 7y agoSure, here are some first impressions from someone who has no real experience with Jupyter, Observable, etc. :) I'm really amazed at the seamless reading and editing experience. This is very well-made, I can see it must have been quite an effort to make it feel so effortless. The ability for a code block to provide a "mini UI" is perfect. A reader can tweak controls and see immediate visual feedback, in the form of graphs, algorithms with variables.. One control I missed was a stop button - for the second example, I wanted to pause the generation of the 3D city/terrains, to continue reading. I liked the dotted blue vertical lines to indicate "block focus", to see what it contains when I click edit. While dragging a block, I had some difficulty crossing over another block which was long - in fact, I couldn't swap them, since dragging down didn't trigger the scroller to start. From what I know, this relates to the "center of gravity" during drag, whether the dragged block goes under another one. As for the scroller, maybe it's using a range that needs to be bigger, so that when I drag a block "below" the bottom of the screen, it starts scrolling. Anyway, lots of fun and impressive work! This looks very useful for educational purposes. You're really hitting a sweet spot with the concept - (just iterating on this phrase to get my head around it), a social publishing platform for interactive content. Great execution, and I'm looking forward to seeing it grow.
- welder 7y agoCan't wait for the ability to trigger plugins on events instead of having to select them from a menu [1]. That would open the door to many more useful plugins running automatically for ex every time a file is saved. 1: https://www.figma.com/plugin-docs/whats-supported/#trigger-plugin-code-on-events https://www.figma.com/plugin-docs/whats-supported/#trigger-p...
- jjcm 7y agoI was actually talking to their lead developer for their plugin architecture on Tuesday, and he said that it's likely very easy to allow this internally for org created plugins, or self-uploaded ones (similar to developer mode for chrome extensions), but it's an issue having it available in the wider market. Security concerns are a big one (as mentioned in this post since this is ran in the browser you can easily make requests as Figma.com) and scripts that run on non-user directed actions can be sneaky dangerous. The other thing is they want to make sure performance stays high, as slowdowns caused by plugins are often attributed to the program itself rather than the plugin. That said I'm totally fine if they isolate any event-based plugin as something you have to upload yourself or is bound strictly to your org using the app. As long as there's some way to do it designers will find a way, but without it I don't see it as being nearly as flexible as Sketch.
- NullPrefix 7y agoWhat's figma?
- twog 7y agoAn online design tool. Think of it as sketch/photoshop/illustrator in the browser.
- airstrike 7y agoThanks. For a single-person or maybe two-person team working in the same office, would you recommend Figma or Sketch?
- open-source-ux 7y agoBoth would work well assuming you are using them for UI design. Note that Sketch is Mac only though. Although Figma runs in the browser, they also have Electron-based desktop apps for Mac and Windows. Purchasing Sketch gives you one year of updates. If you decide not to renew the subscription, the app is still yours to keep and run - but you won't get any new updates. Figma follows the SaaS model of subscription, but they do have a free tier. Sketch also has a lot more tutorials and plugins than Figma at the moment, in case that is important.
- have_faith 7y agoI have used Sketch a lot and can recommend it quite highly. I'm not a big fan of in-browser tools so it's not a knock on Figma at all, I haven't used it beyond a very short demo. edit: I've used sketch from a design and a front end devs perspective and it was nice to use from both.
- allannienhuis 7y agoOur designers use Figma, and I've had to interact with it a fair bit as a developer. It's great. :) I can't speak to feature parity with sketch (I've used both though), but I really like the cross-platform nature of Figma, and its performance has been great for the projects I've worked with.
- ralmeida 7y agoThat's a very interesting topic! Does anyone know of other resources (blog posts or books) talking about how to build such extensibility in a SaaS app? Obviously, there are lots of inspiration to be drawn from apps we use everyday, such as GitHub, JIRA, etc, but these behind the scenes view is very informative.
- rudi-c 7y ago(Author here) There is material on the internet that are relevant to the topic, but it's quite hard to piece together. After all, there are only a handful of players right now who need to build an API that isn't a REST API. Among big names, I can think of Zendesk (uses iframes), Coda (runs third-party code on their servers IIRC, isolated via server mechanisms), Salesforce (not sure exactly what they do, but I think they also use Realms as a component to their system). https://medium.com/zendesk-engineering/sandboxing-javascript-e4def55e855e https://medium.com/zendesk-engineering/sandboxing-javascript... https://trailhead.salesforce.com/en/content/learn/modules/lex_dev_lc_vf_tips/lex_dev_lc_vf_tips_javascript https://trailhead.salesforce.com/en/content/learn/modules/le... There's a couple of academic papers on JavaScript isolation, but you'll have to do a lot of work to figure out how relevant they are. Be sure to check the publication date. The folks at Agoric are probably the leading experts actively working on untrusted code isolation in a browser environment right now. I would follow them if you want to hear about the latest new tech: https://github.com/Agoric/SES https://github.com/Agoric/SES
- benologist 7y agoI use iframes with my software to separate user accounts and other "SaaS boilerplate" into one web app that then proxies your web app and uses an iframe's srcdoc to serve the content within a template regardless of which server it came from. Using the srcdoc attribute lets you skip an additional request and mask the server's address, but it comes with some additional API restrictions like not having document.location. https://userdashboard.github.io https://userdashboard.github.io I believe this approach was pioneered by Facebook some years ago in the earliest incarnation of their app platforms. There was no iframe sandboxing so they had an intermediate contrived language called "FBML" which compiled to a subset of HTML they allowed. That was the platform where Zynga made their fortune on Farmville originally, just before the iPhone.
- gfodor 7y agowow, we are literally working this problem right now, and I made an off hand comment that it'd be nice if Figma shared how they did their plugins. thank you for this! edit: having now read the article, this is amazing, lots of get insights here. one question to the author if you are reading: it seems like it would be a worthy idea to open source the 500 line, security-sensitive interface Realm-shim. Selfishly, we would use it, but also, we (and surely others) would add eyeballs to it to ensure it's correct. Since it's a small slice of the system, and agonistic to the product itself, it seems unlikely to be part of any kind of technical competitive advantage. Any plans to do so?
- yuchi 7y agoLet me DuckDuckGo it for you: https://duckduckgo.com/?q=javascript+realm+shim https://duckduckgo.com/?q=javascript+realm+shim
- rudi-c 7y agoI think he's asking about the layer we built on top of the shim to copy objects in and out of it. It's open-sourceable, we can consider it.
- derp_dee_derp 7y agothink about this for a second: your competitor is asking to copy your work. If I were you, I wouldn't assume good intentions especially considering you are just a start up and in a race for marketshare. If it were up to me, I would set a date a year or two in the future and then open source it then, but only AFTER you have a large enough lead against your competitors in the market. There is no reason that I can see to open source the secret sauce of your entire product.
- gfodor 7y agowe're not competing with Figma (though we are happy customers), and we already open source everything we do, we're Mozilla.
- osrec 7y agoThis is one of the cleverest things I've seen in JS in a while. In a nutshell, they use a same origin iframe to ensure the plugin gets its own copy of globals (so it can't mess up the globals your app uses), coupled with a proxy object which whitelists certain globals for the plugin to use along with certain vars from your app. Really rather clever, although the guys who develop browsers should consider an API for something like this as it's becoming such a common use case.
- robinricard 7y agoThe Realms polyfill is a polyfill for an actual TC39 JS proposal [0]. It's currently at stage 2. If the proposal gets accepted, you will not need the polyfill. This would also work with any JS embedding (browsers, node, etc...) as it would be baked in the language. [0]: https://github.com/tc39/proposal-realms https://github.com/tc39/proposal-realms
- lern_too_spel 7y agoIt is possible to skip the iframe if you use a compiler. This was used by OpenSocial implementations more than a decade ago. https://en.m.wikipedia.org/wiki/Caja_project https://en.m.wikipedia.org/wiki/Caja_project
- 7y ago
- kaiby 7y agoI really like Figma's engineering blog. I find that they do a great job introducing the concepts that need to be understood with the level of detail in their implementation of those concepts. I'm always learning something new when I read an entry. This is the first time I've heard of Realms API or QuickJS, will need to keep those in mind if I ever need to write a plugin system.
- musicale 7y agoThird-party and/or untrusted javascript is obviously a massive security and privacy hole if you don't put it in an iframe.
- matharmin 7y agoThe entire post is going into depth on iframes and alternative solutions.
- pbreit 7y agoI think PayPal tried something like this awhile back that did not get much traction. "PayPal Apps" Developer Guide (2010): https://www.paypalobjects.com/webstatic/en_US/developer/docs/pdf/PayPalApp.pdf https://www.paypalobjects.com/webstatic/en_US/developer/docs...
- pbowyer 7y agoIt reminds me of https://github.com/krakenjs/zoid https://github.com/krakenjs/zoid which the PayPal widgets use internally to improve iFrames. Except AFAIK that project isn't security focused. Here's an article from 4 days ago about it: https://news.ycombinator.com/item?id=20772326 https://news.ycombinator.com/item?id=20772326
- felixfbecker 7y agowith (new Proxy()) { eval() } blows my mind.
- kodablah 7y agoPardon my ignorance, but what if my JS was "for (;;) {}"? Can this handle heavy-CPU plugins? Maybe in a service/web worker? Part of the Realm API being a good use case for plugins I assume would include this kind of isolation but I admit to not having looked in detail.
- EGreg 7y agoI was searching for where they would talk about Realms handling infinite loops on the main JS thread but I didn’t see it. Have they tried WebWorkers? This seems like exactly the type of thing WebWorkers were designed for, instead of iframes. A background thread that can load arbitrary code in a sandbox and postMessage to send messages to the main thread. I remember looking into completely locking down the webworker to not even be able to make requests. It was possible! Just start a webworker from a sandboxed iframe. https://stackoverflow.com/questions/10653809/making-webworkers-a-safe-environment https://stackoverflow.com/questions/10653809/making-webworke... https://gist.github.com/pfrazee/8949363 https://gist.github.com/pfrazee/8949363 https://github.com/asvd/jailed https://github.com/asvd/jailed
- AgentME 7y agoA web worker would come with the downsides of their first iframe-based solution. (Web workers are basically iframes that have no visual portion and are guaranteed to run in a separate thread.)
- rudi-c 7y agoExcellent question! Being able to interrupt plugins would be nice, but it was a requirement we had to drop. Instead, we show UI that makes it clear that a plugin froze rather than Figma, in the hope that the user can understand. WebWorkers have the same issues as iframes where are basically like separate processes where data needs to be copied with message-passing. But if we had gone with the iframe approach, we’d consider sticking the plugin code inside a WebWorker inside the iframe.
- pfrrp 7y agoRelated work by the AMP team is their worker dom project: https://github.com/ampproject/worker-dom https://github.com/ampproject/worker-dom The gist is to mirror a subset of DOM apis in workers and project changes back out to the main page. As far as I know a few companies have tried similar methods, but most write proprietary APIs, rather than using the DOM. Still in development but the examples are promising.
- warpech 7y agoPrior work in this area: http://www.adsafe.org/ http://www.adsafe.org/. It is a dated library targeted at safe DOM manipulations. However, it was researched quite carefully (see the PDFs in the left column)
- jasonmcaffee 7y agoHas anyone tried running the solution? It doesn't seem to work... The below code results in the console logging the document object, which has the document object, and the code hits a ReferenceError when trying to log the 'a' variable. Calls to p.whateverPropYouMakeUp result in the log 'get for target: ...' const proxyHandler = { get(target, name){ console.log(`get for target: `, target, name); return 'tacos'; }, } const p = new Proxy({}, proxyHandler); with (p){ console.log(`document with proxy: `, document); console.log(`access random property: `, a); } https://jsfiddle.net/wf02n4gs/ https://jsfiddle.net/wf02n4gs/
- pfraze 7y agoI was able to make it work by adding a has() trap. https://jsfiddle.net/wepmLrh0/ https://jsfiddle.net/wepmLrh0/
- quickthrower2 7y agoThanks - I extended that to show two "widgets" being fed 2 different JQueries: https://jsfiddle.net/habla/a56807ng/ https://jsfiddle.net/habla/a56807ng/
- jasonmcaffee 7y agoAwesome! Thanks!
- lwansbrough 7y agoWell I'm surprised I haven't seen it yet, but I'm using Google Caja[0] for my own side project[1] (which, actually, I haven't touched in a while..) This allows plugin developers to create fully custom HTML plugins that run in the same frame (which is crucial for AudioNodes to be operated on by multiple plugins in my case.) Example of a plugin: https://github.com/lwansbrough/attack/blob/master/src/plugins/push2-test.html https://github.com/lwansbrough/attack/blob/master/src/plugin... Actually my demo goes as far as showing how one plugin can register a resource that is then used by another plugin. In this case I developed a plugin which registers a high level interface for access to an Ableton Push 2 device, and then another plugin uses that interface to draw to the Push's display using the canvas API. https://twitter.com/lwansbrough/status/1125842014128312320 https://twitter.com/lwansbrough/status/1125842014128312320 [0] https://github.com/google/caja https://github.com/google/caja [1] https://github.com/lwansbrough/attack/blob/master/src/areas/daw/components/plugin/Plugin.vue https://github.com/lwansbrough/attack/blob/master/src/areas/...
- anamexis 7y agoThat is super cool!
- lwansbrough 7y agoThanks! And of course Caja is to thank for a large part of the major technical hurdles.
- cxr 7y agoCaja was started at Google under Mark S. Miller, and it's the predecessor to Agoric's current sandboxing work, which is what Figma is using here.
- lwansbrough 7y agoInteresting. I guess what is missing from the sandbox described in the article is the HTML/CSS component.
- zawerf 7y agoThis post was such a ride. At one point they found a fucking legitimate reason to compile a javascript interpreter to javascript(wasm) to run javascript!
- hn_throwaway_99 7y agoI know I'm just piling on (positively), but this was such as excellent post. Honestly, I think my biggest reaction after reading this was how amazing the engineering culture must be at this company. Working at a startup, with a relatively small team, but still having the luxury of all that time to try out multiple different approaches, get feedback, can the ones that didn't work without making it feel like a "failure" in any way. Major, major kudos. This is how engineering should be done.
- unnouinceput 7y agoPlugins. The security nightmare of desktop applications and the main reason Google store / Apple store is banishing developers accounts left and right. Like Schneier always said "you can always create an unbreakable security for you but smarter ones will find holes in it". I'm curious how this one will hold on long term, let's say an year from now.
- bastawhiz 7y agoI think the nice thing about their approach is that it's built on top of the same sandboxing that underpins the very fundamental primitives of the web. If this approach is broken, the same-origin policy of the web is broken. Compared to "native" approaches, where every application needs to implement their own solution, this seems far more durable (and easily fixed).
- unnouinceput 7y agoiframe is already broken. If I get you to hold my code inside an iframe and what I put there is a malware ActiveX your users going to blame you for their stolen accounts / btc mining worms. Windows is still a good target for malware creators despite the rise of mobile consumers.
- jackcarter 7y agoI learned about Figma recently, in this HN-frontpage article about fast software: https://craigmod.com/essays/fast_software/ https://craigmod.com/essays/fast_software/ It really is delightfully fast. It's no surprise that the team behind it is producing this caliber of content.
- AthLado 7y agoAs part of the YC Startup School we are inviting everyone that can code to join as a tech tutor at https://www.mooctors.com https://www.mooctors.com and help MOOC students graduate their courses while earning extra money for doing so. You set up your fee, and your availability to suit your life style, while your input could help shape MOOCTORS.