Commercial

Source code escrow and technical due diligence when your JavaScript is obfuscated

Two requests arrive at software companies with enough enterprise customers, and both cause a moment of alarm in teams who protect their client-side code. A customer's procurement department wants a source code escrow agreement. An acquirer's technical reviewer wants to read the codebase. Someone asks whether obfuscation has made either of these impossible, and the honest answer is that it has not — but the reason why is worth understanding properly, because the ways teams get this wrong are expensive.

The confusion, stated plainly

Obfuscation is a build step. It runs after the source is written, and it transforms the artifact you ship — not the artifact you keep. Your repository is unchanged. Escrow and diligence both operate on the repository.

Put that way it sounds trivial, and structurally it is. The failures are not conceptual, they are procedural: someone deposits the wrong files, or the deposit is never verified, or a diligence reviewer is handed a bundle instead of a branch. Each one is avoidable by knowing in advance which artifact belongs in which conversation.

What escrow is actually for

A source code escrow agreement exists to answer a continuity question. Your customer has built a process on your software. If you cease trading, get acquired and sunset the product, or simply stop supporting the version they run, they need a path that does not end with their operations stopping. A neutral agent holds a deposit, and on a defined release event — insolvency, breach, discontinued support — the customer receives it.

The purpose is continuity, not inspection. That single fact settles most of the confusion, because it tells you what the deposit must contain: whatever a competent engineer needs to rebuild, run and maintain the product without you. A protected bundle satisfies none of that. It cannot be maintained, and the customer already has it — it is what you shipped them. Depositing protected output is the one genuinely serious mistake available here, and it happens when someone treats the deposit as an archive of the release rather than as the means of rebuilding it.

What belongs in the deposit

Write the list once and make it part of your release process, because a deposit that ages is close to worthless:

  • Unprotected source, at the commit corresponding to the released version.
  • Build and protection configuration — your config file, the option set, and any per-environment variations.
  • Pinned dependencies, including the lockfile, so the build resolves the same way years later.
  • Build instructions written for someone who has never seen the project, including toolchain and runtime versions.
  • The protection tool version and a note on how a beneficiary would obtain a licence for it. A deposit that cannot complete its own build step is a deposit that fails at the moment it matters.
  • The release manifest and signature for the shipped build, which is what makes the deposit verifiable rather than merely present.

That last item is the one most deposits omit, and it is the one that turns escrow from a filing exercise into something with evidential weight.

Verification, and the polymorphism problem

Serious escrow arrangements include a verification tier: the agent takes the deposit into a clean environment, follows your instructions, and confirms a build completes. Higher tiers compare the result against what customers actually received.

Protection introduces a specific obstacle here that will fail a naive verification, so it is worth pre-empting. Protected output is polymorphic by default — the same input produces different bytes on every run, deliberately, so that every release differs from the last. An agent who rebuilds from your deposit and hashes the result will get a value that does not match your release, and will report a discrepancy. Nothing is wrong; they have measured the feature.

The fix is determinism. Supply a seed, and the same input, options and seed produce byte-identical output. Record the seed for each release alongside its version tag, include it in the deposit, and the agent's rebuild becomes directly comparable to the outputSha256 values in the manifest that shipped with that release. Verification stops being a judgement call and becomes arithmetic. Reproducible builds covers what you trade for that determinism — briefly, per-release variation between builds, which for an escrow deposit is a cost worth paying.

The end-to-end chain is then genuinely strong, and unusually so for a client-side product:

  • The deposit contains source, config and seed.
  • A seeded rebuild reproduces the release byte for byte.
  • The manifest records a SHA-256 for every source and output file.
  • A signed attestation over the build identifier, the polymorphism fingerprint and those hashes proves the release came from you.
  • The customer can re-hash the files in their own production directory and compare.

A vendor who can demonstrate that chain is answering a question most software companies cannot answer at all. It is worth raising proactively during procurement rather than waiting to be asked.

Due diligence is a different conversation

An acquirer's or investor's technical reviewer is not asking a continuity question. They want to assess code quality, architecture, test coverage, open-source licence hygiene, security posture and how expensive the codebase will be to maintain. None of that is visible in protected output, and none of it is meant to be.

So give them source, under an NDA, in a controlled environment — read-only access to a repository, or a data-room export, with someone available to answer questions. This is entirely standard and nobody involved will find it unusual.

