Bridge Protocol
The bridge is a lightweight HTTP server embedded in the Tauri app during development. It enables tauri-agent-tools to evaluate JavaScript in the webview.
Endpoints
The bridge exposes eight HTTP endpoints:
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/eval |
POST | token | Evaluate JS in a webview (supports window param for multi-window) |
/logs |
POST | token | Rust tracing logs and sidecar output — bare {token} drains; {cursor, waitMs, limit} (v0.8+) reads without draining |
/describe |
POST | token | Report PID, window labels, and capabilities |
/version |
GET | none | Bridge version and available endpoints |
/process |
POST | token | Tauri PID, exe, args, uptime, and sidecar registry snapshot (v0.7+) |
/capabilities |
POST | token | Declared Tauri capability set per window (v0.7+) |
/devtools |
POST | token | Webview inspector URL or platform hint (v0.7+) |
/health |
POST | token | Uptime, webview readiness, and sidecar liveness (v0.7+) |
Eval Endpoint
Request
| Field | Type | Description |
|---|---|---|
js |
string | JavaScript expression to evaluate in the webview |
token |
string | 32-character authentication token |
window |
string? | Target webview window label (default: "main") |
Response
Success (200):
Authentication error (401/403):
Bad request (400):
Communication Flow
sequenceDiagram
participant CLI as tauri-agent-tools
participant FS as /tmp/
participant Bridge as Rust Bridge<br/>(tiny_http)
participant WV as Tauri Webview
Note over Bridge: App startup
Bridge->>Bridge: Generate random token
Bridge->>Bridge: Bind to 127.0.0.1:0 (random port)
Bridge->>FS: Write tauri-dev-bridge-{pid}.token<br/>{port, token, pid}
Note over CLI: Command execution
CLI->>FS: Scan tauri-dev-bridge-*.token
CLI->>CLI: Parse JSON, check PID liveness
CLI->>CLI: Clean stale files from dead processes
CLI->>Bridge: POST /eval {js, token}
Bridge->>Bridge: Validate token
Bridge->>Bridge: Generate UUID request ID
Bridge->>WV: eval(wrapped JS with callback)
WV->>WV: Evaluate expression
WV->>Bridge: __TAURI_INTERNALS__.invoke("__dev_bridge_result", {id, value})
Bridge->>Bridge: Match result by ID, unblock HTTP thread
Bridge-->>CLI: {result: ...}
Note over Bridge: App shutdown
Bridge->>FS: Delete token file (scopeguard)
Token File Format
Token files are written to the system temp directory (/tmp/ on Linux/macOS):
Filename: tauri-dev-bridge-{pid}.token
Contents:
| Field | Type | Description |
|---|---|---|
port |
number | HTTP server port |
token |
string | 32-character random authentication token |
pid |
number | Process ID of the Tauri app |
Authentication
- Every request must include the
tokenfield matching the generated token - Token is a random 32-character alphanumeric string
- Mismatched tokens return HTTP 401/403
Security Properties
- Localhost only — bridge binds to
127.0.0.1, not0.0.0.0 - Random port — no fixed port to target
- Token auth — prevents unauthorized access from other local processes
- Debug only — wrapped in
cfg!(debug_assertions), compiled out of release builds - Cleanup — token file deleted on exit via
scopeguard - Inspection is read-only — inspection commands only evaluate JS, never inject input events
- Interaction is debug-only — interaction commands use eval-based DOM dispatch, sandboxed to the webview
Log Capture Endpoint
Endpoint
Request
| Field | Type | Description |
|---|---|---|
token |
string | 32-character authentication token |
cursor |
number (optional, v0.8+) | Read entries with id > cursor without draining; 0 replays the full buffer |
waitMs |
number (optional, v0.8+) | Long-poll up to this many ms for new entries (capped at 25000); only with cursor |
limit |
number (optional, v0.8+) | Max entries per response, clamped to 1..1000; only with cursor |
The mode is selected by the presence of cursor: a bare { token } request
keeps the legacy drain semantics byte-for-byte, so pre-v0.8 callers are
unaffected.
Response
Success (200):
{
"entries": [
{
"id": 41,
"timestamp": 1710000000000,
"level": "info",
"target": "myapp::db",
"message": "Connected to database",
"source": "rust"
},
{
"id": 42,
"timestamp": 1710000001000,
"level": "warn",
"target": "stderr",
"message": "deprecated flag used",
"source": "sidecar:ffmpeg"
}
],
"cursor": 42,
"dropped": 0
}
| Field | Type | Description |
|---|---|---|
entries[].id |
number (v0.8+) | Monotonic entry id, assigned at capture |
entries[].timestamp |
number | Milliseconds since UNIX epoch |
entries[].level |
string | trace, debug, info, warn, or error |
entries[].target |
string | Rust module path or stdout/stderr for sidecars |
entries[].message |
string | Log message text |
entries[].source |
string | rust for tracing logs, sidecar:<name> for sidecar output |
cursor |
number (cursor mode only) | Pass back on the next request to resume after the last returned entry |
dropped |
number (cursor mode only) | Entries evicted from the ring buffer past the cursor (lost to overflow) |
cursor and dropped are omitted in drain mode — their presence on the
response is how clients feature-detect cursor support with zero extra
round-trips.
Behavior
- Drain mode (
{ token }only) drains the buffer — each entry is returned only once, and concurrent consumers steal entries from each other - Cursor mode (v0.8+) is non-draining — each consumer tracks its own cursor, so any number of consumers can tail concurrently
- Cursor requests with
waitMs > 0long-poll on a worker thread (the bridge's accept loop is serial, so waiting inline would stall other endpoints) - The buffer holds up to 1000 entries; oldest entries are dropped on overflow (visible as
droppedin cursor mode) - The buffer is populated by a
tracing::Layer(Rust logs) and background reader threads (sidecar stdout/stderr)
Version Endpoint
Request
No authentication required.
Response
{
"version": "0.8.0",
"endpoints": ["/eval", "/logs", "/describe", "/version", "/process", "/capabilities", "/devtools", "/health"]
}
| Field | Type | Description |
|---|---|---|
version |
string | Bridge protocol version |
endpoints |
string[] | Available endpoint paths |
Describe Endpoint
Request
Response
{
"pid": 12345,
"windows": ["main", "overlay", "settings"],
"capabilities": ["eval", "logs", "describe"]
}
| Field | Type | Description |
|---|---|---|
pid |
number? | Process ID of the Tauri app |
windows |
string[]? | Registered webview window labels |
capabilities |
string[] | Available bridge capabilities |
Error Handling
| HTTP Status | Meaning | CLI Behavior |
|---|---|---|
| 200 | Success | Parse and return result |
| 401/403 | Token mismatch | Throw "authentication failed" error |
| 400 | Missing/invalid request | Throw error with response body |
| 5xx | Bridge error | Throw error with status and body |
| Timeout | No response within 5s | AbortSignal.timeout(5000) throws |
| Connection refused | Bridge not running | Throw connection error |