A software test automation service where the UUID is the account

Every ranked answer to this topic sells a seat. You sign up, add teammates, generate an API key, provision a project, and only then does your first test get to exist. Assrt skips that entire layer. The scenario's UUID v4 is the access token. There is no login, no invitation flow, no org, no role. One file in the repo says it in eight words.

M
Matthew Diakonov
12 min read
4.9from open-source stars and shipped production tests
Five public endpoints, zero Authorization headers
122 bits of entropy per scenario URL (UUID v4)
Anonymous telemetry: trackServerEvent("anonymous", ...)
Offline-capable: ~/.assrt/scenarios/<uuid>.json local cache

The eight words that define the service

Open /Users/matthewdi/assrt-mcp/src/core/scenario-store.ts, jump to line 7, and the design doc for the entire public data plane is in the file header:

scenario-store.ts

Not “we're working toward a share-by-link story.” Not “zero-trust coming in Q3.” The file is written that way because the service was shaped that way: the UUID v4 is the access token, the same UUID is the URL path parameter, and the URL itself is the thing you share.

What replaces the auth layer

When a typical software test automation service handles a request, the middleware stack reads a session cookie or a Bearer token, resolves it to a user, resolves the user to an org, checks a role, and then lets the handler run. Assrt's public plane replaces all of that with one function call. If the URL has a valid UUID v4, the handler runs. If not, 404.

api/public/scenarios/[id]/route.ts

The PATCH and DELETE handlers below in the same file follow the same shape: validate the UUID, hit Firestore, emit an anonymous telemetry event, return. No getServerSession, no JWT, no API-key middleware, no rate-limiter keyed on identity, no org check. The access control model of the service fits in a regex.

Every public endpoint, one identity primitive

MCP client
Web app
curl / CI
Shared link
isValidUUID(id)
POST /scenarios
GET /scenarios/:id
PATCH /scenarios/:id
POST /runs
POST /runs/:runId/artifacts
0public endpoints
0Authorization headers
0bits of entropy per URL
$0seat price on public plane

What a request actually looks like

The shape of a request is the clearest way to see the architecture choice. On the left: a seat-based service where sharing the response is its own workflow. On the right: the Assrt public endpoint where the URL is the share.

the difference is the headers

# Typical software test automation service: seat-based auth.
# Every request carries an API key tied to a user tied to a seat tied to an org.

curl -X POST https://api.vendor.example.com/v1/tests \
  -H "Authorization: Bearer sk_live_7fA9...REDACTED" \
  -H "X-Organization-Id: org_2zV4TqLb3kPqR" \
  -H "X-Project-Id: proj_9bKfNpXm7L2vA" \
  -H "Content-Type: application/json" \
  -d '{"plan": "...", "visibleTo": ["team:qa"], "invite": ["alice@..."]}'

# Sharing the response requires adding the recipient as a seat.
# Revoking requires rotating the API key and re-inviting everyone.
-22% fewer identity primitives

The MCP client mirrors this on the request side

Every top-ranking page for this topic describes the client as “install our SDK, paste your API key.” Here is what the Assrt client looks like on the GET path. Notice what is not in the headers object.

scenario-store.ts
122 bits

UUID v4 entropy is the same unguessability budget as a 20-character alphanumeric bearer token. The thing you share is the thing that authenticates.

RFC 4122 § 4.4, applied to scenario routing

The workflow this design unlocks

A software test automation service that requires a seat forces a sequencing: create the account, provision the project, invite the collaborator, wait for them to accept, then share the test. The capability-URL model collapses that into one step.

1

Run a test

Your coding agent calls assrt_test with a plan. The MCP client POSTs to /api/public/scenarios and receives a UUID v4 back. A scenario.md file is written to /tmp/assrt and mirrored to ~/.assrt/scenarios/<uuid>.json.

2

Get the capability URL

buildCloudUrls (scenario-store.ts:243-263) emits the share page URL and per-artifact URLs. The page URL is the scenario's canonical address for everyone, forever.

3

Paste it anywhere

Slack, email, a PR comment, a Linear ticket, a client deliverable. The recipient needs no account and there is no 'share with...' dialog. The URL is the grant.

