# A headless browser whose session is four records, not a window

> h5i is a pure Rust headless browser with no Chromium and no V8, built so an agent can drive pages and then edit, replay and diff the HTTP traffic underneath them, wrapped in an auditable sandbox with five isolation tiers, and paired with h5i-app, a framework that pushes one transition function into Lean 4 for formal verification.

**h5i-dev/h5i** — Fast, security-first headless browser for AI agents. Built for automation, red teaming, web testing, and scraping, with direct HTTP traffic control and auditable sessions. Pure Rust, no Chromium or V8.

- Repository: https://github.com/h5i-dev/h5i
- Website: https://h5i.dev
- Stars: 671 · Forks: 66
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/h5i-dev-h5i

## A session is a page, a cookie jar, a policy and a record

The definition of a session is where the design starts. It is not a browser window: it combines one page state, a cookie jar, a network policy and a request record.

Those four components are separate on purpose. A cookie jar you can inspect is not the same as cookies buried in a profile directory, a network policy you can name is not the same as an ad-hoc request block, and a request record that survives the session is what makes replay possible later.

The commands operate on that abstraction, and the ref handles are the detail worth noting. A snapshot returns a page outline with handles like @e3, and a click takes one, so an agent never has to guess at a selector or scrape coordinates out of a screenshot.

```bash
h5i browser open https://docs.rs/ --allow docs.rs
h5i browser snapshot
h5i browser click @e3
h5i browser extract '{"titles": ["h2"]}'
```

Structured extraction takes a small selector expression rather than prose, and there is a separate read command for one page with no persistent session, which is the honest way to fetch something without leaving state behind.

## The browser owns its network, so replay is a verb

Most automation stacks treat HTTP capture as something a proxy does from the outside. Here the native browser owns its network layer, so the traffic is captured, inspected, edited, replayed and compared from inside the same tool.

Editing is per-field rather than per-request. A captured message gets an id, and replay takes that id with a set expression, so you can change one query parameter and resend exactly that message:

```bash
h5i websec requests
h5i websec replay req_42 --set query.id=456
h5i websec diff res_42 res_43
h5i websec sequence flow.json
```

The diff command compares two responses, which is how you tell whether an injected value changed behaviour. The sequence command runs a multi-step test from a file, which is the unit that the CI integration replays.

Sites that genuinely need a full browser go through the same workbench through a proxy command, so the workflow does not fork when a target requires Chromium. That is the pragmatic part of the design: the no-Chromium claim is about the native path, not a refusal to accommodate the sites that need more.

The discovery command is shaped the same way. Endpoint enumeration takes a state filter, and the documented invocation asks for confirmed endpoints, with each row naming the evidence behind it. Requiring evidence per row is what keeps a discovery list from being a list of guesses.

## Five isolation tiers, picked in a profile file

The stated reason for the sandbox is blunt: agents may run out of control and take dangerous actions.

Isolation is configured per profile in a file called .h5i/env.toml, and the profile picks one of five tiers: workspace, process, supervised, container, or microvm. Each tier limits network egress and filesystem access, so the choice is really a choice about how much you trust the code being run.

The two extremes are worth naming. A workspace tier is a git worktree, which is enough for an agent that is mostly editing text. A microvm is a full virtual machine boundary, which is the setting for a target you do not control. In between sit process isolation, supervised execution, and containers.

The lifecycle is designed around review rather than around running:

```bash
h5i box create alpha --profile agent-claude
h5i box shell alpha
h5i browser open https://docs.rs/ --in alpha
h5i box propose alpha
h5i box apply alpha
h5i box rm alpha
```

A box is created with a profile, can be entered as a confined shell, can host a browser session with an in flag, and then ends in one of two ways. Propose produces a reviewable snapshot and apply merges what was approved. Removal discards it. So the default path is that an agent's work arrives as a diff for a human, which is the only version of agent autonomy that survives contact with a repository.

## serde_json keeps key order so a replay is the recorded request

The dependency manifest explains its decisions in comments, and one of them is the most instructive line in the project.

serde_json is enabled with the preserve_order feature. The stated reason: a JSON edit rewrites the body, and a replay whose keys came back sorted is not the request that was recorded. So the tool refuses to silently normalise an HTTP body, because byte order in a signed or hashed request can matter and because a diff against the original would lie.

The second comment is about git2, which is pulled with default features disabled. The stated reason is that the HTTPS and SSH features pull OpenSSL, and the macOS build ended up linked against a Homebrew OpenSSL that is not present on every Mac. So git2 cannot fetch, push or clone. That is a deliberate trade to make the build hermetic on macOS, and it also means the binary has no built-in network path for version control.

The rest is ordinary and well organised: thiserror pinned at 2.0.18, serde with derive, toml with parse and display, and sha2 for hashing. All of it lives in workspace dependencies with a comment explaining why, namely that a version skew between git2 and serde would break conversions across a crate boundary.

One version is floating rather than pinned, and it is the one that has to match the platform: nothing in the list fixes a Rust toolchain beyond the workspace resolver setting of 3.

## One transition function, translated into Lean

The second half of the project is not a browser feature. h5i-app is a Rust web framework whose purpose is to let you prove properties in Lean 4.

It is added as an ordinary dependency with feature flags for the pieces you use:

```toml
[dependencies]
h5i-app = { version = "0.1", features = ["http", "postgres"] }
```

The shape of the method is what matters. Logic is written as pure Rust functions and proved in Lean 4 through Aeneas, which is the translation layer between the two. Serving happens through axum, and the constraint that handlers never touch the database is the load-bearing rule: the database is a source of rows, not a place where behaviour lives.

