Add-ins · Plugins · Editor Extensions · App Listings

Two parties stand between you and your user, and both of them read your code.

Every other distribution model on this site involves you and whoever receives your bundle. A marketplace add-in involves a reviewer who decides whether it may be published and a host application that decides what it may do once it runs. Neither of them is your customer, and neither of them is under your control. Nearly everything unusual about protecting this kind of code follows from those two relationships rather than from the JavaScript itself.

The Short Version

Exclude the host API · Pin the seed · Expect the review question

Three decisions carry the whole configuration, and two of them are the opposite of the site's usual advice.

The host owns the API namesRename them and the add-in loads, connects to nothing, and reports no error.
Pin the seed so updates diffRandom per-build identifiers make every submission look like a total rewrite to a reviewer.
The evaluation wrappers are often unavailableThree options need dynamic evaluation, and many hosts forbid it outright.
Why This Page Exists

The distribution model that adds a reviewer

This site has a page for most ways JavaScript reaches a user: a web page, a desktop application, a mobile bundle, a widget on somebody else's site, an npm package, a device. Marketplace distribution is different enough to need its own, because it is the only model where a third party has to approve your artifact before anyone can install it, and where the runtime your code lands in was designed by somebody with different priorities from yours.

The artifact is published by design

Anyone can install your add-in and read what they installed. There is no meaningful restriction on who obtains a copy, which rules out access control as a protection strategy and leaves attribution as the thing worth investing in.

A reviewer forms a view about your code

Review works by reading, either by a person or by a scanner. Whatever the written policy says, transformation makes that reading harder, and a process built on the assumption that nobody will notice is a fragile one.

The runtime is somebody else's design

Frames with unusual origins, restricted evaluation, injected global objects with fixed names, message-passing boundaries. Half the options on this site behave differently in that environment than they do on a page you serve.

What Is Actually At Risk

Separate the four things people mean by copying

The word covers four different problems with four different answers, and conflating them is how teams end up disappointed by a build setting. This is the same separation the widget and SDK page makes, sharpened by the fact that your artifact is deliberately public here.

Reimplementation, which protection does not touch

A competitor who wants your feature will use your product, understand the behaviour and write their own. They never open your bundle. This is the most common form of copying and the least affected by anything in a build pipeline.

Rebranding, which protection does slow down

Somebody publishes your add-in under a new name with the logo swapped. Renamed identifiers and transformed strings make that meaningfully more work, and a watermark lets you prove where the copy came from when you report it.

Feature unlocking, which needs a server

A paid tier checked in the browser is a check a user can remove. Protection raises the cost of finding it; it does not change the fact that the decision is being made on the wrong machine. Entitlements belong in a request your service answers.

Credential extraction, which is a design error

Any key shipped inside a published add-in is a public key in the practical sense, because the artifact is downloadable by everyone. Nothing in a build fixes that: the add-in must call your service, and your service must hold the credential.

The Host API Surface

The names the host chose are not yours to rename

Every add-in platform hands your code an object graph: a context, a document model, a set of commands, an event channel. Those names are fixed by the host. The failure that follows from renaming them is silent, which is what makes it worth a section of its own.

Member renaming rewrites property access

A call into the host is property access like any other. Rename it and the call reaches a property that does not exist. There is no build error and no syntax error: the add-in loads, the panel renders, and the button does nothing.

Use rule-based selection, not everything

Member protection is designed to be pointed at what you choose rather than applied blindly. Keep the host namespace out of scope, and keep the list next to your manifest so it is reviewed whenever the manifest changes.

Manifest identifiers are strings the host matches

Command names, action identifiers and function names declared in your manifest are matched as text by the host. Keep them out of the string transforms with the reserved strings mechanism so the declared name and the shipped name stay the same.

Runtime Options In A Host

Three options behave differently here, and one of them helps

Runtime defenses assume a browser page you control. An add-in frame is a browser context somebody else configured, so it is worth knowing exactly what each guard reads before you enable it.

Domain locking usually works, and here it is useful

A hosted add-in is served from your origin into a frame, so the guard reads your own hostname and the allowlist matches. That constrains someone who republishes your bundle from their own site, which is a realistic threat for a published artifact. The failure case is a host that runs your code in a frame with no meaningful origin: the guard treats an empty hostname as failure and fires before the allowlist is consulted, so test inside the real host rather than on your development server.

