Watermark verifier

Paste a protected JavaScript file. If it carries a JSO watermark, this page extracts the embedded tag. Supply the HMAC key (or set it in JSO_WATERMARK_KEY in your own tooling) to validate the signature. Everything runs in your browser via SubtleCrypto. Nothing leaves the page — no upload, no JSO involvement, no server round-trip. Open DevTools · Network and confirm.

Both fields stay client-side. The key is held in DOM state only and never serialized off-page.

Paste a protected file and click Verify.

Input · protected JavaScript

Paste up to ~5 MB

Output · parsed marker

Awaiting input
// Parsed marker fields appear here after Verify is clicked.

What this verifies

  • Marker presence: scans for the /*! __jso_watermark_v1 */ block at any position in the file.
  • Tag readability: decodes the base64url tag back to its plaintext form (UTF-8).
  • Signature validity: when a key is supplied, recomputes HMAC-SHA256(key, tag) and compares against the embedded signature in constant time.

Identical semantics to jso-protector --verify-watermark. The wire format is documented at WireFormat.aspx and the Node + Python + .NET clients all share the same regex and HMAC rules — this page is the fourth reference implementation, written in vanilla browser JS.

Why client-side?

A watermark verifier you upload your protected files to is a verifier that can lie about the result. By doing the entire HMAC check in your browser via SubtleCrypto.sign(), the trust boundary is your browser process, not our infrastructure. This page sends zero bytes off your machine: open DevTools · Network before clicking Verify and confirm.

What a watermark is for

A watermark does not stop anyone doing anything. It answers a question after the fact: whose copy is this? That makes it an attribution control rather than a protective one, and it is worth being precise about the difference, because the two get conflated in vendor material constantly.

The cases where it earns its place:

  • Per-licensee builds. Stamp each customer’s build with their identifier. When a copy turns up somewhere it should not be, the tag tells you which licence it came from — which is the fact a commercial conversation actually needs.
  • Release provenance. Stamp a version and commit identifier so a file recovered from production can be matched to the build that produced it, without relying on filenames or deploy logs.
  • Incident triage. When a customer reports a bug in a protected file, the tag identifies the exact build to reproduce against, which is otherwise guesswork once several releases are in the field.

The signature is what separates this from a comment. Anyone can copy a plaintext marker onto a different file; only the holder of the HMAC key can produce a tag that verifies. That is why the tag and the signature travel together, and why the key never leaves your side — including on this page, which computes the HMAC in your browser.

Operating the key

The verification property is only as good as key handling, and there are three rules that matter:

  • One key per product line, not per build. The tag carries the per-build detail; rotating the key per build means you can no longer verify last month’s artifact.
  • Never ship the key. It belongs in CI secret storage and in whatever tooling verifies. If it ends up in the protected bundle, the signature proves nothing.
  • Record which key signed which release. After a rotation you need to know which key to verify an older artifact with, and that mapping is not recoverable from the file.

Note that a watermark is trivially removable by anyone editing the file — it is a comment block. That is not a flaw, because removal is not the threat it addresses: a leaked copy with the marker stripped is still a leaked copy, and the person who stripped it has demonstrated intent. If you need to know when a shipped file has been modified at all, that is integrity monitoring, a different control with a different failure mode.

Next steps

  • Bulk-scan a directory tree: npx jso-protector --scan-watermarks dist/ --watermark-key $KEY
  • Stamp during build: npx jso-protector --watermark $TAG --watermark-key $KEY
  • Pair with signed release attestations for end-to-end build provenance.

Frequently asked questions

What does this page actually check?

Three things, in order. It scans the pasted file for the watermark marker block, which may sit anywhere in the file. It parses the embedded tag and signature out of that block. Then it recomputes the signature from the tag and the key you supply and compares the two. A pass means the tag was produced by someone holding that key; a parse without a matching signature means the marker is present but was not signed by it.

Why does the verification run in my browser rather than on our server?

Because a verifier you upload your protected files to is a verifier that could report whatever it liked. Doing the whole check locally means the result does not depend on trusting us: the file never leaves the page, the key is held in page state and is not serialised anywhere, and you can read the code doing the work. For a control whose entire purpose is establishing provenance, being able to confirm the answer yourself is the point.

How much does a watermark change what someone can do with a copy?

Nothing at all, and it is not meant to. It does not prevent copying, reading, modifying or redistributing anything. What it does is answer a question afterwards: whose copy is this. That makes it an attribution control rather than a preventive one, which is a genuinely useful thing to have and a completely different thing from protection. Judge it on whether it helps you act after a leak, not on whether it stops one.

Can someone simply delete the marker from the file?

Yes, trivially, because it is a comment block. That is not the flaw it first appears to be, because removal is not the situation the mechanism addresses. A copy with the tag stripped is a copy that can no longer be attributed to anyone, which does the remover no good if the question is whether your build is the source of what turned up. The property being relied on is that a valid signature is hard to forge, not that the marker is hard to remove.

How should we handle the signing key?

Three rules cover nearly all of it. Use one key per product line rather than one per build, because the tag already carries the per-build detail and rotating too often means older releases can no longer be checked. Never ship the key: it belongs in your continuous integration secret storage and in whatever tooling verifies, and a key that ends up inside a protected bundle proves nothing about anything. And record which key signed which release, since after a rotation that mapping is not recoverable from the artifacts themselves.

What separates this from writing a plain comment into the file?

The signature. Anyone can copy a plaintext marker from one file and paste it onto another, so an unsigned tag tells you only what someone wanted you to believe. Producing a valid signature over a different tag requires the key, so a marker that verifies against a key only you hold is evidence rather than a label. That is the whole difference, and it is why the key handling below matters more than the marker format.