From there the claims get specific. Invariants can be proved for the rows loaded back out of the database, so a property is not only true of the pure function but of what you actually get after a round trip. And properties can be proved across requests for every order in which clients' requests commit, which is concurrency stated as a theorem rather than as a stress test.

The kernel is one function that decides what a command does. The calculator tutorial shows it returning a memory and a reply from a principal and a snapshot, with a Set command storing a number per user, an Apply command computing on the stored value, and a Get returning it.

In Lean the same thing is an ordinary theorem, with hypotheses naming the room available in the snapshot and the transition having returned a value. Aeneas does the translation; the proof is Lean.

## Eight crates, and the examples are excluded from the workspace

The verification framework is split into eight published crates, and each is declared with both a path and a version of 0.1.0 so that the crates can be released against each other.

They are h5i-app-core, h5i-app-http, h5i-app-json, h5i-app-pg, h5i-app-pgsql, h5i-app-schema, h5i-app-sql and h5i-app-token. The naming tells you the layering: core logic, HTTP serving, JSON, PostgreSQL in two forms, schema, SQL and tokens. Two PostgreSQL crates is the interesting one, and the manifest does not explain the split.

Two structural decisions are documented in the manifest itself. First, external dependencies are declared per crate rather than inherited from the workspace, with the stated reason that a user's build of h5i-app must not inherit the CLI's feature choices, naming serde_json's preserve_order as the concrete example. That is the right call: the feature exists for the browser's HTTP replay and would be wrong to force on an application.

Second, the workspace members are the crates directory with the resolver set to 3, and the application examples are excluded, with a comment explaining that they are their own workspace and depend on the h5i-app crates by path, as an application would.

So the examples are built the way a user would build, which means an example that compiles is evidence about the published API rather than about a monorepo's internal wiring.

## The CI template pins a v1 tag while releases sit at 0.4

The repository ships a GitHub Action template under an examples directory for replaying confirmed flows in CI, and the action definition itself lives at the repository root as action.yml.

The template is short. It references the project by a major-version tag, points at a target URL, and names a directory of tests:

```yaml
- uses: h5i-dev/h5i@v1
  with:
    target: http://localhost:3000
    tests: .h5i-tests/tests
```

The version reference is the thing to check before you adopt it. The released versions are in the 0.4 series, with 0.4.8 dated 1 October 2026, 0.4.7 on 27 September and 0.4.6 on 25 September. So the action is consumed at a v1 tag while every release is numbered below 1. Whatever that tag points at is the version your pipeline actually runs, and it is not the one the release list names.

The cadence around it is fast. Three releases inside a week at patch level, with the last push to the main branch on 1 October 2026 and the repository not archived. For a security tool that is a reasonable rhythm, and it also means pinning a version is worth doing deliberately.

The red-team examples are separated by purpose: a web security dojo target, a set of personas, and the security regression CI template, alongside the application examples that the workspace excludes.

## Conclusion

It fits two distinct jobs. For red-teaming and scraping it gives an agent a browser whose traffic it can edit and resend, which is the capability most agent browsers leave to a proxy written in another language. For application work, h5i-app is a rarer proposition, a framework where the properties you care about are theorems rather than tests, which suits a team already committed to Lean. Two things to check first. The GitHub Action template references the project at a v1 tag while released versions are in the 0.4 series, so check what that tag points at before wiring it into CI. And git2 is compiled without HTTPS or SSH support to avoid an OpenSSL dependency, which means this binary cannot fetch, push or clone over the network.

## FAQ

### What is h5i?

It is a headless browser written in pure Rust for AI agents, with no Chromium and no V8, built for automation, red-teaming, web testing and scraping. A session combines one page state, a cookie jar, a network policy and a request record, and HTTP traffic can be captured, edited, replayed and diffed. The licence is Apache-2.0.

### How do I install h5i and its plugins?

The installer takes optional plugin flags, for example websec, recon and test, and there is an alternative raw GitHub URL if you would rather not pipe from a domain. You can also build from source with cargo install --path ., and h5i plugin list reports what is installed. For agent runtimes, npx skills add h5i-dev/h5i or h5i skill install writes the skill where your runtime looks.

### How do h5i sandbox tiers work?

A profile in .h5i/env.toml selects one of five tiers: workspace, process, supervised, container, or microvm, and each limits network egress and filesystem access. Work happens in a box, so you can create one with a profile, enter it as a confined shell, run a browser inside it with an in flag, then either propose a reviewable snapshot and apply the approved changes, or remove the box and discard the work.

### What does h5i-app do differently from other Rust web frameworks?

It targets formal verification. You write logic as pure Rust functions, prove them in Lean 4 through Aeneas, and serve them with axum, with handlers that never touch the database. The framework is built so you can prove invariants for rows read back from the database and prove properties across requests for every order in which clients' requests commit. It ships as eight crates at version 0.1.0.

### Can h5i clone or push over the network?

Not through git2. The manifest disables its default features because the HTTPS and SSH features pull OpenSSL, which caused a macOS build to link against a Homebrew OpenSSL that is not present on every machine. As a result git2 cannot fetch, push or clone, which keeps the build hermetic and costs you networked version control from inside the binary.

## Sources

- [h5i-dev/h5i on GitHub](https://github.com/h5i-dev/h5i)
- [License: Apache-2.0](https://github.com/h5i-dev/h5i/blob/main/LICENSE)
- [Project website](https://h5i.dev)
- [README](https://github.com/h5i-dev/h5i/blob/main/README.md)
- [Releases](https://github.com/h5i-dev/h5i/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/h5i-dev-h5i
