Architecture
Straden is a Laravel 13 / Livewire application packaged as a single Docker image. At deploy time that image runs as four services, alongside three stock data stores. See the interactive diagram for a visual walkthrough.
Services
| Service | Image | Role |
|---|---|---|
app | straden | Web UI and MCP server on FrankenPHP (Caddy + PHP 8.4). Handles HTTPS, proxies WebSockets to Reverb, runs migrations on boot. |
reverb | straden | Laravel Reverb WebSocket server. |
runner | straden | Laravel Horizon queue worker. Executes k6 (v2.0.0), AI agent jobs and repository syncs. |
scheduler | straden | php artisan schedule:work — polls running tests. |
postgres | postgres:17-alpine | Application database. |
redis | redis:7-alpine | Queues, cache and sessions. |
influxdb | influxdb:1.8 | k6 time-series metrics (database k6). |
The image entrypoint reads STRADEN_ROLE (app, reverb, runner or scheduler) and becomes that process. All four Straden services share the storage volume, which holds scripts, run logs, cloned repositories and the generated instance secrets.
Request path
Browsers talk only to the app container. Caddy serves the Livewire UI and forwards /app/* and /apps/* to Reverb, so WebSockets use the same origin as the page (wss://your-domain/app/...). There is no separate websocket port to expose and no environment baked into the frontend build.
MCP clients call POST /mcp on the same container, authenticated with a Sanctum token.
Life of a run
-
Queued — starting a script (UI or MCP
start-run) creates arunsrow with statusqueuedand dispatchesRunTestJobto therunsqueue. -
Running — Horizon on the
runnerpicks the job up, resolves the script'soptionsinto a run config and starts k6 in its own session:BASHk6 run script.js \ --summary-export=<run>.json \ --summary-trend-stats='avg,min,med,max,p(90),p(95),p(99)' \ --out influxdb=http://influxdb:8086/k6 \ --tag run_id=… --tag test_id=… --tag script_id=…TARGET_URLis passed as an environment variable. The k6 PID, console log and exit code are written to files on the shared volume. -
Live view — the run page tails the log and charts metrics straight from InfluxDB. Status changes and notifications arrive over Reverb.
-
Finalised — every 30 seconds the scheduler queues
CheckRunningTestsJob. When a k6 process has exited, the summary is parsed and the exit code mapped to a status:k6 exit code Status 0passed99,104failed(thresholds crossed)105,130,137,143abortedanything else errorA run whose only problem was a
teardown()timeout after a clean load (0% failed requests) is stillpassed, with a warning. -
Insight — optionally,
GenerateRunInsightJobasks the Run Insight agent for a structured report.
Background jobs
| Job | Queue | Purpose |
|---|---|---|
RunTestJob | runs | Start k6 for a queued run. One at a time per script. |
CheckRunningTestsJob | runs | Finalise runs whose k6 process has exited. Scheduled every 30s. |
CancelRunJob | runs | Send SIGTERM to a running k6 process (runs on the runner, where the process lives). |
ChatAgentJob | default | Run a Test / Script agent turn and broadcast the streamed response. |
GenerateRunInsightJob | default | Produce a run insight report. |
SyncRepositoryJob | default | Clone or pull a repository and index its file tree. |
Realtime channels
| Channel | Used for |
|---|---|
App.Models.User.{id} | Notifications (run completed, insight ready, sync finished…) |
test.{testId} | Streamed agent chat events for a test |
project.{projectId} | Repository sync progress |
Data model
User → Project → Test → Script → Run → RunInsight. Projects also own Repository and Connector records; connectors can be attached to individual tests. AI provider credentials and settings are instance-wide and encrypted with APP_KEY.