The evaluation-based wrappers are often unavailable

The self-defending wrapper, the self-healing recovery wrapper and the anti-tampering monitor all evaluate code at run time, and the engine emits a build warning saying they need a policy that permits it. Many hosts forbid dynamic evaluation, exactly as a strict content security policy does. The same three are skipped automatically for ES module source, with their own warning, because they are classic-script wrappers and would break import linking. Read the warnings rather than assuming the option took effect.

Blocking developer tools is the wrong instinct here

The keyboard and context-menu blocking option cancels a global event, and inside a host application that is not your window to govern. It interferes with the host's own affordances and with assistive technology, and it does not prevent anyone from reading a published artifact they already downloaded.

Console suppression will cost you support time

The option that empties the console methods removes the one diagnostic channel a user can send you from inside somebody else's application. For a distribution model where you cannot reproduce the environment, that trade is almost always bad.

The Seed Inversion

Pin the seed, because somebody is diffing your releases

Identifier generation is randomised per build by default, and on most of this site that is a benefit: an attacker's notes about one release do not survive to the next. Marketplace distribution inverts the calculation, because the party comparing your builds is the party deciding whether to publish them.

Without a seed every update is a rewrite

Two builds of the same source differ in every identifier. A reviewer diffing your submission against the approved version sees a wholesale replacement rather than the three functions you changed, on every release, forever.

With a seed the diff is the change

A fixed seed makes the output reproducible from the same input and configuration, so the difference between consecutive submissions is roughly the difference in your source. That is a considerably easier conversation to have with a reviewer.

It is the opposite of anti-bot advice

Detection code wants per-build churn, because that is what makes a bypass perishable. Two different goals, so keep them in two different builds rather than compromising one configuration between them.

Attribution

What to do about a copy you cannot prevent

Since the artifact is published deliberately, the useful question is not how to stop a copy but how to prove one. That is a solved problem, and the answer is recorded in the build rather than enforced at run time.

Watermark the build

The tool embeds a tag with a keyed signature over it, so a copy recovered from a listing, a support ticket or a customer report can be checked against your key and attributed to the build that produced it. Producing a valid signature over a different tag requires the key, which is what makes the attribution worth something in a dispute.

Keep a manifest for the submitted artifact

The build manifest records source and output hashes per file. For a published add-in that is your record of exactly what a given version contained, which matters both for a takedown and for the far more common case of someone reporting a bug in a version you no longer have.

Frequently Asked

Marketplace add-ins and protected JavaScript, answered

What makes a marketplace add-in different from an ordinary web app?

Two parties sit between you and your user, and both of them read your code. A reviewer decides whether your submission may be published at all, and a host application decides what your code is allowed to do once it runs. Neither of those parties exists for a web application you deploy yourself. Everything unusual about protecting an add-in follows from that: a build setting that would be uncontroversial on your own site becomes something a reviewer has to form a view about, and a runtime guard that works on your own pages may be running inside a frame whose properties you did not choose.

Will a marketplace reject protected JavaScript?

That depends entirely on the marketplace, and it is a contractual question rather than a technical one, so confirm it with the specific programme before you build a release process around protection. What we can describe is the mechanism, which is consistent everywhere. Review works by reading code, whether by a human or by an automated scanner. Heavily transformed output looks structurally similar to what those scanners are trained to flag, because the techniques that conceal proprietary logic and the techniques that conceal a malicious payload are the same techniques. Expect the question to be asked, and prepare an answer rather than being surprised by it.

How do we keep member renaming from breaking the host API?

By treating the host's API surface as public interface and excluding it. Every add-in platform injects an object graph your code calls into, and every one of those names is fixed by the host rather than by you. Member renaming rewrites property access, so a blanket setting will happily rename a call into the host and produce a program that loads and then does nothing. Use the rule-based member selection rather than renaming everything, and keep the host namespace out of scope. The same reasoning covers any string the host matches on, such as command identifiers declared in your manifest.

Does domain locking work inside an add-in frame?

Usually yes, and this is one of the few distribution models where it means something. A hosted add-in is loaded from your own origin into a frame, so the guard reads your hostname and the allowlist matches. That genuinely constrains someone who copies your bundle onto their own site. The exception is a host that runs your code in a frame with no meaningful origin, written from a string or a blank document, because the guard treats an empty hostname as failure and fires the failure action before the allowlist is consulted. Test the guard inside the real host before you ship it, not on your development server.

