Docker-first AI end-to-end browser testing.
RiddleRun is a CLI and optional self-hosted web app for teams that want automated agentic end2end test. Describe a user journey in JSON, run it in Docker, and let a Playwright/browser-use agent execute your test case for you!
The example runs below come from the example tests in this repository and were recorded from the self-hosted RiddleRun web app.
The repo ships with Wikipedia examples you can run without hosting your own app first:
| Test ID | Steps | Purpose |
|---|---|---|
wikipedia-python-language-es |
3 | Switch the Python article from English to Spanish. |
wikipedia-search-toc-references |
7 | Search Wikipedia, use the table of contents, and verify references. |
wikipedia-cross-article-links |
8 | Follow in-article links from Python to related topics. |
wikipedia-python-language-fail |
3 | Intentional failure for checking error reporting. |
End-to-end testing is where product quality often becomes expensive:
- Vibecoding. Your codebase changes faster than you can test.
- Manual E2E testing does not scale. Every release needs core flows checked again, and the cost grows quickly as your product, supported browsers, and user journeys expand.
- Traditional Playwright and Selenium suites can be brittle and slow to maintain. Selector-heavy tests break when UI structure changes, while modern AI-assisted development increases the speed at which applications change and therefore the speed at which teams need feedback.
The CLI is published as a Docker image. Clone this repo for the example project and test files, or mount your own JSON files once you are ready.
git clone https://github.com/raeudigerRaeffi/riddlerun.git
cd riddlerunSet an API key in your shell, pull the image, and run one example test:
$env:OPENAI_API_KEY = "sk-..."
docker pull riddlerun/riddlerun:latest
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-esRuns write JSON artifacts under /data/artifacts/runs by default. Browser-use logs are hidden by default so the CLI can show a live results table; add --verbose when debugging.
Use this option when you want to develop RiddleRun itself or avoid pulling the published image.
cd riddlerun
docker build -t riddlerun:local .
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun:local `
run /workspace/examples/tests --test-id wikipedia-python-language-esRiddleRun needs one project JSON file and one or more test case JSON files.
A project describes the application under test:
{
"id": "local-demo",
"name": "Local demo app",
"baseUrl": "http://host.docker.internal:3000",
"auth": {
"username": "demo@example.com",
"email": "demo@example.com",
"password": "change-me"
},
"browser": {
"mode": "local",
"headless": true,
"timeoutMs": 120000
}
}A test case belongs to a project and contains ordered natural-language steps:
{
"id": "login-smoke",
"name": "Login smoke test",
"projectId": "local-demo",
"description": "Verify that a user can log in.",
"tags": ["smoke", "auth"],
"steps": [
{
"description": "Open the project base URL."
},
{
"description": "Log in using the saved project credentials.",
"expected_result": "The authenticated dashboard is visible."
}
]
}Field notes:
authis optional if testing requires an accountbaseUrlis the starting URL for the app under test. From Docker on macOS and Windows,host.docker.internalpoints back to your host machine.projectIdin each test must match the projectid.descriptiontells the browser agent what to do.expected_resultis optional and tells the agent what outcome to validate.tagsare optional and make it easier to run subsets of tests.
See docs/test-spec.md, schemas/project.schema.json, and schemas/testcase.schema.json for the full JSON contract.
Run your own files by mounting their directory and pointing RIDDLE_RUN_PROJECT at your project file:
docker run --rm `
-v "${PWD}/my-riddlerun-tests:/workspace/tests:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/tests/projects/local-demo.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/tests/cases --tag smokeEach test case is executed as one browser-use agent task. The options below control which tests are selected, how long the agent can work, which LLM is used, and what gets saved afterward.
Run every Wikipedia example:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --tag wikipediaRun the intentional failure example:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-failValidate JSON files without opening a browser:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
riddlerun/riddlerun:latest `
validate /workspace/examples/testsRecord a run video:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-es --video-dir /data/artifacts/videosRun a longer flow with a higher agent step budget:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e OPENAI_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests `
--test-id wikipedia-cross-article-links `
--max-steps 45 `
--verbose| Flag | Default | Description |
|---|---|---|
--project, -p |
RIDDLE_RUN_PROJECT |
Project metadata JSON file. Required unless the env var is set. |
--test-id |
all tests | Run only the given test IDs. Repeat the flag to select multiple tests. |
--tag |
all tests | Run only tests that include one of the given tags. Repeat to match any listed tag. |
--max-steps |
29 |
Maximum browser-use agent actions allowed per test. Increase this for longer multi-step journeys. |
--dry-run |
off | Validate the selected tests and print skipped results without opening a browser. |
--artifact-dir |
/data/artifacts in Docker |
Directory where JSON run result files are written. |
--video-dir |
off | Enable screen recording and save videos under this directory. |
--verbose |
off | Show browser-use logs instead of only the live results table. |
--llm-provider |
openai |
LLM provider passed to browser-use. |
--llm-model |
o4-mini |
Model name for the selected provider. Important the test works best when Vision Language Models are used |
--llm-api-key |
provider env var | API key override. Prefer provider-specific env vars in Docker. |
--llm-base-url |
provider default | Custom base URL for local or compatible providers such as Ollama. |
All settings can also be set with the RIDDLE_RUN_ prefix. See .env.example for a starting point.
| Variable | Default | Description |
|---|---|---|
RIDDLE_RUN_PROJECT |
— | Default project JSON path for run. |
RIDDLE_RUN_MAX_STEPS |
29 |
Default agent step cap per test. |
RIDDLE_RUN_ARTIFACT_DIR |
artifacts |
Default directory for run JSON artifacts. |
RIDDLE_RUN_VIDEO_DIR |
off | When set, enables video recording to this directory. |
RIDDLE_RUN_VERBOSE |
false |
Show browser-use logs by default. |
RIDDLE_RUN_LLM_PROVIDER |
openai |
Default LLM provider. |
RIDDLE_RUN_LLM_MODEL |
o4-mini |
Default LLM model. |
RIDDLE_RUN_LLM_API_KEY |
— | Generic API key override for any provider. |
RIDDLE_RUN_LLM_BASE_URL |
— | Default custom provider base URL. |
Browser behavior for a project is defined in the project JSON browser block:
| Field | Default | Description |
|---|---|---|
mode |
local |
Browser execution mode. Remote browser connections are not supported yet. |
headless |
true |
Whether Chromium runs headless inside the Docker container. |
timeoutMs |
120000 |
Browser session timeout in milliseconds. |
RiddleRun launches Chromium inside the Docker container for each run. The browser is not displayed on your host machine; use --video-dir when you need a visual recording.
RiddleRun uses browser-use with a configurable LLM provider. OpenAI is the default.
PowerShell:
$env:OPENAI_API_KEY = "sk-..."macOS and Linux:
export OPENAI_API_KEY="sk-..."Then pass the variable into Docker with -e OPENAI_API_KEY. Provider-specific keys such as OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY, MISTRAL_API_KEY, and GROQ_API_KEY are read automatically when no CLI flag or RIDDLE_RUN_LLM_API_KEY override is set.
For Docker Compose development, put keys in .env:
RIDDLE_RUN_LLM_PROVIDER=openai
RIDDLE_RUN_LLM_MODEL=o4-mini
OPENAI_API_KEY=sk-...
Anthropic:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e ANTHROPIC_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-es --llm-provider anthropic --llm-model claude-sonnet-4-5Local Ollama:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-es --llm-provider ollama --llm-model llama3.1 --llm-base-url http://host.docker.internal:11434LiteLLM provider, for example Groq:
docker run --rm `
-v "${PWD}/examples:/workspace/examples:ro" `
-v "${PWD}/artifacts:/data/artifacts" `
-e RIDDLE_RUN_PROJECT=/workspace/examples/projects/wikipedia.json `
-e GROQ_API_KEY `
riddlerun/riddlerun:latest `
run /workspace/examples/tests --test-id wikipedia-python-language-es --llm-provider groq --llm-model groq/llama-3.3-70b-versatileSupported providers include openai, anthropic, google, mistral, ollama, browser-use, vercel, and LiteLLM aliases such as groq, openrouter, together, azure, bedrock, lmstudio, and openai-compatible.
The web UI and API are built from this repository only; they are not published as Docker Hub images. Run the full stack with Docker Compose:
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| API docs | http://localhost:8000/docs |
| Postgres | localhost:5432 |
On first visit, create the local admin account at /setup. The web UI lets you manage projects, author test cases, store provider keys, and inspect run history.
Run CLI commands through Compose:
docker compose --profile cli run --rm cli validate /workspace/examples/tests
docker compose --profile cli run --rm cli run /workspace/examples/testsRun package tests:
docker compose --profile cli run --rm -v ./tests:/app/tests cli sh -lc 'python -m pip install -e ".[dev]" && python -m pytest'.
├── riddlerun/core/ # Shared Playwright / browser-use execution engine
├── backend/ # FastAPI API and persistence layer
├── cli/ # Docker-first command-line entrypoint
├── docs/ # Project and JSON spec documentation
├── examples/ # Example projects and test case files
├── frontend/ # Next.js TypeScript web UI
├── media/ # Logo, banner, and example run recordings
├── schemas/ # JSON schemas for projects and test cases
├── docker-compose.yml # Docker-only development and self-hosted stack
├── Dockerfile # CLI image
├── Dockerfile.api # API image
└── pyproject.toml # Python package metadata for container builds
A production-oriented Compose stack lives in docker-compose.prod.yml. Copy .env.production.example to .env.production, replace every placeholder secret, set the public frontend and API URLs, then start the stack:
cp .env.production.example .env.production
docker compose --env-file .env.production -f docker-compose.prod.yml up --build -dWhen RIDDLE_RUN_ENV=production, the API refuses to start if RIDDLE_RUN_APP_SECRET is still a placeholder or shorter than 32 characters. The production stack uses named volumes for Postgres data and run artifacts; put TLS and public host routing in front of the exposed API and frontend ports with your reverse proxy of choice.
RiddleRun is licensed under AGPL-3.0-only. A commercial license may be offered later for organizations that cannot use AGPL software.
Contributions are welcome. We are especially happy to accept improvements that expand test coverage, add realistic example flows, or make RiddleRun easier to run in CI and self-hosted environments. For questions: raffaelschoERASECHARSCAPITALIZED@gmail.com