4

Recipient opens the URL

The GET handler validates the UUID, reads from Firestore, returns the plan. The plan renders. No 'Sign in to continue,' no 'Request access.' The telemetry event logs as anonymous because it literally is.

5

Recipient reruns the scenario

Same UUID, different machine. Their MCP client uses fetchScenario(id) to pull the plan, then the agent executes it against the URL. Runs append to the same scenario under the same UUID.

6

Done

No identity was created at any step. No seat was billed. No role was granted. The test ran.

Before and after the capability URL

The difference is easiest to feel as a workflow, not a feature list. Here is the minute-by-minute reality of shipping a test run to a colleague under each model.

You finish the test run. You open the dashboard. You click Share. A dialog asks for the recipient's email. You paste it. The vendor sends an invitation email. The recipient clicks through, hits the signup flow, creates a password, verifies the email, joins the org. Finance gets billed for a new seat. Now they can see the run.

  • Email invitation required for every collaborator
  • Recipient creates an account before they can read the artifact
  • Seat billing kicks in per viewer
  • Admin has to provision SSO for a one-time review

Where every file for a scenario actually lives

Because there is no account and no org, the service stores the artifacts as if they were first-class public records keyed by UUID. Here is the layout for a single scenario, split across the three places the data physically lives.

Central API (app.assrt.ai)

/api/public/scenarios/:id, /runs, /runs/:runId/artifacts. Five endpoints, UUID-keyed, anonymous telemetry via trackServerEvent.

/tmp/assrt/scenario.md

The plan text, editable in place. A file-watcher (scenario-files.ts) debounces edits and auto-syncs PATCHes back by UUID.

/tmp/assrt/results/<runId>.json

Full TestReport JSON per run. Mirrored to the central API under the scenario UUID and the generated runId UUID.

~/.assrt/scenarios/<uuid>.json

Offline fallback cache. Every read writes here; every write here is the authoritative copy if the central API is unreachable.

/tmp/assrt/<runId>/video/recording.webm

Playwright video, uploaded via multipart to the artifacts endpoint. Retrieved by ?file=recording.webm on the same URL.

