🧙 websummoner 1.0.0
The hub itself: receives WebDriver requests, summons a browser into a Docker container for each session and proxies the session to it. Everything below is a change to this repository's code since the fork from Aerokube Selenoid (last upstream commit 17 December 2024); the release story across all projects is in the announcement.
⚠️ Breaking
- Go module path is
github.com/websummoner/websummoner; the binary iswebsummonerand the imagewebsummoner/websummoner. - The Docker image's entrypoint reads
/etc/websummoner/browsers.jsonand writes video to/opt/websummoner/video. Update the two mount targets, or pass your own-confand-video-output-dir. -video-recorder-imagedefaults towebsummoner/video-recorder:latest-release.- For
opera,yandexandbravesessions the hub replacesgoog:chromeOptionswith its own binary and arguments (see Session handling); clients cannot pass Chromium flags for those three browsers. Chrome and Edge are untouched.
🤝 Compatibility kept, permanently
selenoid:optionsis still merged; the newwebsummoner:optionsblock wins when both are present.X-Selenoid-No-WaitandX-Selenoid-Fileare accepted alongsideX-WebSummoner-No-WaitandX-WebSummoner-File.- The
/wd/hub/session/<id>/aerokube/...vendor path is accepted alongside/websummoner/....
🔀 Session handling
- A full queue with either no-wait header set answers
429 Too Many Requestsinstead of a generic 500 (RFC 6585; upstream issue aerokube#1203). Header presence is what matters, since GGR sends it with an empty value. - Deterministic version lookup in
browsers.json: an exact key wins, otherwise the longest key the requested version is a prefix of. Upstream took the first prefix match in map order. - Vendor blocks and the legacy
versionrouting hint are stripped before the request reaches the driver; modern drivers reject unknown capabilities (upstream issue aerokube#909). adaptDriverCapabilities: sessions foropera,yandexandbraveare forwarded asbrowserName: chromewith the right binary (/usr/bin/opera,/usr/bin/yandex-browser,/opt/brave.com/brave/brave-browser),--no-sandboxand a per-request--user-data-dir; Yandex gets--homepage=about:blank --no-first-run --no-default-browser-check; Opera getsgoog:chromeOptions.w3c: true, because operadriver answers in JSONWP unless asked for W3C. Routing still happens on the real browser name, so a browser fix here applies to images built long before it.- JSONWP replies are wrapped into the W3C envelope; a JSONWP reply with a non-zero
statusis treated as an error rather than a session, and a reply without asessionIdreturns a clear error instead of panicking. se:cdpandse:cdpVersionare advertised only for Chromium browsers; advertising a CDP endpoint for Firefox or WebKit made clients fail their first WebSocket handshake.- A session that fails to start removes the temporary video and log files it had reserved.
- The reverse proxy uses
httputil.ProxyRequest.Rewriteand setsX-Forwarded-*; theDirectorAPI is gone. - Start-up returns errors instead of calling
log.Fatalfrom three places, and signal channels are buffered, so shutdown drains the server, closes the Docker client and reports once.
📈 Observability
/metricsin Prometheus text format:websummoner_sessions_active,websummoner_sessions_limit,websummoner_queue_depth,websummoner_queue_pending,websummoner_browser_sessions{browser="…"}, and the counterswebsummoner_sessions_created_total,websummoner_sessions_failed_total,websummoner_sessions_timed_out_total,websummoner_sessions_deleted_total,websummoner_video_sessions_total,websummoner_vnc_sessions_total,websummoner_audio_sessions_total.HEAD /pinganswers200;HEAD /statusanswers200when a slot is free and503when the hub is full. The old handlers servedGETonly, which broke Kubernetes probes and load-balancer checks.
📤 Files, video and audio
- File upload for containerised browsers is served by the hub, on
/session/<id>/fileand on the/session/<id>/se/filepath Selenium 4 actually sends (the old interception matched only the legacy four-fragment form, so it never fired). The hub unpacks the zip and places the file at/opt/websummoner/uploads/inside the browser container by streaming a tar totar -xrunning in the container, which honours tmpfs and volume mounts and leaves the file owned by the browser's user; images withouttarfall back to the Docker copy API, and atest -fin the container confirms the file is visible before the path is handed to the driver.-enable-file-uploadgates the feature as before. enableAudiocapability (defaulttrue) controls the audio track of recordings:ENABLE_AUDIOon the browser container,DISABLE_AUDIO=trueon the recorder when off.- The W3C
proxycapability is translated intohttp_proxy,https_proxyandno_proxyon the browser container forsafarisessions, because WebKitWebDriver ignores the capability;localhost,127.0.0.1and::1are always excluded so the driver's own connection is never proxied.
🔒 Hardening
videoNameandlogNamemust be plain file names; anything with a path element is rejected withinvalid argument. The video delete endpoint applies the same check.- Video and log output directories are created
0755; upstream created them0644, which is not traversable. - The Docker client negotiates the API version again after the compatibility probe instead of staying pinned to the minimum the probe left behind.
SECURITY.mddocuments the reporting process and that anyone who can reach the hub can start containers.
📦 Dependencies
| Was | Now | |
|---|---|---|
| Go | 1.22 | 1.27 |
github.com/docker/docker |
v26.1.5 | v28.5.2 (types.ContainerJSON → container.InspectResponse and friends) |
github.com/docker/go-connections |
v0.5.0 | v0.8.1 |
github.com/imdario/mergo |
v0.3.15 | dario.cat/mergo v1.0.2 |
github.com/mafredri/cdp |
v0.34.1 | v0.35.0 |
golang.org/x/net |
v0.25.0 | v0.58.0 |
github.com/gorilla/websocket |
v1.5.1 | v1.5.3 |
github.com/stretchr/testify |
v1.9.0 | v1.12.1 |
github.com/aws/aws-sdk-go |
v1.53.5 | v1.55.8 |
| GGR config types | github.com/aerokube/ggr |
github.com/websummoner/ggr |
🏗️ Build, CI and supply chain
golangci-lintgates every push and pull request:errcheck,govet,staticcheck,ineffassign,misspell,unusedandgofmt; the tree passes with the full set enabled.govulncheckruns in CI with a narrow, documented allowlist (ci/govulncheck.sh): two Moby daemon-side advisories with no affected symbols and no fixed version. Anything else fails the build.- Release workflow: binaries for linux amd64/arm64, darwin amd64/arm64 and windows amd64/arm64/386, all
CGO_ENABLED=0; the image built forlinux/amd64andlinux/arm64; an SPDX SBOM generated with Syft and attached to the release; the image signed with cosign (keyless). Docker Hub login reads the password from stdin, the repository name is lower-cased, and theselenoid/hubtag is no longer pushed. - Test workflow with the race detector and coverage to Codecov and DeepSource; a lint workflow; a docs workflow; the stale-issue workflow on
actions/stalev11; Dependabot weekly with grouped pull requests for Go modules, the Docker base image, GitHub Actions and the documentation site's npm packages. AGENTS.mddescribes the workspace, the dockerised toolchain and the contribution rules;ci/docs.shand the AsciiDoc pipeline are gone.
🧪 Tests
New test files cover driver mode end to end (driver_mode_test.go), the HEAD probes (health_test.go), graceful start and shutdown (run_test.go), the metrics format and counters (metrics_test.go), the queue's no-wait and 429 paths (protect/queue_test.go), capability adaptation, JSONWP wrapping, CDP advertising, safe file names and vendor-option stripping (safety_test.go, branches_test.go), plus unit tests for info and jsonerror. Coverage is reported for every package.
📚 Documentation
The AsciiDoc fragments under docs/ are replaced by docs-site/, an Astro Starlight site — 30 pages, full-text search, dark mode, an llms.txt export — deployed to GitHub Pages at websummoner.riadvice.com/websummoner by its own workflow, which also builds every pull request. New pages include Migrating from Selenoid, a source-checked comparison with Selenoid, Moon, Selenium Grid, Selenide, Playwright and Puppeteer, Browser images with per-browser behaviour, Image tags, Building browser images, Docker Compose, S3 upload and a FAQ. Analytics load only when a measurement id is configured, and then only after consent.
WebSummoner 1.0.0 · 📖 Documentation · 💬 Discussions
WebSummoner is developed and maintained by RIADVICE under Apache-2.0. It builds on Selenoid by Aerokube and Ivan Krutov, and on the original UI by Kirill Merkushev.