Browser Operator - The AI browser with built in Multi-Agent platform! Open source alternative to ChatGPT Atlas, Perplexity Comet, Dia and Microsoft CoPilot Edge Browser
Developers need a locally‑run AI‑augmented browser that can host multiple autonomous agents without sending user data to a remote service. Existing commercial options lock users into proprietary clouds, making privacy, compliance, and custom model integration difficult.
What This Does
browser-operator-core ships a Chromium‑based front‑end (front_end/entrypoints/main/main.ts) together with an agent server (agent-server/nodejs/src/api‑server.js) that exposes a JSON‑RPC API for AI providers. The UI loads front_end/core/common/App.ts, which registers panels such as front_end/panels/ai_chat/cdp/index.ts.
Configuration lives in .env.example (root) and agent-server/.env.example; they define provider URLs and model keys. The Docker assets (docker/Dockerfile, docker/docker‑compose.yml) allow the whole stack to be built into a single container for reproducible deployment.
How It Is Wired
Startup – The container entry point runs docker/Dockerfile → node agent-server/nodejs/start.js. That script loads agent-server/nodejs/src/config.js (reads .env.example) and starts the HTTP server (src/api‑server.js).
Browser launch – The front‑end is served by the same Node process; the browser loads extensions/cxx_debugging/src/index.html or the packaged UI entry front_end/entrypoints/main/main.ts.
Panel activation – When the user opens the AI Chat panel, front_end/panels/ai_chat/a11y/index.ts registers UI callbacks that eventually call src/lib/BrowserAgentServer.js.
Agent request flow – BrowserAgentServer.js builds a request object and forwards it to the selected LLM judge (src/lib/judges/LLMJudge.js). The judge uses the provider key from the environment to call the external API.
Response handling – The result is streamed back through HTTPWrapper.js → RequestStack.js → the UI panel, where it is rendered.
The static call‑graph only resolved four intra‑repo edges (e.g., test → getX), indicating that most of the runtime path traverses external libraries or dynamically loaded modules, which are not captured by the current analysis.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/browser-operator-core
cd browser-operator-core
# Install Node dependencies (uses lockfile)
npm ci
# Copy example env and edit provider keys
cp .env.example .env
# edit .env to add OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
# Build and run the Docker composition (includes UI and API server)
docker compose -f docker/docker-compose.yml up --build
The UI is reachable at http://localhost:3000 (port defined in docker/nginx.conf). To run the server without Docker:
cd agent-server/nodejs
node start.js # reads ./nodejs/.env.example if present
Real‑World Use
A compliance team can launch the container on an isolated workstation, point the UI to an internal LLM endpoint (configured in .env), and create an “audit agent” that crawls a corporate intranet, extracts policy statements, and stores results in a local SQLite file (implemented by a custom extension that calls BrowserAgentServer.js).
Code Health & Issues
Critical – Secrets in workflow: .github/workflows/claude.yml contains CLAUDE_CODE_OAUTH_TOKEN.
High – node_modules/ committed (23953 files).
High – Unpinned GitHub Action: anthropics/claude-code-action@beta.
High – Wildcard CORS (*) with credentials in scripts/component_server/server.js.
Medium – No Dependabot/Renovate configuration.
Medium – CI uses npm install instead of npm ci.
Medium – Docker base images not pinned by digest.
Medium – No dependency‑vulnerability scan in CI.
Medium – Large binaries (e.g., third_party/image_diff/mac/libbase.dylib) exceed 5 MiB.
Medium – Checkout persists credentials in .git/config (.github/workflows/claude.yml).
All findings are from the deterministic static analysis; no additional issues were invented.
The Bottom Line
browser-operator-core provides a functional, locally hosted AI browser with a clear separation between UI and agent server, but the repo suffers from serious security hygiene problems (exposed secrets, committed dependencies, permissive CORS). It is suitable for teams that can remediate these issues and need an extensible, privacy‑first AI browsing platform.
What the analyser found
Deployment readiness
6/7
✓Container image
✓CI pipeline
✓Lockfile committed
✓Test suite
✓README
✓License
✗No committed secrets
Composition
33,770 files
JavaScript12587
TypeScript8625
Markdown1536
JSON1528
CSS480
HTML421
ReactAngularExpressDockerTransformersSparkNeo4j
Findings
LowSecret-shaped paths present; the code-health audit confirms or clears them scripts/hosted_mode/cert.pem, scripts/hosted_mode/key.pem
Ranked by severity × confidence × production reach. Reach is the honest discriminator across a collection that is mostly other people's code: the same finding matters more in something that ships.
critical1
high3
medium7
low1
Keep secrets out of workflows a fork can triggercritical
.github/workflows/claude.yml
CLAUDE_CODE_OAUTH_TOKEN (publish or cloud scope)
Contributor-controlled input reaches a job holding a publish token or cloud credential, so a pull request becomes credential exfiltration.
Fix: Move secret-using steps into a workflow_run job that never checks out pull request code, or gate on an environment with required reviewers.
Remove the committed dependency directoryhigh23953 occurrences
node_modules
23953 files committed under node_modules
The code that actually runs was never resolved from a manifest, so no scanner, lockfile or update bot has any view of it.
Fix: Gitignore the directory, git rm -r --cached it, and reinstall from the lockfile.
Pin third-party GitHub Actions to a commit SHAhigh
.github/workflows
anthropics/claude-code-action@beta
A tag can be moved, so the action running with your token and secrets is whatever its owner last pushed; this is how tj-actions/changed-files leaked secrets from thousands of repos.
Fix: Replace each @vN with the 40-character commit SHA, keep # vN as a comment, and let Dependabot bump the SHAs.
Replace the wildcard CORS origin with an explicit allow listhigh
scripts/component_server/server.js
Access-Control-Allow-Origin', '*'
Any page on the internet can call the API with the browser's cookies attached, so a logged-in visitor to an unrelated site performs authenticated requests without knowing, and the wildcard combined with credentials is the exact configuration browsers refuse for that reason.
Fix: List the origins the API actually serves, and never pair a wildcard with credentials.
Enable Dependabot or Renovatemedium
6 manifest(s), no update bot configured
Without a bot a published advisory sits unpatched until someone audits by hand, which across 1,322 repositories means never.
Fix: Commit .github/dependabot.yml covering the repo ecosystems plus github-actions.
Install from the lockfile in CImedium
.github/workflows
npm install with a committed package-lock.json
A fresh resolution in CI means the tested dependency set is not the locked one, so the failure only appears after merge.
Fix: Use npm ci, yarn install --immutable, or pnpm install --frozen-lockfile.
Pin the container base image by digestmedium3 occurrences
docker/Dockerfile
ubuntu:22.04, node:18-alpine, nginx:alpine
An untagged or mutable base means today's build and last month's contain different libc and a different CVE set, with no record of which shipped.
Fix: Use image:tag@sha256:<digest> and enable Dependabot's docker ecosystem.
Gate pull requests on a dependency vulnerability scanmedium
.github/workflows
no dependency scan in CI
This is the one gate that would catch a known-vulnerable package before it reaches a build, and no repository in the sample had it.
Fix: Add dependency-review-action on pull_request, or osv-scanner on push and a schedule.
Move large binaries to Git LFS or out of the repositorymedium15 occurrences
third_party/image_diff/mac/libbase.dylib
15 blobs over 5MB: libbase.dylib 18.4MB, yahoo-news.json.gz 16.2MB, web-dev-with-advanced-instrumentation.json.gz 16.1MB
One repository here carries twenty blobs over 5MB including a 10.9MB spreadsheet, so every clone and every CI checkout pays for data nobody diffs.
Fix: Track those extensions with LFS, or move datasets to object storage and fetch them in a setup step.
Set persist-credentials: false on checkoutmedium
.github/workflows/claude.yml
checkout keeps the token, then dependencies are installed
The token stays in .git/config for every later step, so a malicious postinstall script reads a pushable credential without one ever being passed to it.
Fix: Add with: persist-credentials: false, and pass an explicit token only to the step that pushes.
Add a non-root USER to the imagemedium
docker/Dockerfile
CMD or ENTRYPOINT with no USER directive
A process running as root in the container is root against every mounted volume, and it turns any container escape or writable-mount mistake from a contained problem into a host one.
Fix: Create an unprivileged user, chown what it needs, and end the Dockerfile with USER.
Set timeout-minutes on the workflow jobslow4 occurrences
.github/workflows/auto-close-prs.yml
4 workflow(s) declare no job timeout
A wedged step runs to the six-hour platform default, which on a two-hourly schedule means three runs overlap behind it.
Fix: Add timeout-minutes with a realistic bound to each job.
Checked deterministically against the repository tree and a bounded set of its files: committed credentials, unpinned actions and base images, missing lockfiles and update bots, workflows that discard failures, published advisories against the declared dependencies, runtime configuration, licensing and notebook reproducibility. No language model is involved in this section.