Skip to content

UI tests you write in English.

An agent drives a real browser inside your own network, and judges the outcome.

Self-hostable · AGPL-3.0 · Bring your own model provider

asserty 0.3.0 — in development.

The api contract, the database schema and the runner protocol all still change. Production images and a Helm chart already exist.

See what's shipped

Natural language

Write “log in as the finance user, open the invoice list and check the totals add up” rather than a selector for every button along the way.

Inside your network

Runs happen on a runner you install yourself, so the browser can reach an application the internet cannot.

Yours to run

AGPL-3.0, a Helm chart and signed images. Running it inside your company asks nothing of you.

How it works

From a sentence to a verdict.

  1. 01

    Write the test

    Describe the case in prose, the way you would explain it to someone joining the team.

  2. 02

    It becomes instructions

    A model parses your prose into atomic instructions — each one an action, an assertion or a routine. Edit, reorder or disable any of them.

  3. 03

    A runner picks it up

    A runner inside your network claims the job, drives Chromium and reports progress back. Nothing dials in.

  4. 04

    You get a verdict

    Pass or fail, with a screenshot, the model's reasoning, timings and token counts for every step.

Product

Everything a test needs, and nothing it does not.

Authoring

Prose in, atomic instructions out

A test case keeps the prompt you wrote and the instructions it parsed into. Edit an instruction inline, reorder it, mark it optional, disable it, or change the prompt and regenerate the lot. There is nothing to keep in sync when the markup changes, because there is no markup in the test.

  • Inline editing and reordering
  • Optional and disabled steps
  • Regenerate from an edited prompt

Reuse

Routines and test users

Logging in should not be re-derived by a model on every run. A routine is a named, deterministic sequence — navigate, click, fill, wait — with declared inputs resolved at run time. Test users hold the personas those inputs come from: credentials, roles and whatever metadata your app needs.

  • Deterministic steps, declared inputs
  • Test a routine on its own
  • Personas with roles and metadata

Scale

Suites that fit the runner you have

Order the members, choose whether each starts in a fresh browser or shares one, and decide whether a failure stops the suite or lets the rest continue. Weights and capacity keep a large suite from swamping a small runner — a suite is never too big for a runner, only slower on a small one.

  • Fresh or shared isolation
  • Continue or stop on failure
  • Weight capacity per runner

Results

Every step, with the evidence

A run is not just a red or green bar. Open any step to see the screenshot the agent was looking at, the reasoning it gave, how long it took, how many tokens it spent and how many computer-use iterations it needed to get there.

  • Per-step screenshots
  • Model reasoning in the open
  • Timings and token counts

Operations

Runners and groups

Create a runner and you get a token, shown once, and a docker run command ready to paste. Groups decide which runners may take an application's work — a runner in the DMZ cannot reach an application behind the VPN, and groups are how you say so.

  • One command to start
  • Token rotation revokes instantly
  • Groups as a reachability boundary

Security

The browser comes to your network. Nothing comes the other way.

The runner dials out, never in

A runner never accepts a connection. The only thing your firewall has to allow is outbound HTTPS to asserty.

No provider key reaches the runner

Model calls are proxied through the api and credentials are injected server-side, so no api key is ever on a machine in your network — and no model endpoint has to be allow-listed either.

Nothing is written to disk

The runner holds no database, no driver and no volume. The container is disposable; recreating it changes nothing.

The token is the whole identity

It cannot be read back after creation — the server keeps only a hash. Rotation is the revocation mechanism, and the old token dies immediately.

Tenant isolation in the ORM

Separation between organizations is enforced by a Hibernate @TenantId discriminator at the persistence layer, not by a check each controller has to remember to make.

Signed images, provenance, SBOM

Every image ships with a signature, a build provenance attestation and an SPDX SBOM. One gh attestation verify tells you what you are about to run inside your network.

Built for the network you actually have

A corporate proxy is the ordinary case here, not an edge case. HTTPS_PROXY and NO_PROXY are honoured by both the api connection and the browser, and your corporate root CA is added to the standard trust store, never substituted for it.

  • Zscaler, Netskope and other TLS-inspecting proxies
  • Your root CA added, never swapped in
  • One startup line reports what resolved — and the proxy password is never logged

Self-host

Run the whole thing yourself

asserty is AGPL-3.0. Running it — for yourself, inside your company, against whatever applications you like — asks nothing of you. There is a Helm chart, a deployment runbook and published images for every component.

  • Kubernetes 1.27+, ingress-nginx, cert-manager, Postgres
  • ghcr.io/grnrb/asserty-api, -frontend and -runner
  • Bring your own OpenAI, Azure OpenAI or Anthropic key

Start a runner

docker run -d --restart unless-stopped \
  -e ASSERTY_API_URL=https://app.asserty.ai \
  -e ASSERTY_RUNNER_TOKEN=<your-token> \
  ghcr.io/grnrb/asserty-runner:latest

The token is shown once when you create the runner, and the app hands you this command with it already filled in.

FAQ

Questions worth asking.

Do I have to change my firewall?

Only to allow outbound HTTPS to asserty, if you do not already. The runner opens the connection and holds it; nothing from outside ever dials into your network, and there is no tunnel and no exposed environment.

What happens if no runner is online?

The run stays pending. Nothing is lost, and it starts the moment a runner connects. Waiting is the point — the alternative is executing it somewhere that cannot reach the application.

Which model providers can I use?

OpenAI, Azure OpenAI and Anthropic. You supply the key, it is stored write-only and never read back, and you can map a different provider to each application.

Do my tests break when the UI is redesigned?

There is no markup in the test, so there is nothing to keep in sync when the markup changes. A test describes intent; the agent works out how to satisfy it against whatever is on screen.

Can I stop the agent from wandering off?

Yes. Each application declares the domains the browser is allowed to navigate to, along with a ceiling on computer-use iterations and per-instruction, connection and request timeouts.

Can I self-host all of it?

Yes, and today that is the main way to run it. The repository ships a Helm chart and a deployment runbook covering Kubernetes, ingress, TLS and a managed Postgres.

What does it cost?

There is no pricing yet. asserty is pre-1.0 and self-hosting is free under the AGPL — your only cost is the infrastructure you run it on and the model provider key you bring.

Is it ready for production?

Not yet. asserty is pre-1.0: the api contract, the database schema and the runner protocol all still change between releases. Production images and a Helm chart do exist, and upgrades to the runner happen on your schedule, not ours.

Describe a test. Watch it run.

Start in the hosted app, or clone the repository and run the whole stack yourself.