/tmp/assrt/<runId>/screenshots/*.png

One screenshot per step, named <step>_<action>.png. Uploaded with the video in the same multipart request.

A live request session, annotated

You can reproduce this session against the live API right now. No account is required because that is the point. Use any UUID v4 generator for the scenario body and pass the returned id into the GET request.

curl against app.assrt.ai

Feature by feature, against the seat-based default

One way to read the capability-URL choice is as a list of things that disappear compared to the default software test automation service shape. Another is the list of things that become free as a side effect.

FeatureSeat-based serviceAssrt (capability URL)
Authentication on the data planeBearer token + org header + project scopeValid UUID v4 in the URL path
Sharing a test with a colleagueInvite email + signup + seat billPaste the URL
Telemetry identityPer-user, per-org event propertiestrackServerEvent("anonymous", ...) across the board
Offline fallbackAPI 503s; dashboard is darkReads ~/.assrt/scenarios/<uuid>.json and keeps running
Test artifact storageVendor DB, accessible via dashboard loginUUID-keyed public endpoints plus local /tmp mirror
RevocationRotate API key, remove seat, reinviteDELETE on /api/public/scenarios/:id
Seat price at 5 viewers5 x monthly seat fee$0
Self-host pathEnterprise tier, contact salesClone the repo, set ASSRT_API_URL

The trade, stated explicitly

This design is not free. It is a deliberate swap of per-user revocation and audit for link-first ergonomics. Both columns are real; neither is universally correct.

What you give up:the ability to say “revoke Alice's access to this test without affecting Bob's,” because access is keyed to the URL rather than to identity. Revocation is URL-scoped, not person-scoped.

What you get:the ability to say “here is the test run” and have the other person see it, with no signup, no invitation, no org onboarding, no new billing row. And the ability to run tests at all with the internet partly unplugged, because the scenario plan lives on your disk at ~/.assrt/scenarios/<uuid>.json.

When to pick the other side: compliance environments where auditability per human is the whole point (finance, healthcare, government). For those, the same code base runs as a self-hosted instance inside your network, with the capability-URL plane converted into an internal-URL plane, and your existing SSO layer sitting in front.

The services this replaces, in one strip

The shortlist of software test automation services for a small engineering team in 2026. All of them start with an account. The capability-URL design is the one architectural move that none of them can copy without breaking their seat-billing model.

Sauce LabsBrowserStack AutomateLambdaTestmablFunctionizeTestimApplauseTestlioQASourceGlobal App TestingCypress CloudChromatic

See the capability URL model on your own app

Thirty minutes: we run assrt_plan against a URL you pick, share the result as a paste-able link, and walk through the five public endpoints that make it work.

Book a call

Frequently asked questions

What do you mean a software test automation service with no accounts?

The Assrt public data plane is implemented as five unauthenticated HTTP endpoints, all keyed by scenario UUID in the URL path. When you create a scenario with assrt_test or assrt_plan, the MCP client POSTs the plan to /api/public/scenarios and gets back a UUID v4. Every subsequent operation (fetch the scenario, patch the plan, record a run, upload a video or screenshot) uses that UUID as the path parameter and carries no Authorization header. The comment at /Users/matthewdi/assrt-mcp/src/core/scenario-store.ts line 7 says it in one sentence: 'No auth required: the UUID v4 IS the access token.' This is the capability-URL pattern from 1970s academic computer security, applied to a testing service in 2026. Whoever holds the URL holds the scenario. No signup, no seat, no invitation flow, no org, no role.

Is that actually secure?

UUID v4 has 122 bits of entropy, so guessing one at random is equivalent to guessing a 20-character alphanumeric password. The math works out to roughly 5.3 x 10^36 possible values. For a scenario that should stay private, the UUID is as private as a well-generated bearer token, with the same trade-off: if you leak the URL, you leak the thing. For scenarios that are meant to be shared (the typical case when a founder or PM wants to show a test run to a colleague), shipping a link is the entire workflow. If you want genuine org-level isolation with revocation and audit, the self-hosted Assrt app at /Users/matthewdi/assrt/src/app/app lives behind the auth provider and the ~/.assrt/scenarios/<uuid>.json local cache; the public data plane is explicitly the share layer, not the vault.

Where in the code is the no-auth claim actually enforced?

The server side is at /Users/matthewdi/assrt/src/app/api/public/scenarios/[id]/route.ts. Line 9 defines isValidUUID. Lines 18, 52, and 100 call it inside GET, PATCH, and DELETE respectively; if the path parameter is not a valid UUID v4, the handler returns 404 without touching Firestore. There is no call to getServerSession, no JWT verification, no API-key middleware, no rate limiter that keys off an identity. Every handler emits trackServerEvent("anonymous", "api_public_scenario_accessed" | "...created" | "...updated" | "...deleted"). The MCP client at scenario-store.ts:65-92 and :109-121 mirrors this on the request side: fetch is called with headers: { "Accept": "application/json" } and headers: { "Content-Type": "application/json" }, and never Authorization.

How does this compare to Sauce Labs, BrowserStack, LambdaTest, mabl, or Functionize?

They all start with an account. You sign up, you get an org, you add seats, you generate an API key with scopes, you create a project, and only then do you get to store a test. The test lives in their database, rendered through their dashboard, accessible only to users you have explicitly invited. That is the entire reason a software test automation service subscription costs money: you are paying for the ACL machinery, the seat licensing, the SSO integrations, the audit logs, the per-team dashboards. Assrt removes that layer entirely on the public plane. The trade is deliberate: you give up per-user permissions, and in exchange you get URL-first sharing, anonymous usage, and a data format that is impossible to vendor-lock because there is no vendor-held identity to re-auth against.

What happens if the central API is down?

The MCP client degrades gracefully. At scenario-store.ts:89-92, fetchScenario catches the network error and falls back to readLocal(scenarioId), which reads from ~/.assrt/scenarios/<uuid>.json. Every scenario fetched or saved is mirrored to this disk path via writeLocal (lines 41-48). saveScenario (lines 99-130) does the same on the write path: if the POST to /api/public/scenarios fails, it generates a local-only ID prefixed with 'local-' and writes just the JSON to your disk, so your test still runs. The service literally continues to work with the internet unplugged, because the scenario file is on your machine and the browser is on your machine. Most SaaS test automation services return 503 Unavailable in the same situation, because the test artifacts only exist in their cloud.

How do I share a test run with a colleague who does not have an Assrt account?

You do not need one, because they do not need one. After a run, the MCP response contains a cloud URL built at scenario-store.ts:243-263 in buildCloudUrls: the page URL is ${CENTRAL_API_URL}/s/${scenarioId}, and each artifact (video recording, screenshots, log) has its own ${CENTRAL_API_URL}/api/public/scenarios/${scenarioId}/runs/${runId}/artifacts?file=... URL. Paste either link into Slack, email, or a PR comment. Whoever clicks it sees the scenario or the video. No signup, no 'request access,' no 'owner must approve.' If they want to rerun the same scenario against a different URL or with different variables, they clone the scenarioId and point their MCP at it; the plan text is already public.

What about runs, screenshots, and video artifacts? Same model?

Same model. /api/public/scenarios/[id]/runs (POST) records a run scoped to the scenario UUID. /api/public/scenarios/[id]/runs/[runId]/artifacts (POST) uploads the artifact files as multipart form-data, again keyed only by UUIDs. Artifact retrieval is the same URL with ?file= appended. The MCP client at scenario-store.ts:202-238 uses a 2-minute timeout for this upload (videos can be large). None of these endpoints accept or validate an Authorization header. They are publicly readable by UUID, which is exactly the property that makes share-by-link work at all. If you compare this to how Cypress Cloud, Playwright Trace Viewer hosted, or Chromatic treat test artifacts (all auth-gated, org-scoped), the architectural difference shows up in one line of the request path.

Who is this for and who is this not for?

For: solo engineers, founding engineers, small cross-functional teams, contractors handing deliverables to clients, open-source maintainers, AI coding agents that should be able to run a test without first provisioning an account. The capability-URL model collapses the friction between 'I wrote a test' and 'my teammate clicked it.' Not for: regulated environments where auditability per individual human is a compliance requirement (finance, healthcare, government), teams that need per-user revocation tied to offboarding, or orgs where a shareable URL is itself considered a data-leak risk. For those, the self-hosted mode is the answer: clone /Users/matthewdi/assrt, point ASSRT_API_URL at your own instance, swap Firestore for your own DB, and the capability-URL plane becomes an internal-network URL plane.

How do I try it in five minutes?

Install the MCP server: npx assrt-mcp. Set ANTHROPIC_API_KEY (or sign in with a Claude Pro token via the OAuth path in getCredential at server.ts:777). In your MCP config (Claude Code, Cursor, Claude Desktop), add assrt-mcp as a stdio server. Then ask the agent 'run assrt_plan on https://your-app.com' or 'run assrt_test with this plan ...'. The first run creates a scenario UUID and a shareable URL you can paste anywhere; subsequent runs against the same UUID append a new run record. Everything else (the runner, the video, the scenario.md on disk, the auto-sync watcher, the telemetry, the retry logic) is described in detail at the bottom of the README. The capability URL is the only thing you have to remember.

Can I self-host the service to keep scenarios internal?

Yes. The API is a Next.js app at /Users/matthewdi/assrt, and the MCP reads the CENTRAL_API_URL environment variable at scenario-store.ts:14 (defaulting to https://app.assrt.ai). Set ASSRT_API_URL=https://your-internal-domain.example.com in the MCP environment and every public endpoint (scenarios, runs, artifacts) routes through your instance. The data model is Firestore by default (see /Users/matthewdi/assrt/src/lib/firestore.ts), but the public API surface is a thin wrapper; swapping in Postgres, SQLite, or a file-backed store is a couple of hundred lines. The capability-URL contract stays the same: UUID in the path, no Authorization header, 404 on invalid UUID. You get the post-account ergonomics on your own network.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.