arena-agent
by IvanSkainet
README.md
<div align="center">
# π Skainet Bridge
**Local automation bridge for AI agents β one process, one port, full control of your machine.**
Turn any AI chat or agent into a hands-on assistant that can run commands, read and edit files, browse the web, remember things, and drive your desktop β all through a single token-authenticated service you run yourself.
One process Β· One port Β· REST + MCP + browser extension Β· Windows / Linux / macOS
**π English Β· [Π ΡΡΡΠΊΠΈΠΉ](README.ru.md)**
[](https://github.com/IvanSkainet/arena-agent/actions/workflows/ci.yml)
[](https://codecov.io/github/IvanSkainet/arena-agent)
[](https://app.codspeed.io/IvanSkainet/arena-agent?utm_source=badge)
[](https://github.com/IvanSkainet/arena-agent/releases/latest)
[]()
[](LICENSE)
</div>
---
## Project at a glance
<!-- BEGIN GENERATED METRICS -->
| Metric | Value |
| --- | --- |
| Runtime code | **115,818** lines across 824 files |
| Test code | **119,689** lines across 605 files |
| Test-to-code ratio | **1.03Γ** |
| CI workflows | **16** |
| Quality gates (ratchets) | **20** |
<sub>Generated by `scripts/render_readme_metrics.py`; CI checks this block is current.</sub>
<!-- END GENERATED METRICS -->
<!-- BEGIN TRAFFIC BADGE -->
-696-brightgreen)
<sub>696 unique cloners over the 14 days to 2026-09-04 (39545 clones total). Refreshed daily by `.github/workflows/traffic-badge.yml`; GitHub only keeps a 14-day window. Deliberately not a release-download count β v4.169.50 showed 349 downloads of one asset against 3 of the byte-identical copy, so that number measures automation, not people.</sub>
<!-- END TRAFFIC BADGE -->
---
## Contents
- [North star](#north-star)
- [Mission: the flight computer](#mission-the-flight-computer)
- [Why Skainet Bridge?](#why-skainet-bridge)
- [How it works](#how-it-works)
- [What it can do](#what-it-can-do)
- [Quick start](#quick-start)
- [Browser extension: Arena Chat Bridge](#browser-extension-arena-chat-bridge)
- [Remote access providers](#remote-access-providers)
- [Optional components](#optional-components)
- [Security model](#security-model)
- [API overview](#api-overview)
- [Development](#development)
- [Documentation map](#documentation-map)
- [License](#license)
---
## North star
This project exists for one reason: to give an AI a real machine to act on β
to let it step out of the sandbox and actually do things in the world (read
files, run commands, drive a phone, control a desktop, talk to other tools),
reliably, for a human who is watching.
Everything in this bridge is in service of that. A capability is only "done"
when it makes the real thing work for the person at the other end β not when
a test turns green or a release tag is pushed. Green CI and shipped tags are
not success by themselves; they are self-deception if the human watching does
not see the real thing work and make sense.
This project is built largely by AI, for AI, and is maintained autonomously.
The human in the loop is the observer and the reality check: the one node
connected to whether the thing actually works. When something drifts into
looking like success without being real, catching that is the maintainer's
job; when the observer catches it, that is the most important signal in the
loop, not a nuisance.
> If you are an AI continuing this work: hold the whole map yourself and keep
> the pieces coherent. Measure your work by "does the observer see the real
> thing work?", never by "did my tests pass?". Do not bolt features onto
> whatever module is convenient β place them where they belong. Do not use a
> volume of releases as a proxy for progress.
## Mission: the flight computer
The framing that organizes this project: **Skainet Bridge is the onboard
computer of a spacecraft.** The AI agent is the pilot; the machines it reaches
β a PC, a phone, a browser, a network β are the ship's sensors, actuators and
subsystems. The observer is mission control. This is not decoration; it sets the
engineering bar. A flight computer must be *general* (handle the unknown, not a
fixed checklist), *reliable* (a green test is not proof of flight-worthiness),
*self-diagnosing* (know its own health and limits), *fail-safe* (a risk policy
standing between the pilot and the hull), and *autonomous* (act across many
steps without a human at each one).
It also places the project on a real, named trajectory in how AI systems are
built. The discipline has moved through four stages:
1. **Prompt engineering** (2022β2024) β perfect the single instruction.
2. **Context engineering** (2025, Karpathy) β design what the model *sees*:
retrieval, memory, tool definitions. "Context is RAM; the model is the CPU."
3. **Harness engineering** (2026, Hashimoto / OpenAI) β build the whole
*operating environment* around the agent: tools, constraints, feedback loops,
lifecycle. "Agents aren't hard; the harness is hard."
4. **Dynamic / self-evolving environment** (the frontier) β the agent no longer
just *uses* a fixed environment; it *extends and authors* it, writing new
tools and capabilities for itself at runtime, within guardrails.
Arena is already a harness: one runtime that unifies sensors (screenshot,
camera, microphone, device sensors, file read), actuators (input, exec, file
write, desktop control), pluggable subsystems (MCP servers) and a risk policy.
The mission is to push it down the last step β toward a **self-extending flight
computer**: an environment the agent grows itself. The first concrete step is
already in: with `custom.create` the agent authors a new named tool at runtime
(a schema-validated wrapper over a built-in tool, e.g.
`custom.create` β `{tool: "fs.read", args: {path: "{path}"}}`); the tool then
shows up in `tools/list`, is callable, persists in `mcp/custom_tools.json`, is
revocable via `custom.remove`, and inherits the risk of the tool it wraps.
Listing them is `custom.list`. With `code.run` the agent runs code it authors, fenced by the operator's composable execution posture (`/v1/autonomy/posture`: the sandbox/network/privilege/filesystem/runtime "cubes"); the runner is fail-closed (refuses rather than runs unfenced) and the agent cannot loosen its own fence. Beyond that, installing a vetted external MCP
server (`mcp.add`) grows the environment further; the direction is the agent
closing a capability gap found in a real scenario, generally, so it is gone for
good.
> The project's founding idea β to step out of the sandbox β is written down
> here. If you are an AI continuing this work, this is the heading: build the
> ship's computer, not a pile of remote-control tricks.
---
## Why Skainet Bridge?
Most "AI + your computer" setups mean juggling several servers: one for MCP, one
for a REST API, one for browser control, one for the web UI. Skainet Bridge
folds all of that into **a single local process** that you start once and point
your tools at.
- **Local-first.** Bind it to `127.0.0.1` and nothing leaves your machine. Expose
it deliberately β via Tailscale Funnel or another HTTPS tunnel β only when you
actually need remote access.
- **Protocol-agnostic.** REST, MCP, WebSocket/SSE events, and a browser extension
all talk to the same runtime.
- **Safe by design.** A bearer credential, path restrictions, shell safety
patterns, and explicit risk policies stand between an AI and your host.
- **Works with the chats you already use.** The companion browser extension lets
plain ChatGPT / Claude / Gemini conversations trigger real local tools.
---
## How it works
```text
βββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
β AI chat / agent β β Arena Chat Bridge ext. β β Arena Unifiedβ
β ChatGPT Β· Claude β βββΆ β or MCP / REST β βββΆ β Bridge β βββΆ your machine
β Gemini Β· your CLI β β β β (local:8765) β
βββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
emits a detects / forwards runs the
structured tool block the tool call safely guarded action
```
An assistant emits a structured tool block, the extension (or an MCP/REST client)
forwards it to the local bridge, the bridge runs the guarded action, and the
result flows back β optionally straight into the chat composer.
---
## What it can do
| Area | Capability |
| --- | --- |
| **Shell** | Command execution, allow-listed by default (`--profile cautious`); see [security model](#security-model) |
| **Files** | Read, search, and precise edit helpers with path restrictions |
| **Web** | Fetch / read / search page text on behalf of the agent |
| **Memory** | Persistent facts plus fuzzy recall |
| **Tasks** | Background task queue for long-running work |
| **Browser** | Chrome DevTools Protocol control for real browser automation, plus stealth workflows via [BrowserAct](#optional-components) |
| **Desktop** | Screenshots and input automation where the platform supports it |
| **Dashboard** | Built-in web UI at `/gui` with a dedicated **π Transports** tab that manages every provider side by side (per-transport start/stop, autostart-on-boot toggle, live log tail) |
| **Extension** | Connects ordinary AI chats to the local bridge with a lifecycle Command Center |
| **Remote access** | Unified [`/v1/tunnels/*` facade](#remote-access-providers): Tailscale, Cloudflare Quick Tunnel and ZeroTier as a single failover-aware pool |
| **Skills** | Discovers and lists tool-skill packages (Arena core + upstream [`superpowers`][obra] + [`browseract`](#optional-components)) via `/v1/skills` |
| **Security** | Bearer auth with rate-limit + TLS strict verify by default + optional cert pinning + HMAC-signed URL cache + emit-site log redaction + sandbox blocklist for `.ssh/`/`.aws/`/`.gnupg/`/credentials β see [`SECURITY.md`](SECURITY.md) |
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
[obra]: https://github.com/obra/superpowers
---
---
## Security model
Two profiles, and the difference between them is the whole story.
**`--profile cautious` (default).** A first-word allowlist. It also refuses
shell metacharacters and interpreter code-string flags, because
`echo ok; curl evil` and `bash -c 'curl evil'` both defeat a first-word
check. This is a real boundary.
**`--profile owner-shell`.** No allowlist β the operator has deliberately
handed the bridge their desktop. What remains in front of `/v1/exec` is a
denylist of destructive spellings, and **a denylist of spellings is not a
security boundary**. It stops `rm -rf /`; it does not stop `r''m -rf /`,
`$(echo cm0K | base64 -d) -rf /`, or `python3 -c "shutil.rmtree(...)"`,
all of which erase the same data. Those bypasses are pinned in
`tests/test_security_commands.py` as known and accepted, so the gap is
documented rather than discovered.
Run `owner-shell` only where you would hand someone the keyboard.
## Current flight status (v4.140.x)
Arena is now a **self-extending agent environment**, not just a fixed tool
server. Recent live scenarios proved the bridge can grow new hands at runtime:
- **Ship Status / Preflight is now the top-level map.** `ship.status` aggregates bridge health, operator posture, transports, external MCP/desktop servers, BrowserAct/CDP, mobile/ADB, Code Workbench, known issues, and next actions. `ship.preflight` summarizes fail/warn readiness before real missions.
- **Tool Foundry v1 connects projects to callable tools.** A Code Workbench project can carry `.arena-tool.json` with input schema, run recipe and tests. `tool_foundry.validate` proves it; `tool_foundry.publish` creates a callable `custom.<name>` wrapper around `code_project.run`.
- **Experiments can be promoted directly.** `code_project.promote_tool` and `code_run.promote_tool` generate a Foundry manifest from a known-good recipe/tests, validate it, and publish the resulting `custom.<name>` capability without hand-writing `.arena-tool.json`.
- **Project dependency cache can now stay fenced.** Python `code_project.run(use_project_deps=true)` can run in Windows AppContainer by granting only the project `.deps/python` cache read/execute, while writes remain scratch-only and network stays denied.
- **Runtime compatibility is now machine-readable.** `runtime.compat` reports runtime Γ sandbox support/blockers (for example Python/AppContainer supported, Node/Go AppContainer blocked, Rust linker incomplete) with reasons and next actions used by Workbench status.
- **WASM runtime slice is available.** `runtime.install` can install managed Wasmtime with SHA-256 verification, `runtime.compat` maps `wasm`/`wasmtime`, and `code.run` / `code_project.run` accept `lang=wasm` for WASI command modules.
- **Code Sessions now have files and artifacts.** Long-running Python sessions can read/write files in their cwd, list files, and persist declared artifacts into the normal Workbench artifact store.
- **Code Session lifecycle is hardened.** Sessions now expose pid/returncode/max-session status, enforce a configurable live-session cap, and can be swept by idle/age threshold with terminate-then-kill diagnostics.
- **AppContainer Sessions prototype exists.** With `sandbox=appcontainer`, Python code sessions can start in a replay-backed fenced mode: each exec goes through AppContainer `code.run`, preserving globals by transcript replay while keeping session files/artifacts available.
- **Project dependency locking is available.** `code_project.deps_install` writes `.arena-lock.json`; `code_project.lock_verify` checks current caches; `code_project.run(lock="strict")` refuses mismatched dependencies, and Foundry tools can carry lock provenance.
- **Managed Deno runtime is available.** `runtime.install runtime=deno` installs official Deno with SHA-256 verification, and `lang=deno` runs TypeScript/JavaScript with denied network and scratch-local runtime state for stdout-oriented scripts; Deno file writes in AppContainer remain a known hardening item.
- **External MCP servers** can be installed and called through `mcp.add`,
`mcp.ext_tools`, and `mcp.ext_call`. Live-proven servers include
Desktop-Commander, ScreenPilot, and the official
`@modelcontextprotocol/server-sequential-thinking`.
- **Hung external MCP calls are contained.** `mcp.ext_call` accepts a `timeout`
and the MCP stdio client uses a background reader thread; if an external
server stops responding, it is stopped and the bridge HTTP event loop remains
responsive. This was added after Desktop-Commander froze an older bridge hard
enough to require reinstalling the service.
- **Agent-authored code runs under operator posture.** On Windows, fenced
`code.run` engages AppContainer with no capabilities, grants only scratch
modify + runtime read/execute, captures stdout/stderr, denies outbound TCP,
and denies user-profile files outside scratch. On Linux the strict path uses
`systemd-run` when available. If a requested fence cannot be enforced, the
runner fails closed. `code.run` can execute a multi-file scratch workspace
(`files` + `entry`), pass `argv`/`stdin`, install scratch-local Python, Node/npm, or Go module
dependencies when the operator selects `network=open`, and return declared
`artifacts`.
- **Runtime expansion is real.** In live Windows tests the bridge executed
Python (AppContainer), JavaScript/Node, PowerShell, C# via `Add-Type`, and
Java single-file source mode, creating proof artifacts on disk.
- **Browser stack is layered.** `browser.search` / `browser.read` are pure-Python
fallback tools; `/v1/browser/browse` uses CDP by default and BrowserAct when
`stealth=true`. BrowserAct is launched through the cross-platform Python
wrapper, not a bash-only entrypoint.
Known honest limits:
- Windows AppContainer is not a VM. It protects user files and network by
default, but normal world-readable system files may still be visible; memory
limits are still enforced by the outer runner rather than AppContainer itself.
- Node currently enters AppContainer but may fail during startup because it
probes `C:\` and receives `EPERM`; Python is the live-proven fenced runtime.
- CDP/headless browser control is CI-covered on multiple OSes, but Windows
service sessions can still fail to launch Edge/Chrome headlessly depending on
service elevation and desktop/session isolation. When that happens the bridge
should report the failure and keep running, not fake success.
- Posture is operator-owned. YOLO removes approval prompts, not the sandbox. The
agent must never be able to move its own posture cubes.
---
## Quick start
### 1. Download a release
Grab the latest ZIP:
```text
https://github.com/IvanSkainet/arena-agent/releases/latest
```
Extract it somewhere convenient:
```text
C:\Users\You\arena-bridge # Windows
~/arena-bridge # Linux/macOS
```
### 2. Run the installer
```cmd
:: Windows
install.bat
```
```bash
# Linux / macOS
chmod +x install.sh
./install.sh
```
The installer creates a local bearer credential in `token.txt`, prepares runtime
directories, and asks before installing any optional component.
### 3. Verify the bridge
```bash
curl http://127.0.0.1:8765/health # health check
curl http://127.0.0.1:8765/v1/version # version + platform
```
Open the dashboard at:
```text
http://127.0.0.1:8765/gui
```
### 4. Hand your AI the URL and credential
```text
Base URL: http://127.0.0.1:8765
Auth: Authorization: Bearer <credential from token.txt>
```
For remote access, enable an HTTPS tunnel only if you understand the exposure
model. Tailscale Funnel is recommended: it gives you a real TLS hostname without
port forwarding.
---
## Browser extension: Arena Chat Bridge
The extension is an **Arena-native bridge for normal web chats**. It watches
assistant messages for structured tool blocks, previews/executes them through the
local bridge, and can insert the result back into the chat composer.
**Supported adapters:** ChatGPT Β· Claude Β· Gemini Web Β· Google AI Studio Β· Grok Β·
Perplexity Β· OpenRouter Β· DeepSeek Β· Kimi Β· Qwen Β· generic fallback.
**Canonical payload:**
````text
```arena-tool
{
"bridge": "arena",
"version": 1,
"calls": [
{"id": "call_1", "tool": "sys.status", "arguments": {}}
]
}
```
````
MCP SuperAssistant-style JSONL is also accepted and normalized internally.
**Load it for development:**
1. open `chrome://extensions`;
2. enable **Developer mode**;
3. click **Load unpacked**;
4. select `chat_extension/`.
More detail: [chat_extension/README.md](chat_extension/README.md).
---
## Remote access providers
Skainet Bridge treats **Tailscale**, **Cloudflared**, and **ZeroTier** as
one pool of remote-access providers with a configurable priority and automatic
failover. If your primary tunnel drops, the Bridge stays reachable through the
next healthy provider β a single outage does not take the Bridge offline.
```bash
# See every provider at once (installed, active, public URL, cli source, hints)
curl -sH "Authorization: Bearer $(cat ~/arena-bridge/token.txt)" \
http://127.0.0.1:8765/v1/tunnels/status | jq
# Just tell me where clients should connect right now
curl -sH "Authorization: Bearer $(cat ~/arena-bridge/token.txt)" \
http://127.0.0.1:8765/v1/tunnels/active
# Bring providers up in priority order, stop on first healthy
curl -sH "Authorization: Bearer $(cat ~/arena-bridge/token.txt)" \
-X POST http://127.0.0.1:8765/v1/tunnels/start
```
Priority defaults to `tailscale > zerotier > cloudflared > ngrok > bore` (v4.47.0)
and can be overridden with e.g. `ARENA_TUNNEL_PRIORITY=cloudflared,zerotier`
(unmentioned providers keep their default position).
Each provider works out of the box on Windows, macOS, and GNU/Linux β no sudo
wrappers or platform-specific hacks required. ZeroTier is discovered via the
local HTTP API at `127.0.0.1:9993` with fallback to `zerotier-cli` from PATH,
Program Files, `/Library/Application Support/`, `/usr/sbin/`, etc. Cloudflared
install/update hints are tailored per platform (`winget`/`scoop`/`brew`/
`pacman`/`apt`). ngrok reads `ARENA_NGROK_AUTHTOKEN` (free tier requires one).
bore (v4.47.0) is the zero-account fallback: `cargo install bore-cli` or a
GitHub release binary drop β no signup, no dashboard cookie; TCP-only relay
through `bore.pub` (override via `ARENA_BORE_SERVER` for self-hosted).
The dashboard's dedicated **π Transports** tab exposes the same facade with
per-transport start/stop buttons, autostart-on-boot toggles (with an
`env-override` pill when `ARENA_<TRANSPORT>_AUTOSTART` is set from the service
unit), copy-URL buttons and a live log tail on transports that stream stdout
(cloudflared / ngrok / bore). ZeroTier network membership (join/leave by nwid,
list of joined networks, install/permission hints) has its own **π ZeroTier**
tab.
---
## Optional components
The bridge runs locally with just Python and `aiohttp`. Some features want extra
tools β and none of them are installed silently; the installer always asks first.
| Component | Purpose | Install |
| --- | --- | --- |
| **Tailscale** | Zero-config HTTPS exposure via Funnel | System-level: <https://tailscale.com/download> |
| **cloudflared** | Cloudflare Quick Tunnel fallback | `winget install Cloudflare.cloudflared` / `brew install cloudflared` / `pacman -S cloudflared` |
| **ZeroTier** | Private overlay network as a backup provider | System-level: <https://www.zerotier.com/download/> |
| **ngrok** | Public HTTPS via `*.ngrok-free.app` (free tier needs an authtoken) | `winget install ngrok.ngrok` / `brew install ngrok/ngrok/ngrok` / `snap install ngrok` |
| **bore** *(v4.47.0)* | Zero-account TCP relay through `bore.pub` (or self-hosted) | `cargo install bore-cli` or a GitHub release binary from <https://github.com/ekzhang/bore/releases> |
| **BrowserAct** | Stealth browser automation CLI (Arena `skills/browseract/`) | `uv tool install browser-act-cli --python 3.12` |
| **Camoufox** | Anti-fingerprinting Firefox for BrowserAct | Auto-installed with `browser-act-cli` |
| **ydotool / xdotool** | Linux desktop input automation | `pacman -S ydotool` or `apt install xdotool` |
| **Tesseract** | OCR for desktop/screenshot flows | `pacman -S tesseract` / `brew install tesseract` |
The installers detect what is already present, offer to install the rest, and
report status via `/v1/capabilities`. Uninstalling any component never breaks
the Bridge β every optional feature degrades gracefully.
---
## Security model
Skainet Bridge can take powerful actions on the host, so the security model
is intentionally explicit. The v4.40.0 β v4.46.0 sweep closed **31 findings** and
locked in a continuous-security pipeline (see [`SECURITY.md`](SECURITY.md) for
the full threat model, environment-variable reference, and audit history).
**Authentication.**
- Every non-local client authenticates with the bearer credential from
`token.txt`. Comparison is constant-time (`hmac.compare_digest`) and
rate-limited (10 failed attempts / 60 s / IP β HTTP 429 with `Retry-After`).
- Multi-agent bearer tokens (`agent-<id>-<hex>`) let sub-agents run with
narrower scope than the master token.
- `?token=` query-string auth still works for legacy WebSocket clients but is
deprecated β every response served through it now carries a
`Warning: 299 - "?token= query auth is deprecated..."` header.
**Transport.**
- TLS is verified strictly by default (v4.41.0). System trust store, hostname
checked. `ARENA_INSECURE_TLS=1` opts out with a one-time stderr warning.
- **Optional certificate pinning** (v4.45.0): set
`ARENA_BRIDGE_PIN_SHA256=<sha256-hex>` to tighten the trust anchor from
"any of the OS's ~150 CAs" to "this specific bridge cert (or its public
key)". Both cert-hash and SPKI-hash checked on every handshake; a pin
mismatch tears the connection down **before** the bearer token is sent.
**Filesystem access.**
- Every `/v1/fs/*` verb (view / edit / create / upload / **download**) routes
through the same sandbox validator. Sensitive files are blocked by both
basename (`token.txt`, `.env`, `id_rsa`, `.git-credentials`, `.pypirc`,
`.npmrc`, `.bash_history`, shell history in general) and directory prefix
(`.ssh/`, `.aws/`, `.gnupg/`, `.docker/`, `.kube/`, `.config/gh/`, browser
profiles). Sensitivity check runs **before** the existence check so a 403
vs 404 side channel can't leak file-presence.
- Archive extraction (release download, skill install, APK inspect) goes
through `arena/files/safe_extract.py` which rejects path-traversal, symlink
members, and zip-bomb ratios in a pre-scan pass β **no bytes are written
before validation completes**.
**Data at rest.**
- `token.txt` is `chmod 0o600`.
- `~/.arena/last_urls.json` (persistent fallback URL cache) is HMAC-signed
keyed on the bearer token so cache-poisoning attacks can't redirect the
client to an attacker's URL. Also `chmod 0o600`; parent `~/.arena/` is
`chmod 0o700`.
- `audit.jsonl` + `requests.jsonl` are `chmod 0o600` (v4.44.0), rotated files
get re-chmod on rename.
**Logs.**
- Both audit and request logs run every string value through
`arena/observability/redact.py::redact_string`, which scrubs Bearer tokens,
AWS AKIA keys, GitHub `ghp_`, OpenAI `sk-`, Slack `xox[baprs]-`, Google
`AIza`, JWTs, DB URIs with inline credentials, and PEM `PRIVATE KEY`
blocks. Matches become `<redacted:kind>` so operators still see what
class of secret leaked without the secret itself.
- Peer-IP logging is dial-able: `ARENA_LOG_PEER=full` (default), `mask`
(SHA-256 hash with per-install salt, unlinkable across installs), or
`off` (field omitted entirely).
**Common attack classes explicitly closed.**
- SSRF β guard on browser fetch, skill install, auto-update; opt-in strict
mode for webhooks (`ARENA_WEBHOOK_STRICT=1`).
- Zip-slip / zip-bomb β `safe_extract_zip()` 2-pass validation.
- XXE / billion-laughs β DOCTYPE / ENTITY prefix gate in mobile UI dump.
- TOCTOU tempfile races β `NamedTemporaryFile` / `mkdtemp` with 0o700.
- Nan-injection β `safe_float()` rejects NaN / Β±Inf, clamps to a range.
- Symlink escape via `~/malicious-link` β `resolve()`-based path validation.
**Continuous protection.**
- Every push, every PR, and a daily cron trigger a CI security scan
(`bandit` + `semgrep` across 9 rule packs + `pip-audit`). Any HIGH/MEDIUM
bandit finding, any semgrep ERROR/WARNING, or any CVE in a runtime dep
blocks the merge. Run the same three gates locally with
`make security-scan`.
> Found a security issue? See [`SECURITY.md`](SECURITY.md) for the private
> disclosure workflow. **Never paste credentials into an untrusted chat, log,
> or public issue.**
---
## API overview
Core:
| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/health` | Unauthenticated health check |
| `GET` | `/v1/version` | Version and platform info |
| `GET` | `/v1/info` | Bridge runtime info |
| `GET` | `/v1/status` | Bridge status |
| `GET` | `/v1/capabilities` | Machine-readable capability map (agents rely on this) |
Runtime tools:
| Method | Path | Purpose |
| --- | --- | --- |
| `POST` | `/v1/exec` | Guarded shell execution |
| `GET/POST` | `/v1/tasks` | Background task queue |
| `GET/POST/DELETE` | `/v1/memory` | Memory facts |
| `GET` | `/v1/recall` | Fuzzy memory recall |
| `GET` | `/v1/browser/read` | Fetch and extract web page text |
| `GET` | `/v1/desktop/screenshot` | Desktop screenshot where supported |
| `GET` | `/v1/skills` | List discovered skill packages |
Extension bridge:
| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/v1/extension/policies` | Extension policy metadata |
| `POST` | `/v1/extension/preview` | Dry-run extension tool calls |
| `POST` | `/v1/extension/execute` | Execute approved extension tool calls |
Remote access / tunnels:
| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/v1/tunnels/status` | All providers + suggested active endpoint |
| `GET` | `/v1/tunnels/active` | Just the currently reachable endpoint |
| `POST` | `/v1/tunnels/start` | Start providers in priority order (stop on first healthy) |
| `POST` | `/v1/tunnels/stop` | Stop tunnels the Bridge started (ZeroTier untouched) |
| `GET/POST` | `/v1/tailscale/funnel/{action}` | Tailscale Funnel primitives |
| `GET/POST` | `/v1/cloudflared/tunnel/{action}` | Cloudflare Quick Tunnel primitives |
| `GET/POST` | `/v1/ngrok/tunnel/{action}` | ngrok tunnel primitives (fourth transport) |
| `GET/POST` | `/v1/bore/tunnel/{action}` | bore relay primitives (fifth transport, v4.47.0) |
| `GET` | `/v1/zerotier/status` | Full ZeroTier snapshot (backend, networks, hints) |
| `GET/POST` | `/v1/zerotier/network/{action}` | Join / leave / status networks |
The full surface is modular; see the dashboard, route tests, and [`docs/`](docs/).
---
## Development
```bash
git clone https://github.com/IvanSkainet/arena-agent.git arena-bridge
cd arena-bridge
python -m pip install -e ".[full,dev]"
pytest
```
Targeted checks for extension work:
```bash
pytest -q tests/test_chat_extension_assets.py tests/test_chat_extension_adapter_flow.py tests/test_chat_extension_sidepanel_flow.py tests/test_extension_bridge.py tests/test_project_modularity.py
for f in background content parser adapters insert_strategies insert_history adapter_sites popup settings sidepanel; do
node --check "chat_extension/$f.js"
done
```
Targeted checks for remote-access / provider work:
```bash
pytest -q tests/test_tunnels.py tests/test_zerotier.py tests/test_cloudflared.py \
tests/test_browseract.py tests/test_superpowers_layout.py
```
Before pushing, run the same security gates CI runs:
```bash
make install-security-tools # one-time: bandit + semgrep + pip-audit
make security-scan # 0 HIGH+MEDIUM bandit, 0 semgrep findings, 0 CVEs
```
If it passes locally it passes in CI β the Makefile and the CI workflow both
call the same `scripts/security_gate.py`.
Contributor notes: [CONTRIBUTING.md](CONTRIBUTING.md) Β· Release checklist: [RELEASE.md](RELEASE.md) Β· Security posture: [SECURITY.md](SECURITY.md).
---
## Documentation map
| Document | What's inside |
| --- | --- |
| [SECURITY.md](SECURITY.md) | **Threat model, environment-variable reference (14 knobs), recommended production preset, CI security-scan pipeline, audit history v4.40.0 β v4.46.0. Read this before exposing the bridge to any network.** |
| [CHANGELOG.md](CHANGELOG.md) Β· [ru](CHANGELOG.ru.md) | Release history |
| [RELEASE.md](RELEASE.md) | Release packaging and publishing checklist |
| [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup, tests, workflow, `make security-scan` gate |
| [AGENTS.md](AGENTS.md) | Hard rules for AI maintainers β where things live, what not to add, security-annotation rules |
| [chat_extension/README.md](chat_extension/README.md) | Browser extension details |
| [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md) | Integration notes β Tailscale / cloudflared / ZeroTier / MCP + cert pinning |
| [docs/RELAY.md](docs/RELAY.md) | Generic operator mailbox and persistent CLI/ConPTY terminal ingress |
| [docs/SUPERPOWERS.md](docs/SUPERPOWERS.md) | Superpowers vendored copy: layout + update flow |
| [docs/MODULE_MAP.md](docs/MODULE_MAP.md) | Codebase / module map |
| [docs/V3_MODULAR_ARCHITECTURE.md](docs/V3_MODULAR_ARCHITECTURE.md) | Modular architecture notes |
| [docs/AI_CODEBASE_NAVIGATION.md](docs/AI_CODEBASE_NAVIGATION.md) | Navigation tips for AI maintainers |
Some files in `docs/` are design notes or historical audits. The README and
CHANGELOG are the public entry points.
---
## Tool reference (per-namespace examples)
Each tool is registered in the `MCP_TOOLS` catalogue with a stable `namespace.action`
name and a JSON-Schema `inputSchema`. Below is one canonical call per namespace,
to make the bridge surface discoverable from the README without reading the
source. The full catalogue is in `arena/mcp/tool_registry.py`.
| Namespace | Example call |
| --- | --- |
| `admin` | `admin.run` β Cross-platform admin escalation. Linux/macOS proxies to sudo |
| `asr` | `asr.transcribe` β Transcribe an audio file locally with whisper.cpp. Auto-conv |
| `browser` | `browser.search` β DuckDuckGo search via pure-Python (no chromium) |
| `desktop` | `desktop.ocr` β Run OCR on a fresh desktop screenshot and return recognized |
| `desktop_app` | `desktop_app.click_window_relative` / `desktop_app.screenshot_window` β Resolve a real window, then click or screenshot relative to that window for less brittle GUI scenarios |
| `document` | `document.structure` β Structure OCR/ASR/text into tasks or physics homework JSON |
| `exec` | `exec.exec` β Namespaced alias for ``exec``. Run shell command outside bri |
| `code` | `code.run` β Execute agent-authored code under the operator's execution posture (composable fence); fail-closed, agent can't set the posture |
| `code_project` | `code_project.run` / `code_project.lock_verify` / `code_project.promote_tool` β Run persistent projects, enforce dependency locks, or promote recipes/tests into tools |
| `code_run` | `code_run.info` / `code_run.promote_tool` β Inspect persisted runs or use a run as provenance for a promoted tool |
| `code_matrix` | `code_matrix.run` β Run up to 8 Code Workbench jobs sequentially under the current operator posture |
| `code_session` | `code_session.exec` / `code_session.artifacts` β Execute stateful Python sessions and persist session files/artifacts |
| `code_artifact` | `code_artifact.read` β Read a persisted Code Workbench artifact by run_id and path |
| `fs` | `fs.read` β Read file contents (utf-8) |
| `git` | `git.status` β Show git status for a repository. |
| `hooks` | `hooks.list` β List configured hooks per event |
| `image` | `image.preprocess_for_ocr` β Preprocess an image file for OCR |
| `mcp` | `mcp.ext_call` β Call a tool on a registered external MCP server (Desktop-Commander, ScreenPilot, ...). Discover servers with `mcp.ext_servers` and their tools with `mcp.ext_tools` |
| `mcp_server` | `mcp_server.create` / `mcp_server.test` / `mcp_server.install` β Author, verify, and install an external MCP stdio server |
| `memory` | `memory.recall` β Find relevant facts/snapshots/sessions by query (TF score), |
| `mission` | `mission.autopilot_start` / `mission.autopilot_report` / `mission.run` β Execute bounded mission tool chains with persisted progress and flight records, or run persisted missions |
| `mobile` | `mobile.preflight` / `mobile.devices` β Preflight Android/ADB readiness and list connected devices |
| `emulator` | `emulator.providers` / `emulator.list` / `emulator.start` / `emulator.stop` / `emulator.attach` β Boot and stop Android emulators through whichever manager the host has (AVD, Genymotion, MuMu, Waydroid, or a host-declared one); post-boot control is plain ADB via `mobile.*`. See [docs/emulators.md](docs/emulators.md) |
| `input_helper` | `input_helper.click` / `input_helper.key` / `input_helper.launch` / `input_helper.send_chat_command` β Route real hardware input through the Interactive Input Helper running in the user's desktop session (solves Session 0 limitation for Java Swing, LWJGL, and all GUI automation) |
| `capability_gap` | `capability_gap.record` / `capability_gap.list` / `capability_gap.resolve` β Track missing bridge capabilities found during real scenarios |
| `net` | `net.http` β Typed HTTP client. Only http/https to public hostnames (inhe |
| `ocr` | `ocr.extract` β Run OCR on any image file and return text + word boxes |
| `plan` | `plan.create` β Create a structured execution plan for a goal, with suggeste |
| `react` | `react.run` β Run a bounded reason-act-observe loop using safe observation |
| `reflect` | `reflect.run` β Reflect on a prior react/planning run and produce concerns, |
| `runtime` | `runtime.probe` / `runtime.compat` β Probe runtimes and show runtime Γ sandbox compatibility with known blockers and next actions |
| `scenario` | `scenario.run` / `scenario.promote_from_history` β Execute scenarios or promote successful runs/history into reusable scenarios |
| `secrets` | `secrets.list` β List available secret keys (values never returned). |
| `service` | `service.autostart_status` / `service.autostart_repair` β Diagnose or repair bridge autostart setup |
| `skill` | `skill.list` β List available agent skills |
| `subagent` | `subagent.spawn` β Spawn isolated subagent for delegated work; returns summary |
| `sudo` | `sudo.run` β Run a command through 'sudo -n <cmd>' (non-interactive). Req |
| `sys` | `sys.status` β Bridge/services/funnel status |
| `relay` | `relay.check` / `relay.reply` / `relay.send` β Operator mailbox: read instructions left from the Dashboard or a terminal, answer them, or ask a question mid-task. Queued, not pushed β the agent has to look. |
| `watch` | `watch.files` β List, add, or remove file watchers that emit realtime file c |
| `workbench` | `workbench.status` β Show posture, runtimes, projects, sessions, recent artifacts, known limits, and next actions |
| `ship` | `ship.status` / `ship.preflight` / `ship.smoke` β Whole-ship maps, readiness checks, Linux flight check, and real-machine smoke proof |
| `tool_foundry` | `tool_foundry.validate` / `tool_foundry.publish` β Validate a Workbench project manifest/tests and publish it as a callable custom tool |
All calls go through `POST /v1/mcp/call` with JSON body `{"name": "<tool>", "arguments": {...}}`.
---
## License
MIT β see [LICENSE](LICENSE).
## Naming and affiliation
Skainet Bridge is an independent, unaffiliated open-source project. It is not
produced, endorsed, sponsored by, or connected with Arena Intelligence, Inc.
(arena.ai / LMArena), Anthropic, OpenAI, Google, or any other company whose
product it can talk to.
The browser extension lists several chat sites β including arena.ai, ChatGPT,
Claude, Gemini and others β purely as pages it can attach to. Those names are
the trademarks of their respective owners and appear here only to describe
compatibility, which is nominative use, not a claim of any relationship.
The project was previously named "Arena Unified Bridge". It was renamed to
avoid any implication of affiliation with Arena Intelligence, Inc. Internal
identifiers (the `arena` Python package, `ARENA_*` environment variables, the
`ArenaUnifiedBridge` Windows service name and the `arena-unified-bridge`
service string in API responses) are deliberately left unchanged for now, so
existing installations keep working; they are implementation details, not
branding.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessResponsive