A web browser built from the bottom up by AI, using formal verification instead of trust.
- Memory safe: 100% safe Rust except
clazel-sys: 14unsafeblocks (20 lines) that enter the OS sandbox, machine-checked and audited. - Verified where it matters: the kernel's security policy and every parser of untrusted input carry machine-checked proofs (Verus, Kani).
- Honest about it: each crate declares its verification tier, and the trusted computing base is listed in docs/VERIFICATION.md.
Status: Phases 0 (foundations) and 1 (verified primitives) are complete:
UTF-8 (clazel-encoding), percent-encoding (clazel-percent), forgiving
base64 (clazel-base64), MIME types and sniffing (clazel-mime), IDNA
(clazel-idna) and URLs (clazel-url) are at Tier 2, on macOS and on
Linux CI. Phase 2 (kernel and IPC) is in progress; see the table below and
docs/phases/phase-2.md. Phase 3 (networking) has
HTTP field syntax, HTTP/1.1 response framing and request serialization
(clazel-http), DNS (clazel-dns) and the request builder (clazel-fetch)
implemented at tier 0, target tier 2, and a minimal curl clone:
clazel fetch URL does one http:// GET through our own resolver and the
proven URL parser, request builder and response parser, and prints the
response. It has no TLS, redirects or cookies yet. The TLS work
(DER, RSA PKCS#1 v1.5 verification and TLS 1.3 message formats) is parked on
the phase3/tls branch until it passes CI. Full CI verification is pending;
the remaining networking work is in
docs/phases/phase-3.md.
Recheck every proof yourself, on Linux x86-64, with:
docker build --platform linux/amd64 -t clazel-verify .
docker run --rm --platform linux/amd64 clazel-verifySee docs/ROADMAP.md.
CI rewrites the table below on main after every push whose full
verification passes. Until that first happens it predates Phase 3: it has no
rows for the Phase 3 crates, which stay at tier 0.
| Crate | Tier (target) | Verus | Kani | Axioms | unsafe blocks (lines)⁵ |
Lines: spec / proof / exec¹ | Spec mutations: theorem / incidental / survived² | Conformance⁴ | Spec review³ |
|---|---|---|---|---|---|---|---|---|---|
| clazel | 0 (0) | not used | no harnesses | 0 | 0 (0) | — | — | WPT cookies/ domCookieTest, document.cookie set and get through real processes: 11 / 11 WPT cookies/ httpCookieTest, document.cookie read through real processes: 166 / 167 (1 expected failures) |
no spec files |
| clazel-base64 | 2 (2) | 80 verified, 0 errors | no harnesses | 0 | 0 (0) | 665 / 758 / 173 | 23 / 0 / 0 of 23 | WPT fetch/data-urls base64: 80 / 80 | 0 of 3 files current |
| clazel-cookie | 2 (2) | 424 verified, 0 errors | no harnesses | 0 | 0 (0) | 4066 / 4061 / 1676 | 43 / 0 / 0 of 43 | WPT cookies/ httpCookieTest and domCookieTest tables: 177 / 178 (1 expected failures) cookie_store differential, random set and get sequences: 3000 / 3000 http-state dates/: 70 / 70 http-state parser corpus: 195 / 222 (27 expected failures) |
0 of 9 files current |
| clazel-dns | 2 (2) | 242 verified, 0 errors | no harnesses | 0 | 0 (0) | 1979 / 1668 / 869 | 163 / 0 / 0 of 163 | captured DNS responses: 12 / 12 hand-built DNS messages: 24 / 24 resolv.conf nameserver lines: 10 / 10 simple-dns differential: mutated messages: 58731 / 60000 (1269 expected failures) simple-dns differential: our queries: 4000 / 4000 simple-dns differential: random messages: 8000 / 8000 |
0 of 5 files current |
| clazel-encoding | 2 (2) | 81 verified, 0 errors | no harnesses | 0 | 0 (0) | 764 / 485 / 205 | 22 / 0 / 0 of 22 | WPT encoding/ UTF-8 decode: 57 / 57 WPT encoding/ UTF-8 encode (decode cases reversed): 8 / 8 |
0 of 3 files current |
| clazel-fetch | 0 (2) | 164 verified, 0 errors | no harnesses | 0 | 0 (0) | 1500 / 1108 / 595 | 64 / 0 / 0 of 64 | http-state parser corpus (through the client): 195 / 222 (27 expected failures) url and cookie_store differential, random redirect chains: 1500 / 1500 |
0 of 5 files current |
| clazel-http | 2 (2) | 407 verified, 0 errors | no harnesses | 0 | 0 (0) | 3364 / 3188 / 1734 | 150 / 0 / 0 of 150 | RFC 9112 7.1 and 11 examples: 122 / 122 framing by construction: random bodies: 3000 / 3000 httparse differential: handcrafted heads: 29 / 44 (15 expected failures) httparse differential: llhttp corpus heads: 124 / 136 (12 expected failures) httparse differential: mutated responses: 53915 / 60000 (6085 expected failures) httparse differential: random responses: 6000 / 6000 llhttp test/response and framing cases: 109 / 136 (27 expected failures) |
0 of 5 files current |
| clazel-idna | 2 (2) | 375 verified, 0 errors | no harnesses | 0 | 0 (0) | 2406 / 2887 / 17549 | 69 / 0 / 0 of 69 | RFC 3492 section 7.1 samples: 19 / 19 WPT url/ IdnaTestV2.json: 2675 / 2675 WPT url/ toascii.json: 87 / 87 |
0 of 5 files current |
| clazel-ipc | 2 (2) | 177 verified, 0 errors | no harnesses | 0 | 0 (0) | 1240 / 1076 / 545 | 69 / 0 / 0 of 69 | RFC 8949 Appendix A (cbor/test-vectors): 82 / 82 minicbor differential: random and mutated bytes: 60000 / 60000 minicbor differential: random items: 20000 / 20000 |
0 of 7 files current |
| clazel-kernel | 2 (2) | 340 verified, 0 errors | no harnesses | 0 | 0 (0) | 3229 / 4264 / 1103 | 36 / 0 / 0 of 36 | WPT cookies/ domCookieTest tables, document.cookie set and get through the kernel: 11 / 11 WPT cookies/ httpCookieTest tables, document.cookie read through the kernel: 166 / 167 (1 expected failures) WPT url/ urltestdata.json navigation (rows with a base): 336 / 336 WPT url/ urltestdata.json origin_of and path_of (rows without a base): 560 / 560 WPT webstorage/ localStorage calls: 24 / 24 cookie_store differential, random multi-renderer document.cookie traces: 3000 / 3000 scalar and ASCII guards never fire (WPT urltestdata.json and fuzz): 2896 / 2896 |
0 of 3 files current |
| clazel-mime | 2 (2) | 288 verified, 0 errors | no harnesses | 0 | 0 (0) | 2584 / 2122 / 1640 | 35 / 0 / 0 of 35 | WPT mimesniff/media: 6 / 7 (1 expected failures) WPT mimesniff/mime-types: 955 / 955 WPT mimesniff/sniffing feeds: 2 / 2 WPT mimesniff/sniffing mislabeled binary: 3 / 3 |
0 of 5 files current |
| clazel-net | 0 (0) | not used | no harnesses | 0 | 0 (0) | — | — | none | no spec files |
| clazel-origin | 2 (2) | 43 verified, 0 errors | no harnesses | 0 | 0 (0) | 495 / 209 / 284 | 31 / 0 / 0 of 31 | HTML 7.1.1 example tables (same origin, same site): 11 / 11 WPT url/ urltestdata.json origin: 414 / 414 differential vs rust-url Url::origin(): 403 / 414 (11 expected failures) |
0 of 3 files current |
| clazel-percent | 2 (2) | 54 verified, 0 errors | no harnesses | 0 | 0 (0) | 520 / 297 / 173 | 34 / 0 / 0 of 34 | WPT url/ percent-encoding: 23 / 32 (9 expected failures) | 0 of 3 files current |
| clazel-psl | 2 (2) | 201 verified, 0 errors | no harnesses | 0 | 0 (0) | 1063 / 1840 / 45824 | 23 / 0 / 0 of 23 | differential vs publicsuffix 2.3.0: 49685 / 49685 publicsuffix tests.txt checkPublicSuffix: 78 / 78 |
0 of 3 files current |
| clazel-renderer | 0 (0) | not used | no harnesses | 0 | 0 (0) | — | — | none | no spec files |
| clazel-sandbox | 1 (1) | not used | 8 successfully verified harnesses, 0 failures, 8 total | 0 | 0 (0) | — | — | none | no spec files |
| clazel-sys | 0 (0) | not used | no harnesses | 0 | 14 (20) | — | — | none | no spec files |
| clazel-url | 2 (2) | 565 verified, 0 errors | no harnesses | 0 | 0 (0) | 5725 / 5260 / 2026 | 144 / 0 / 0 of 144 | WPT url/ setters_tests.json: 278 / 278 WPT url/ toascii.json: 87 / 87 WPT url/ urltestdata.json: 896 / 896 WPT url/ urltestdata.json hosts: 359 / 359 |
0 of 7 files current |
| xtask | 0 (0) | not used | no harnesses | 0 | 0 (0) | — | — | none | no spec files |
¹ From Verus's line_count; only code inside verus! is counted, and type definitions count as exec.
² Only the first number is coverage: a proven theorem is false for that many deliberately broken specs. See docs/VERIFICATION.md.
³ Optional human reviews of specs, contracts, axioms and mutation labels, unchanged since review. They gate nothing; see docs/SPEC-REVIEWS.md.
⁴ Measured by the run: passing / total units per vendored suite, as each tests/conformance*.rs reports them. Expected failures are counted separately and never as passes. See docs/VERIFICATION.md.
⁵ Counted by cargo xtask check: unsafe blocks (and unsafe extern blocks), with the lines from the unsafe keyword to the closing brace. Only clazel-sys may have any; the rule and its audits are in docs/VERIFICATION.md and docs/SYS-AUDITS.md.
⁶ The host shell (crates/clazel/src, item 2.15) is 298 lines, counted as wc -l counts them over its Rust files; the plan's limit is 300. The command line and the demo (crates/clazel/cli, item 2.16) are not counted: they only call the shell's public functions.
Phase 2 process tests: where they have run. The demo (cargo run -p clazel -- demo), the sandbox escape attempts, the hostile-renderer test and the WPT
cookies/ run through real processes (item 2.16) have been run on macOS arm64
only, locally: macOS is not in CI. On Linux they have never run (they were
written on a Mac), and CI on ubuntu-24.04 cannot run them until a maintainer
decides how to allow unprivileged user namespaces for the renderer, which
otherwise fails closed (see item 2.16 in
docs/phases/phase-2.md). Attempts to fork and to
ptrace a sibling are not made yet: they need a clazel-sys addition and its
audit. The table has rows for clazel and clazel-renderer once CI next
updates it.
Formalizing the standards has already found one spec bug: see docs/ERRATA.md.
scripts/setup.sh # install the pinned, checksummed toolchain (needs rustup)
scripts/verify.sh # every check, fresh proof runs (same as `cargo xtask verify`; CI runs this)
cargo xtask verify --changed # every check, but proofs and tests only for crates changed since origin/main
cargo xtask check # fast static checks onlyThe command-line browser, clazel, and the sandboxed renderer it starts for
each tab:
cargo build --release --bin clazel --bin clazel-renderer # both land in target/release/
target/release/clazel fetch http://example.com/ # one http:// GET, response printedfetch resolves the host with our own DNS client, sends a request built by
proven code, and frames the response with the proven HTTP/1.1 parser. It
needs the Internet, and speaks only http:// for now.
Each GitHub release has a Linux x86-64 tarball with both binaries and an
installer: unpack it and run sudo ./install.sh (or PREFIX=... ./install.sh).
On Ubuntu 23.10 and later, which restrict unprivileged user namespaces, the
installer also adds an AppArmor profile that lets clazel-renderer create the
one its sandbox needs, and nothing else; without it the renderer refuses to
start, by design. Built from source, run the same script once:
sudo packaging/apparmor/allow-userns.sh clazel-renderer "$PWD/target/release/clazel-renderer"The code is written by AI agents under rules (CLAUDE.md) designed so that nobody has to trust them. That leaves a lot of useful work for people, and much of it is work only a person can do.
- Check our work. Rerun every proof on your own machine with the Docker
command above, or
scripts/verify.sh. If anything does not match the verification status table, open an issue: a claim nobody can reproduce is a bug. - Review a spec against its standard. Each
spec.rstranscribes a standard step by step, with its modelling choices listed at the top (docs/SPEC-STYLE.md). Reviews gate nothing, and only people may record one, so a reviewed file means a person read it. See docs/SPEC-REVIEWS.md for how.contract.rsfiles are the shortest route to what each crate actually promises. - Take errata upstream. Formalising the standards has found places where they are wrong as written. Agents never file issues with standards bodies; the entries marked "Reported upstream: not yet" in docs/ERRATA.md are waiting for a person to check them and report them.
- Try to break it. Feed the parsers hostile input, look for a spec bug the
proofs would not catch (each crate's
mutations.jsonlists the known gaps, markedsurvives), or close one of those gaps with a lemma or a Kani harness. - Add test suites. Every Tier 2 crate is tested against official suites
and independent engines (
tests/conformance*.rs). Point us at suites we have not vendored, or at engines worth differential testing against. - Run it where we have not. The Phase 2 process tests (the sandbox, the hostile renderer) have only run on macOS arm64, and never on Linux. The README says why under the status table.
- Read
clazel-sys. It is the only crate withunsafe: 14 blocks that enter the OS sandbox. Every change is audited by a second model (docs/SYS-AUDITS.md); a person reading it too is welcome. - Build the next phases. The plan is in docs/ROADMAP.md
and the detailed plans in docs/phases/. TLS is parked on the
phase3/tlsbranch until it passes CI. Contributions go through the same machine checks as everything else, whoever (or whatever) wrote them: read CLAUDE.md first, and expectcargo xtask verify --changedand CI's full verification to be the bar.
MIT. See LICENSE.