Why does a fixed seed matter more here than anywhere else?

Because a reviewer compares your update against the version they already approved. Identifier generation is randomised per build by default, so two builds of the same source produce output that differs everywhere, and a diff between consecutive submissions shows a total rewrite rather than the three functions you actually changed. That is an expensive impression to give a reviewer on every release. Setting a fixed seed makes the output reproducible, so an update diffs to roughly the change you made. It is the opposite of the advice for anti-bot code, where per-build churn is the whole benefit, which is why the two should never share a build.

Do the runtime defense wrappers work in a host sandbox?

Frequently not, and the engine says so during the build. Three of them, the self-defending wrapper, the self-healing recovery wrapper and the anti-tampering monitor, work by evaluating code at run time, and the engine emits a warning that they need a policy permitting dynamic evaluation. Many add-in hosts forbid exactly that, in the same way a strict content security policy or an extension manifest does. Those three wrappers are also skipped automatically when the source is an ES module, with a warning, because they are classic-script wrappers and would break import linking. Read your build warnings before assuming an option is active.

What actually protects us from a competitor cloning the add-in?

Very little in the bundle, and the honest answer is that the defensible asset is usually somewhere else. An add-in is a thin client over your service, and what is worth copying is the behaviour rather than the code that requests it. If the valuable work runs on your server, a clone has to rebuild it. If it runs in the add-in, a determined competitor will reimplement it from watching the product rather than from reading your source, which protection does not slow down. Where protection does earn its place is against the copy-and-rebrand case, where somebody ships your bundle with a new name, and against casual modification of a paid feature check.

How do we prove a leaked copy came from a particular release?

With a watermark, which is the control designed for this and which survives redistribution. The tool can embed a tag with a keyed signature over it, so a copy recovered from anywhere can be checked against your key and attributed to the build that produced it. That is genuinely useful in a marketplace context, where your artifact is published by design and you have no way to restrict who downloads it. It proves origin rather than preventing copying, which is the correct thing to expect from it, and it pairs well with a build manifest that records the source and output hashes per file.

Should the add-in and our main web application share a build?

Keep them separate, for three reasons that all come from the host relationship. The add-in build wants a fixed seed for reviewable diffs, while your web application does not care. The add-in build has to exclude the host's API surface, which your web application does not have. And the add-in build may have to leave the evaluation-based wrappers off because the host forbids them, while your own pages can use them if your policy allows. One shared configuration ends up being the most restrictive of the three, applied everywhere, which is worse for both.

What does a sensible release process look like?

Five steps, in this order. Run every scan that works by reading code, including composition analysis and secret scanning, against the pre-protection artifact. Protect with a fixed seed and an exclusion list covering the host API surface and any manifest-declared identifiers. Load the protected build inside the real host application, not a local page, and exercise the paths that touch the host and the ones that touch the network. Keep the symbol map and the manifest for the exact submitted build, because a support report from a user of a published add-in is the least debuggable thing you will receive. Then submit, and keep the approved artifact so the next diff means something.

Related Guides

Protecting other JavaScript targets

Browser extensions

The closest neighbour: another reviewed, downloadable artifact, with a manifest regime that forbids dynamic evaluation outright.

Widgets and embedded SDKs

The other model where your code runs on a page you do not control, and where domain locking is the primary control rather than a secondary one.

Publishing an npm package

Distribution through a registry rather than a marketplace, with no reviewer but a much stronger expectation that consumers can read what they install.

Over-the-air updates

The other place per-build identifier churn has a concrete cost, this time in the size of the update rather than the patience of a reviewer.

Desktop applications

What changes when the host application is yours and the artifact lands on the user's disk rather than in somebody else's runtime.

Next Step

Three things to settle before your next submission

Write down every name your add-in receives from the host and every identifier declared in your manifest, and turn that list into your exclusion configuration — it is short, and it is the difference between a working release and a silent one. Pin a seed and check that two consecutive builds of unchanged source produce identical output. Then load the protected build inside the real host application and exercise the paths that talk to the host, because a development page proves nothing about a frame you did not configure.