What is unusual, and reliably damaging, is offering a protected bundle. It reads one of two ways to a reviewer: either you are obstructing the review, or you cannot produce your own source. Both are worse for the transaction than anything they would have found by reading the code. Diligence reviewers are experienced at spotting evasion and are professionally obliged to treat it as a finding.

One point worth making early and confidently: your protection setup is a positive in diligence, not a liability. A build pipeline that protects the shipped artifact, produces a signed manifest, audits for leaked source maps and can reproduce any historical release is evidence of engineering maturity. Present it as part of your release process, with the configuration and the reasoning. The reviewer's concern is never that you protect your code; it is whether you can still build, debug and support it — which is precisely what the report archive and symbolication demonstrate.

When the two conversations collide

Occasionally a customer's technical reviewer looks at what you deployed, finds unreadable JavaScript, and raises it alongside the escrow discussion. These are separate issues and answering them as one produces a muddle.

Escrow is procurement asking about continuity. The unreadable-files question is a security review asking whether obfuscated code in their environment is a risk — a question with its own well-worn answer involving the signed attestation, the manifest, a statement that no source maps ship, and a named contact. When a penetration test flags your obfuscated JavaScript covers that response, and the security evidence hub assembles the artifacts. Handle them as two threads with two audiences, and both close faster.

A checklist before you sign anything

  • Confirm the agreement's definition of “source code” explicitly excludes protected output, so nobody can later argue the shipped bundle satisfied the obligation.
  • Decide the deposit cadence — per release is ideal, quarterly is defensible, once at signing is theatre.
  • Include the seed and the release manifest with every deposit.
  • Check whether the verification tier the customer bought actually requires a rebuild, and rehearse it internally before the agent attempts it.
  • Name someone responsible for updating the deposit, or it will be three years stale on the day it is released.
  • Keep the protection report for every release, archived and labelled, so a beneficiary or a future reviewer can decode a stack trace from a build nobody remembers.

None of this is made harder by protecting your JavaScript. It is made harder by treating the shipped artifact and the source as interchangeable — which is the same confusion, in a different suit, that leads teams to publish source maps in production and wonder why their obfuscation stopped working.

Frequently asked questions

Does obfuscating our JavaScript conflict with a source code escrow agreement?

It should not, because the two operate at different stages. Escrow holds source code; obfuscation is a build step applied after that source is written. The deposit contains your unprotected source plus the instructions to build it, exactly as it did before you adopted protection. A conflict only appears when someone deposits the protected output by mistake, which defeats the purpose of the agreement entirely.

What exactly should go into the escrow deposit?

Everything a competent engineer would need to rebuild and run the product without you. Unprotected source, the build and protection configuration, pinned dependency versions with a lockfile, documented environment requirements, and written build instructions. Add the protection tool version and how a licence for it would be obtained, since a beneficiary who cannot run your build cannot reproduce your release.

How can a verification agent prove the deposited source produces the shipped files?

Give them a deterministic build. Protected output is polymorphic by default, so two runs of the same input differ and a naive hash comparison always fails. Supplying a seed makes the same input, options and seed produce byte-identical output, so the agent can rebuild from the deposit and compare hashes against the manifest that accompanied the release. That turns verification from a judgement call into an arithmetic one.

Should we give an acquirer's technical reviewer the obfuscated bundle?

No. A diligence reviewer is assessing code quality, architecture, licence hygiene and maintainability, and none of that is legible in protected output. Handing over a protected bundle reads either as obstruction or as an inability to produce your own source, and both are worse for you than the review itself. Diligence runs on source under an NDA, in a controlled environment.

Can we prove to a customer that the files they run match what we deposited?

Yes, and it is the strongest version of this argument. Sign the release: the CLI writes an attestation over the build identifier, the polymorphism fingerprint and a SHA-256 for every output file. The customer verifies that signature against your published public key and can re-hash the files on disk. Combined with a seeded rebuild from the deposit, the chain runs from escrowed source to the exact bytes in their production directory.

Does a customer asking for escrow mean they distrust obfuscation?

Usually not. Escrow answers a continuity question — what happens to us if this vendor stops existing — and it long predates any concern about readability. Treat the request as ordinary procurement rather than as an objection to your protection choices. The two conversations only merge when the customer's technical reviewer separately flags the unreadable files, which is a different discussion with a different set of evidence.

Related reading