This prototype packages a small Java supervisor as a native application (macOS, Linux, Windows). It starts:
- Selenium Server in standalone mode;
- the Cerberus Selenium extension through
--ext; - a temporary Cloudflare Quick Tunnel targeting the local Selenium endpoint.
It then displays the state and logs at http://127.0.0.1:18080. Docker and a system-wide Java installation are not required by the packaged application.
- JDK 21 or newer on
PATH(java,javac,jar,jlink, andjpackage), pluscurl(macOS/Linux) or PowerShell with internet access (Windows); - network access to whatever URLs
dependencies.txtpoints at; - on Windows, the WiX Toolset v3 on
PATHfor the.exeinstaller.
Each build script must run on its target OS - jpackage does not cross-compile. Build once per OS (and once per Mac architecture if both Apple Silicon and Intel are required).
dependencies.<os>.txt (repo root - one file per OS: dependencies.mac.txt, dependencies.linux.txt, dependencies.windows.txt) declares every third-party binary that OS's build script fetches, one <key>=<url> line per dependency. Each build script only reads its own manifest and looks up the keys it needs (e.g. seleniumServer, cloudflared), saving the download under its own fixed destination filename - the one the app expects. Only the url side ever needs updating when a dependency's version or source changes, and it can point anywhere reachable with curl/Invoke-WebRequest (this delivery server, an artifact registry, or a public release page), whatever filename the source actually uses. See the comments at the top of dependencies.mac.txt for the full format.
cerberusRobotProxy, mitmdumpLinux and mitmdumpWindows are only downloaded when CERBERUS_ROBOT_PROXY=true (Robot Proxy feature). On macOS, mitmdump is deliberately not bundled even then: jpackage's ad-hoc re-signing pass breaks mitmproxy.app's own already-signed binaries (and re-signing it ourselves still gets it killed by the hardened runtime at launch). Run brew install mitmproxy on the Mac before enabling the Robot Proxy - the app's UI shows this reminder when the Robot Proxy is enabled on macOS.
# macOS
chmod +x build-macos.sh
./build-macos.sh
# Linux
chmod +x build-linux.sh
./build-linux.sh
# Windows (PowerShell)
./build-windows.ps1Outputs:
- macOS:
dist/Cerberus Local Runner.appanddist/Cerberus Local Runner-1.0.1.dmg - Linux:
dist/Cerberus Local Runner/(app-image) and a.debpackage - Windows:
dist/Cerberus Local Runner/(app-image) and a.exeinstaller
For an initial unsigned build, macOS Gatekeeper may require a right-click followed by Open. For wider distribution, sign and notarize the application. The build script supports CERBERUS_MAC_SIGN_IDENTITY when a Developer ID Application certificate is available.
Pushing a tag matching v* (e.g. v1.0.1) triggers .github/workflows/release.yml, which builds all three platforms (with the Robot Proxy feature bundled) and uploads the resulting .dmg/.deb/.exe to a GitHub Release named after the tag. The macOS job runs on macos-14 (Apple Silicon) - unsigned, since CERBERUS_MAC_SIGN_IDENTITY isn't configured in CI.
The following command compiles and launches the application with simulated Selenium and Cloudflare processes:
chmod +x run-dev.sh
./run-dev.shOpen http://127.0.0.1:18080, then use Start local runner and Stop.
On first launch, the application creates:
~/Library/Application Support/Cerberus Local Runner/config.properties
Relevant properties:
ui.port=18080
selenium.port=4444
selenium.jar=selenium-server.jar
extension.jar=cerberus-extension.jar
cloudflared.binary=cloudflared
cloudflared.mode=quick
cerberus.callbackUrl=
cerberus.callbackBearerToken=
runner.id=
autostart=false
openBrowser=trueRelative component paths are resolved from the application directory. runner.id is generated automatically if empty.
When cerberus.callbackUrl is configured, the runner sends this JSON after the tunnel becomes ready:
{
"runnerId": "local-runner-uuid",
"tunnelUrl": "https://random.trycloudflare.com",
"seleniumUrl": "http://127.0.0.1:4444",
"status": "READY"
}The callback can be protected with cerberus.callbackBearerToken. For the production version, replace the static token with a short-lived token obtained through the authenticated Cerberus pairing flow.
Quick Tunnels are useful for the prototype because they return a temporary random URL. A managed tunnel can be selected with:
cloudflared.mode=named
cloudflared.token=the-short-lived-or-managed-token
cloudflared.publicUrl=https://runner.example.comThe Cloudflare route must already map the public hostname to http://127.0.0.1:4444.
This first prototype exposes the Selenium endpoint through the tunnel. It must only be used with an authenticated Cloudflare route or a server-controlled, short-lived tunnel. The target architecture should move execution into the Local Runner and keep only an outbound WebSocket connection to Cerberus.