J0 beta

How it works.

Six mechanisms, and the limit that sits next to each one. If a claim on this page is not true of the build, the page is the thing that is wrong.

The proxy, and the certificate

J0 starts listening on 127.0.0.1:9090 as the window opens. Burp uses 8080; J0 uses 9090 so the two can run side by side while you are still deciding. There is no start button to find and no port to choose.

Interception needs a certificate authority. J0 generates one on your machine the first time it runs and serves it from a landing page at the proxy’s own address, in both PEM and DER. It is never installed into a system trust store for you — trusting a CA is a decision, and software that makes it on your behalf has taken something it was not offered.

Upstream TLS verification stays on. An intercepting proxy that silently accepts a bad certificate on the far side is teaching you that the far side is fine.

The limit The proxy speaks HTTP/1.1. The Repeater can speak HTTP/2, but the interception path cannot yet, and HTTP/3 is not implemented. Chaining through an upstream proxy is not implemented either.

What byte-faithful means here

Duplicate headers stay duplicated. Duplicate parameters stay duplicated, in order. Binary bodies stay binary, unusual encodings survive, and cookies are not re-serialised on their way through. Anything J0 normalises or reconstructs is kept apart from the original bytes, and the interface can show you both.

This is not fussiness. A tool that quietly canonicalises a request is reporting on a request it did not send, and the moment that matters is the moment a finding turns on an encoding.

Bodies are content-addressed in the project file, so the same response captured four hundred times is stored once — faithfully, not approximately.

Whose session was that

Browse normally, as each of your accounts. J0 groups requests by the credential set that carried them and gives each group a label, a role and an organisation. There is nothing to configure and no cookie name to type, including the common case where two accounts arrive with differently named cookies.

It records cookie and header names. It does not record values, anywhere, and no part of the interface will show you one.

Values that rotate per request or stay constant across every account — load balancer cookies, device identifiers — are dropped before grouping. Without that step a single login fragments into hundreds of accounts and the whole analysis becomes noise.

The limit Attribution is a heuristic over what was captured. It can be corrected by hand, and the correction is what the analysis then uses.

Sending as somebody else

A cross-account test replaces the whole credential set, not just a cookie: cookies, authorization headers, body tokens, and anti-forgery tokens in all three carriers they travel in. Swapping a session cookie while leaving a stale CSRF token behind produces a rejection that looks exactly like a control holding, which is the most expensive false negative in this class of work.

Every send in the application goes through one function in jo-proxy. That includes the scanner, the Repeater, the Intruder and anything an agent asks for. One chokepoint means one place where scope, method and destructive-action guardrails are enforced, and no second path around them.

Guardrails Out-of-scope hosts are refused. Automated replay is read-only unless you say otherwise. A refusal is recorded with its reason and appears in the report — it never looks like a test that passed.

The verdicts, in the words the application uses

Five outcomes, and the two that are not findings matter as much as the one that is.

got through The other account’s data came back. The report carries how many of the owner’s identifying values appeared, out of how many existed.
blocked Refused, and the denial matches the fingerprint you declared. The control held. It is not coloured as a success, because the absence of one bug is not a result.
own data A 200 that returned the actor’s own records. The query is scoped. A tool that stops at the status code calls this a finding.
already had access The account could reach that object anyway, so the test proves nothing either way. Stated, not hidden.
not sent No comparison was possible — a guardrail stopped it, or there was nothing to compare. There is no verdict to give, and the row says so rather than disappearing.

Every number carries its bar

“Similar in shape and length” is the sentence that gets a report sent back. J0 carries the measurement that settled a finding all the way into the write-up: the size gap, the threshold it was weighed against, where that threshold came from, and how many of the owner’s identifying values came back out of how many there were.

The threshold is learned per endpoint from its own repeated reads. An endpoint that swings 19 % between two identical requests is not judged by the same bar as one that is byte-stable. Resemblance is never decisive on its own.

Before anything is reported, three probes run: an identifier that belongs to nobody, the same request repeated, and the request with every credential stripped. Then one more question — could this account have obtained that identifier at all? J0 searches the replies the account genuinely received and reports how many contained it. A searched silence, not an assumed one.

What a finding carries numbers, with provenance
body similarity 93% size gap 4% vs 19% learned owner values 3 of 17 id discoverable 2 of 94 replies

Saying what was skipped

Coverage is an output, not a footnote. Each class of check reports tested, not measured, or refused, with the reason attached — and not measured is never rendered as a pass anywhere in the application.

The same rule governs an agentic run. Every feature it walks is accounted for as tested, skipped or never reached; a refusal to touch a destructive control is written down; and a feature that broke the crawl is recorded as one rather than quietly dropped.

Findings are bucketed by what to do about them — dig, candidate, not applicable, merged — and never scored. Severity is demonstrated impact, and only somebody who knows what the data is can judge that.

Where a model stops

The agentic layer is optional and off until you turn it on. A model chooses which feature to look at and writes up what came back. It does not compose request bytes, it does not choose a payload, and it is not handed a credential value — the tool API has no shape that could return one.

That boundary is a type in the Rust engine. The send function accepts only a value that the traffic check has already minted from real captured traffic, so a string a model invented cannot be passed to it: the program would not compile. It is not a rule in a prompt that a clever prompt can talk its way around.

Live sending needs an explicit toggle. The click policy refuses destructive controls. Page reads are redacted — tokens and JWTs scrubbed, object identifiers and UUIDs deliberately kept, because those are the thing under test. Every agentic request appears in the history marked as one, with the account it ran as.

The prompt itself An editable tree of files, one source for every driver. The boundary clauses are not in that tree and cannot be edited out — they are compiled in, and a test fails the build if any driver loses one.
A control to compare against Every run is recorded with its brain, scope, ending and the engine’s own before-and-after counts — the deterministic engine included, because a control that is not recorded is not a control.

Project files

A chooser opens on every launch, Burp-style, and projects can be switched while the application is running. A project is a single .jo file: SQLite, with content-addressed bodies. History survives a restart because it was never in memory only.

Captured traffic lives in that file and nowhere else. The repository’s .gitignore excludes .jo files and their sidecars, along with keys, certificates and HAR exports, so a project cannot be committed by accident.

Questions

Is this a Burp replacement?

Not yet, and it is honest to say so. Phases 1 to 5 of the specification are built: proxy, storage, the manual workbench, the detection engines and the agentic layer. Burp has twenty years of extensions and a scanner J0 does not try to duplicate — J0 deliberately stays off payload scanning and spends its effort on authorization, which is the class the scanners skip.

Does anything leave my machine?

Not unless you configure a hosted model and turn it on. There is no account, no telemetry and no update check. With a local model, nothing leaves at all.

Which model does it use?

Whichever you point it at: Claude Code, any OpenAI-compatible endpoint including a local one, or none — the deterministic engine runs the same scan without a model. Keys are entered in Settings and are never written into the repository or a log.

Can I use it on a client engagement?

It is Apache-2.0 and there is nothing stopping you, but read the features page first: this is a beta verified on one operating system, and the Linux and Windows jobs in the CI matrix have not been run.

Why is it called J0?

The repository is JZero and the specification calls the project Project Jo. The application shows J0, which is what fits in a title bar.