
How to build a plugin system on the web and also sleep well at night - rudi-c
https://www.figma.com/blog/how-we-built-the-figma-plugin-system/
======
osrec
This 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
The 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)

~~~
orf
You would still need the polyfill for quite a while. Just not forever.

~~~
donatj
We’ve needed to support IE11 a lot closer to forever than I had ever expected.

------
gfodor
wow, 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
Let me DuckDuckGo it for you:
[https://duckduckgo.com/?q=javascript+realm+shim](https://duckduckgo.com/?q=javascript+realm+shim)

~~~
rudi-c
I 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
think 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
we're not competing with Figma (though we are happy customers), and we already
open source everything we do, we're Mozilla.

~~~
yuchi
I see only now your reply (I’m the “let me DDG it for you" guy). Sorry for my
unelegant answer!

------
lwansbrough
Well 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/plugin...](https://github.com/lwansbrough/attack/blob/master/src/plugins/push2-test.html)

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/...](https://github.com/lwansbrough/attack/blob/master/src/areas/daw/components/plugin/Plugin.vue)

~~~
cxr
Caja 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
Interesting. I guess what is missing from the sandbox described in the article
is the HTML/CSS component.

------
hn_throwaway_99
I 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.

------
kaiby
I 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.

------
billconan
Thank 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
Yeah, 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.

~~~
crdrost
Awesome!

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
Really 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.

------
pfrrp
Related 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.

------
jackcarter
I 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.

------
jasonmcaffee
Has 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
I was able to make it work by adding a has() trap.

[https://jsfiddle.net/wepmLrh0/](https://jsfiddle.net/wepmLrh0/)

~~~
quickthrower2
Thanks - I extended that to show two "widgets" being fed 2 different JQueries:
[https://jsfiddle.net/habla/a56807ng/](https://jsfiddle.net/habla/a56807ng/)

------
kodablah
Pardon 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
I 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-
webworke...](https://stackoverflow.com/questions/10653809/making-webworkers-a-
safe-environment)

[https://gist.github.com/pfrazee/8949363](https://gist.github.com/pfrazee/8949363)

[https://github.com/asvd/jailed](https://github.com/asvd/jailed)

~~~
AgentME
A 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.)

------
warpech
Prior 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)

------
zawerf
This 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!

------
welder
Can'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-p...](https://www.figma.com/plugin-docs/whats-
supported/#trigger-plugin-code-on-events)

~~~
jjcm
I 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.

------
unnouinceput
Plugins. 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
I 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
iframe 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.

------
pbreit
I 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...](https://www.paypalobjects.com/webstatic/en_US/developer/docs/pdf/PayPalApp.pdf)

~~~
pbowyer
It 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)

------
ralmeida
That'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
(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...](https://medium.com/zendesk-engineering/sandboxing-
javascript-e4def55e855e)
[https://trailhead.salesforce.com/en/content/learn/modules/le...](https://trailhead.salesforce.com/en/content/learn/modules/lex_dev_lc_vf_tips/lex_dev_lc_vf_tips_javascript)

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)

~~~
gfodor
We're investigating this now for extensibility of our 3D avatar based
communication tool we're developing at Mozilla, Hubs (hubs.mozilla.com.) Our
thinking + diligence so far lines up entirely with what Figma outlined here --
however we are still in the planning stages, not building anything yet, so
this post was greatly appreciated in revealing a lot of insights we were
missing!

------
felixfbecker
with (new Proxy()) { eval() } blows my mind.

------
NullPrefix
What's figma?

~~~
serpix
It is a vastly superior alternative to Sketch or Illustrator.

Used to work in an ad office making ads with Freehand and Illustrator and
Photoshop. Figma is a wet dream compared to tools of that day. Could not have
even dreamed of something like it back then.

~~~
jjcm
This is hyperbole for sure - there are some amazing things that Figma does and
I love using it, but it's still missing a lot of things that Sketch and
Illustrator have. On the plugin side which is what this article was about,
Sketch is leagues ahead of Figma. File system access and native app
functionality mean that plugins can go beyond just the constraints of a
browser. Even in the web constraints though, Sketch is still leading in the
support it has. An example of this is an events API - Figma doesn't have any
way to listen to insertions of components and react accordingly to it. This
vastly limits the scope of what Figma plugins can do - they can only be
proactive, not reactive. Often we want plugins that just work seamlessly, and
right now Figma doens't support that.

Illustrator on the other hand is still vastly better at what it's named after
- illustration. Figma has great basic vector functionality that will cover 99%
of your design needs, but little around illustration needs.

I'm really excited for Figma and what comes out of it, but it still has a long
way to go to catch up. I think it has a better foundation though.

~~~
ljm
I was only really sold on Figma because it could actually read Sketch files.
That was an absolute blessing when I worked on a Windows machine. It’s
improved loads since then but ultimately I’d still take a native app over a
browser-based one.

~~~
aidos
With Figma’s deep use of webassembly and canvas, it’s much closer to a native
app than a web app. Certainly it’s much less webappy than most.

------
musicale
Third-party and/or untrusted javascript is obviously a massive security and
privacy hole if you don't put it in an iframe.

~~~
matharmin
The entire post is going into depth on iframes and alternative solutions.

