Margyn
Colour theme
Get Watch

Your code never leaves your machine. There is nowhere to send it.

This page lists every process the scanner starts, every file it writes and every network call it makes. The last list is empty.

There is no hosted scan. There is not going to be one

A hosted scanner would need read access to your repository. We would then be holding your source plus a token wide enough to fetch it, which is a security story we are not in a position to defend. So the scan stays on your side of the line.

That is not a promise, it is a missing route. The deployed worker serves the page, sign in, checkout and the licence endpoint. Check it yourself:

$ curl -s -o /dev/null -w '%{http_code}\n' -X POST https://margyn.xyz/api/scan
404
$ curl -s https://margyn.xyz/api/config
{"snippetId":"...","sandbox":true,"products":[...],"scan":false}

The local development server does have a scan route, because there the caller and the repository are the same machine. On a public host that route would take a filesystem path from a stranger and run git against it, which is a filesystem probe wearing a product's clothes.

Every process the CLI starts

git ls-filesLists what the repository actually tracks. Every check starts here, which is also why Margyn needs a git repository rather than a directory.
git check-ignore -vAsks git which rule excludes a file, so the finding can name the rule and the line instead of guessing.
your test commandOnly under --mutate. It is whatever npm test resolves to in the repository being scanned. Nothing is inferred and nothing else is executed.

That is the whole list. Files are read with node:fs, capped at 400 KB each, skipping .git, node_modules, build output and virtual environments. The commands printed inside a finding, including git archive, are text for you to run. The scanner does not run them.

What it writes, what it does not

With no flags: nothing. The four static checks are read only.

Under --mutate: one file at a time, inverted at one line, only after your suite has passed unmutated. The original is restored in a finally block and on SIGINT, so a scan you interrupt with ctrl-c cannot leave a mutated tree behind. A red baseline aborts the whole check with the reason rather than producing results that would mean nothing.

It never writes to your home directory, never installs anything and never edits a config file. It reads a licence from ~/.margyn/licence if one is there.

Network calls: none

The scanner opens no connection at any point, including for billing. There are no runtime dependencies, so there is no transitive package to audit either. It is one directory of ES modules, which means reading it before you run it costs minutes rather than an afternoon.

$ grep -rnE 'fetch\(|node:http|node:https|axios' src/
(no output)

The licence is signed, not phoned in

The server signs a short payload with Ed25519. The CLI verifies it against a public key compiled into the source, so a paid check runs on a CI runner with no network access at all. The private key lives only in the server environment and is not in the repository.

Four attacks are tested rather than asserted, using signatures from the real signer: a flipped signature byte, a payload swapped under a valid signature, an expired licence and a licence for a different product. Each is refused with the reason named, because "your licence expired" and "your licence was tampered with" are different conversations.

A licence carries the product names, the account email, the issue time and the expiry. It is signed rather than encrypted, so treat it as readable by anyone who holds it.

How a licence is installed · What is inside one

The hosted half

SecretsSet once per environment with wrangler secret put and never committed. The API key and the signing key exist only in the worker environment.
/api/configReturns the public snippet id, the environment flag and the product list. The API key is absent from it by construction rather than by filtering.
Token exchangeThe browser gets a short lived verification token from the sign in SDK. Exchanging it for a user happens server side, so the secret key never reaches a page.
Request bodiesCapped at 64 KB before parsing. An unbounded parse on a worker billed for CPU is somebody else's denial of service on our account.
CardsHandled by Tiun. We never see a card number.

Reporting something

Use a private security advisory for anything exploitable, so it stays out of public view until it is fixed. Everything else can be an issue.

Two things this page will not claim. Release tags are not signed yet, so verify the published package against the repository rather than against a signature. There has been no third party audit. There will not be a badge here pretending otherwise.