How to automate svg metadata scrubbing for privacy compliance
- Step 1Understand the execution model — The scrubber is DOM-dependent, so it is in the runner's
headless-browserexecution mode and is not exposed as a server engine. JAD's API is upload-free: the/runendpoint returns400with pairing instructions on purpose. All real processing happens on your machine via the paired runner. - Step 2Install and pair the @jadapps/runner — Follow the runner docs to install and pair the runner with your account API key. Once running, it listens on
http://127.0.0.1:9789and can launch a headless Chromium session for DOM/Canvas tools like this one. - Step 3Fetch the tool contract — Call
GET /api/v1/tools/svg-metadata-scrubberwith your API key. The response describesexecution.runnerMode: "headless-browser",apiAvailable: false, an emptyoptionsarray (the scrubber has no options), and therunnerEndpointto target. This is how MCP/SDK clients build a payload. - Step 4POST each file to the local runner — For every SVG, POST the file payload to
http://127.0.0.1:9789/v1/tools/svg-metadata-scrubber/run. The runner drives the web tool in headless Chromium and returns the cleaned SVG (namedname-scrubbed.svg). Loop over your directory in a small Node or shell script. - Step 5Chain the minifier for full anonymity — Because the scrubber leaves XML comments and the prolog, follow it with the SVG Pro Minifier — which is a server-safe engine tool the runner can execute in-process — to remove the Illustrator generator comment and
<?xml?>line as part of the same loop. - Step 6Verify and gate the build — Add a CI assertion that the output contains no
xmlns:inkscape,<metadata,dc:creator,docname, orGeneratorstrings. Fail the build if any survive, so a regression in an export setting never re-leaks metadata to production.
Automation reality: API vs runner
Where each step actually executes. The key correction: there is no server-side scrub endpoint.
| Action | Endpoint | What happens |
|---|---|---|
| Fetch the tool schema | GET {origin}/api/v1/tools/svg-metadata-scrubber | Returns contract: runnerMode headless-browser, apiAvailable false, options [] |
| Try to run on JAD's server | POST {origin}/api/v1/tools/svg-metadata-scrubber/run | Always 400 Runner required — JAD never accepts uploads |
| Run via the paired runner | POST http://127.0.0.1:9789/v1/tools/svg-metadata-scrubber/run | Local headless Chromium drives the web tool; output streamed back |
| Run in the browser tab (manual) | /svg-tools/svg-metadata-scrubber | In-tab DOM processing; one file at a time |
Batch and size limits per tier
Per-file size limit and the browser batch limit. Note: the in-tab tool processes a single file; batch automation loops files through the runner.
| Tier | Per-file size limit | In-tab batch (this tool) |
|---|---|---|
| Free | 5 MB | 1 file at a time |
| Pro | 50 MB | 1 file at a time (loop in your script) |
| Developer | 2 GB | 1 file at a time (loop in your script) |
What the scrub removes, applied to every file in the batch
The fixed scrub set — identical for all files, which is what makes batch results consistent.
| Removed | Left in place |
|---|---|
All <metadata>, <title>, <desc> elements | XML comments (e.g. Adobe generator line) |
Root xmlns:dc/cc/rdf/svg/sodipodi/inkscape | <?xml?> declaration / prolog |
Root version, id, xml:space | viewBox, width, height, class, style |
Root data-*, inkscape:*, sodipodi:* attributes | Editor attributes on nested child elements |
Cookbook
The real automation shape. These are the calls that work — and the one that intentionally fails — so you build the pipeline correctly the first time.
The schema fetch (what you build payloads from)
The GET contract tells you the scrubber is runner-backed, has no options, and must be executed locally. Use it once at startup to confirm the runner mode.
$ curl -H "Authorization: Bearer $JAD_API_KEY" \
https://jadapps.com/api/v1/tools/svg-metadata-scrubber
{
"execution": { "runnerBacked": true, "apiAvailable": false,
"runnerMode": "headless-browser" },
"input": { "isGenerative": false, "acceptsMultiple": false, "options": [] },
"output": { "type": "svg" },
"minTier": "free"
}The call that intentionally 400s
Posting content to JAD's API never works — by design, no file ever reaches JAD. The response hands you the runner endpoint to use instead.
$ curl -X POST -H "Authorization: Bearer $JAD_API_KEY" \
https://jadapps.com/api/v1/tools/svg-metadata-scrubber/run
HTTP/1.1 400 Bad Request
{
"error": "Runner required",
"reason": "JAD's API/MCP never accepts uploads. Pair an @jadapps/runner …",
"runnerEndpoint": "http://127.0.0.1:9789/v1/tools/svg-metadata-scrubber/run",
"installRunner": "https://jadapps.com/docs/runner"
}Batch loop against the local runner (Node)
Pair the runner once, then loop your directory and POST each SVG to localhost. Files stay on your machine; the runner returns cleaned markup.
import { readdir, readFile, writeFile } from "node:fs/promises";
const RUNNER = "http://127.0.0.1:9789/v1/tools/svg-metadata-scrubber/run";
for (const name of await readdir("icons")) {
if (!name.endsWith(".svg")) continue;
const svg = await readFile(`icons/${name}`, "utf8");
const res = await fetch(RUNNER, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: svg, options: {} }),
});
const out = await res.text();
await writeFile(`dist/${name.replace(/\.svg$/, "-scrubbed.svg")}`, out);
}Scrub then minify in one pass
The scrubber leaves comments and the prolog. The minifier is a server-safe engine tool, so the runner can run it in-process — chain them for fully anonymous output.
// after the scrub loop, run the minifier on each scrubbed file:
const MINIFY = "http://127.0.0.1:9789/v1/tools/svg-pro-minifier/run";
const res = await fetch(MINIFY, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: scrubbedSvg, options: {} }),
});
// minifier drops <!-- Generator -->, <?xml?>, inter-tag whitespaceA CI gate that fails on leaked metadata
Assert the published output contains no fingerprints. This catches a designer changing an export setting before it reaches production.
# scripts/check-svg-clean.sh LEAK=$(grep -rlE 'xmlns:(inkscape|sodipodi|dc)|<metadata|dc:creator|docname|absref|Generator' dist/*.svg || true) if [ -n "$LEAK" ]; then echo "Metadata leaked in:"; echo "$LEAK"; exit 1 fi echo "All SVGs clean."
Edge cases and what actually happens
Calling JAD's `/run` endpoint expecting it to scrub
400 by designPOST /api/v1/tools/svg-metadata-scrubber/run always returns 400 Runner required. JAD's API never receives uploads — this is the privacy guarantee, not an outage. The response body includes the localhost runnerEndpoint you should call instead. Build against the runner from the start.
Assuming the scrubber runs as a server engine
Not supportedThe scrubber needs the DOM parser, so it is in headless-browser execution mode and is excluded from the server-safe engine set. The runner handles it by launching headless Chromium locally. Engine-mode tools (like the minifier) run in-process; this one does not.
Runner not installed or not paired
Pairing requiredWithout a running, paired runner on 127.0.0.1:9789, there is nowhere to execute the batch. Install and pair it via the runner docs. The web tool still works manually in a browser tab for one-off files.
Author name survives because it is in a child `absref` or comment
VerifyThe scrub set removes <metadata> (author records) but not comments or child-element attributes. An absref path on a nested <image> or an author in a comment will survive. Chain the minifier and run the CI grep gate to catch these before publishing.
File exceeds the tier size limit in the loop
413-style blockEach file is bounded by your tier's per-file limit (5 MB free, 50 MB Pro, 2 GB Developer). An oversized file — usually one with embedded base64 raster — is blocked. Externalise large embedded images or upgrade the tier.
Rate limit on the schema endpoint
429The GET /api/v1/tools/... schema endpoint is rate-limited per API key per hour. You only need to fetch the contract once at startup, not per file. The actual scrubbing happens on the local runner and is not subject to that hourly API rate limit.
Idempotency on re-runs
SupportedRe-scrubbing an already-clean file is a safe no-op: there is no <metadata>/<title>/<desc> left and no editor attributes to remove, so output equals input. This makes the step safe to run on every build without tracking which files were already processed.
Mixing in non-SVG files in the batch directory
Skip themFilter your loop to .svg files. Feeding non-SVG content yields a parser-error document with nothing useful to scrub. The example loop above already guards with an extension check.
Embedded raster EXIF needs separate handling
Out of scopeGDPR-relevant EXIF (GPS, timestamps) inside embedded <image> rasters is not SVG metadata and is not removed by this tool. Strip EXIF from photos before embedding, as a separate pipeline step.
Frequently asked questions
Can I scrub SVG metadata with a single JAD API call?
No — and this is the most common misconception. JAD's API is upload-free: POST /api/v1/tools/svg-metadata-scrubber/run always returns 400 Runner required. Automation runs through a paired local @jadapps/runner, which executes the tool on your own machine. The API only serves the schema, not the transform.
Why isn't the scrubber available as a server-side endpoint?
It depends on the browser DOM parser to walk and rewrite the SVG, so it cannot run in the edge engine. It is flagged for the runner's headless-browser mode, meaning the runner spins up a short-lived Chromium locally to do the work. This also means your files never leave your network.
How do I run it in CI then?
Install and pair the @jadapps/runner on the CI machine, fetch the contract from GET /api/v1/tools/svg-metadata-scrubber, then loop your .svg files and POST each to http://127.0.0.1:9789/v1/tools/svg-metadata-scrubber/run. The runner returns the cleaned markup.
Does the scrubber have any options to pass in the payload?
No. Its option schema is empty — the scrub set is fixed. Your payload only needs the SVG content (and an empty options object). That is what makes batch results perfectly consistent across an icon library.
Is this enough for GDPR data-minimisation?
It handles the SVG-level personal data: author names in <metadata>, plus document/layer names and editor fingerprints. It does not remove XML comments, child-element file paths, or EXIF inside embedded rasters. Pair it with the SVG Pro Minifier and a CI grep gate, and strip raster EXIF separately, for a defensible pipeline.
Do my files get uploaded to JAD during a batch?
No. The runner executes locally on 127.0.0.1:9789 and the JAD API refuses uploads by design. The author records and paths you are removing for compliance never transit JAD's servers — which is the whole point.
How large can each file be?
5 MB on free, 50 MB on Pro, 2 GB on Developer, per file. Most SVGs are far under that; oversized ones usually embed large base64 rasters that you should externalise.
Is the operation idempotent for repeated builds?
Yes. Re-running on an already-scrubbed file finds nothing to remove and returns it unchanged, so you can run the step on every build without bookkeeping. No risk of progressive degradation.
What about the Illustrator generator comment in a batch?
The scrubber leaves it (it does not touch comments). Add the SVG Pro Minifier — a server-safe engine tool — to the same loop to drop the comment and the <?xml?> prolog from every file.
How do I prove the output is clean to an auditor?
Run a CI assertion that greps the published dist/*.svg for xmlns:inkscape, <metadata, dc:creator, docname, absref, and Generator, and fail the build on any match. Keep the build log as evidence that every published asset was scrubbed.
Does the hourly API rate limit affect my batch throughput?
Not the scrubbing itself. The hourly per-key rate limit applies to the JAD API schema endpoint, which you call once at startup. The per-file scrubbing happens on your local runner and is not bounded by that limit.
Can I combine scrubbing with other SVG cleanup in the pipeline?
Yes. A common pre-publish chain is scrub → minify → precision-tune → purge unused defs. The engine-mode tools run in-process on the runner; the scrubber runs in its headless session.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.