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.