Where can it go?
Only where it was allowed to. The browser fetching your URL sits inside the box holding your state and your key, so every address is judged before it moves and after it lands.
lib/urlguard.cjs · assertSafeUrl
Data privacy and security are fundamental to our mission, and to how this product is built. Every boundary below names the code that enforces it, and where a guarantee stops, this page says where.
Last updated 16 August 2026
A check is something a new route can forget to write. A directory is not. Two cross-tenant defects here had exactly the shape that choice exists to prevent.
Only where it was allowed to. The browser fetching your URL sits inside the box holding your state and your key, so every address is judged before it moves and after it lands.
lib/urlguard.cjs · assertSafeUrl
The account that filed it, and nobody else. Scoped by directory rather than by a filter each route has to remember, so there is no query to forget.
lib/tenant.cjs · stateDir
Yours, and never another customer’s: a file only you can read locally, and hosted, spent only on runs you started. Plan usage with no key of your own falls back to ours, metered.
lib/workspace.cjs · envVarsFor
The two that read and reason send what a model has to look at: your page text, and the frames the browser captured. The other three send findings or nothing.
Not a summary of the rules. Every one of the 256 IPv4 blocks asked directly, sixteen times each, and coloured by the answer that came back.
Pointing a browser inward is how a testing tool becomes a way to read the machine it runs on. The path traced below is a real refusal.
A public hostname is free to resolve into private space, as often as its owner likes. So nth Labs judges every address the name resolves to, and one private answer refuses the run.
Only http and https reach anything. file:, gopher: and data: are refused outright: they are attempts to read the machine nth Labs runs on, not a product to test.
A hostname is not the target. The addresses behind it are, and a perfectly public name is free to resolve into private space, so the check never judges the text you typed.
Not the first one. A name that returns one public address and one private one would otherwise be a coin flip its owner gets to flip.
After every navigation the address is judged again, before the capture runs. Staying on the origin is allowed; leaving it for a private address is refused.
Where this stops. A DNS answer that flips between the check and the fetch is not caught: the browser driver does not expose the socket. The redirect case is, and test/browser/security.cjs proves it.
Five notations of the cloud metadata endpoint, the whole prize in an SSRF. The guard parses addresses into bytes rather than matching text, because a regex loses to a spelling nobody thought of.
Link-local, multicast and the unspecified address stay refused even when private space is deliberately opened, because nobody has ever legitimately pointed a product audit at the metadata endpoint.
Local mode is somebody testing localhost:3000 on purpose, and refusing it would break the main way the tool is used. Hosted mode is a stranger on somebody else’s box, so private space closes.
The two modes are not a setting apart. They are different threat models, and the guard is stricter in the one where the URL came from a stranger.
The console binds to the loopback interface, and hosting is a mode you turn on rather than a config value you can fat-finger. Your findings and your key are files only you can read.
The URL is untrusted input from any account, so private space closes and every address behind the name has to be public. Your findings and your key sit in your own account directory.
Where this stops. Neither your model key nor your product’s sign-in password is encrypted at rest; owner-only permissions and the account directory protect them. Give the console a test account you can throw away.
In depth
What the code does, by area. Every claim here is a line in a file you can read.
→What we store, and for how longA run dereferences a URL somebody else typed, in a browser, inside the box that holds every other account’s state. That is the whole threat model.
Scoping is a boundary rather than a filter each route has to remember. Two cross-tenant bugs here had exactly that shape, and both were routes that forgot.
Where this stops: neither your model key nor your product’s sign-in password is encrypted at rest. Owner-only permissions and the account directory protect them. Give the console a throwaway test account.
Six of the eight checks never send your product anywhere. Two of them do, and the page says which, because a blanket privacy claim over a tool that calls a model is not true.
=== leaks
the prefix through how long the comparison took.The product’s pitch is that a claim nobody re-tested is not evidence. The same rule is turned inward here.
The endpoint that matters here is the browser a run steers, because that is the thing holding your product’s session.
Run it yourself and the answer is your disk, in whatever country your disk is in. The hosted console is the case worth stating.
The full table, including the lawful basis for those transfers, is on the privacy page.
No SOC 2 report, no ISO certificate, no independent penetration test, and no third-party audit of anything above. There is nothing to point at yet, and we would rather write that than imply one.
Neither key material nor stored passwords are encrypted at rest. There is no bug bounty. If you need either before adopting a tool, we would rather you knew now.
Found a way into somebody else’s data here? hello@nthlabs.dev, the same address the privacy page gives. We will answer, and we will say what we fixed.