@@ -159,6 +159,20 @@ These events are broadcast to all subscribed tabs (no `swarmId` filter).
---
## Examples server (host control)
Toggled from extension Settings / Dashboard (`examplesServerEnabled` in `bridgeSwarmSettings`). The background service worker sends these commands; pages do not call them directly.
Serves the bundled `examples/` tree over HTTP so demos share one origin (required for extension injection). See [examples/README.md](../examples/README.md).
---
## See also
- [DATA-API.md](DATA-API.md) — Data API details and examples.
- **Background service worker** ([extension/background.js](../extension/background.js)): Maintains the native messaging port to the host (`chrome.runtime.connectNative('com.bridgeswarm')`). Sends requests to the host and tracks pending requests by id; forwards events to subscribed tabs. Reconnects with backoff on disconnect. Reads extension settings from **local storage** (key `bridgeSwarmSettings`); when “Show notification when host disconnects” is enabled, shows a browser notification on `port.onDisconnect`.
- **Content script** ([extension/content.js](../extension/content.js)): Reads settings from **local storage** (`bridgeSwarmSettings`). If “Do not inject on file:// URLs” is enabled and the page URL is `file://`, skips injection. Otherwise injects a small defaults script (setting `window.__BRIDGESWARM_DEFAULTS__` from settings) then [api.js](../extension/api.js), [framed-stream.js](../extension/framed-stream.js), and [protomux-bundle.js](../extension/protomux-bundle.js) into the page. Bridges the page and the background: the page cannot use `chrome.runtime` directly (it runs in the page context), so the page posts messages to the content script via `window.postMessage`, and the content script uses `chrome.runtime.sendMessage` to talk to the background. Events from the host are received by the background and sent to tabs; the content script dispatches them as `bridge-swarm-event` custom events on the window.
- **Options page** ([extension/options.html](../extension/options.html), [extension/options.js](../extension/options.js)): Settings UI (CSP-compliant: script in external `options.js`). Saves to **local storage** under `bridgeSwarmSettings` (default app name, default max peers, default request timeout, default ready timeout, disable on file://, notify on disconnect, debug).
- **Injected scripts** (page context): [api.js](../extension/api.js) exposes `window.BridgeSwarm` and`BridgeSwarm.request(type, payload)`. It merges constructor options with `__BRIDGESWARM_DEFAULTS__` (appName, maxPeers, requestTimeoutMs, readyTimeoutMs), implements `BridgeSwarm.ready()` with optional timeout from defaults, and uses the default request timeout when the page doesn’t pass `timeoutMs`. It listens for `bridge-swarm-bridge` messages, forwards them to the content script, and listens for `bridge-swarm-bridge-response` and `bridge-swarm-event`.
- **Background service worker** ([extension/background.js](../extension/background.js)): Maintains the native messaging port (`chrome.runtime.connectNative('com.bridgeswarm')`). Tracks pending requests by id; routes swarm events to the tabs that own each `swarmId`; broadcasts capability events (`cap-chunk` / `cap-end` / `cap-error`) to subscribed tabs. Reconnects with backoff on disconnect. Reads **`bridgeSwarmSettings`** from local storage (autosaved from Options/Dashboard). When “Show notification when host disconnects” is enabled, shows a browser notification on `port.onDisconnect`. When **Examples server** is enabled, sends `examplesServer.start` / `stop` to the host and exposes status to the UI.
- **Content script** ([extension/content.js](../extension/content.js)): Reads settings; if “Do not inject on file:// URLs” is on and the page is `file://`, skips injection. Otherwise injects defaults then [api.js](../extension/api.js), [framed-stream.js](../extension/framed-stream.js), and [protomux-bundle.js](../extension/protomux-bundle.js). Bridges page ↔ background via `window.postMessage` / `chrome.runtime.sendMessage` (the page cannot use `chrome.runtime` directly).
- **Options & Dashboard** ([extension/options.html](../extension/options.html), [extension/dashboard.html](../extension/dashboard.html)): Settings UI. Changes **save on change** (no Save button). Includes swarm defaults, timeouts, injection/notification/debug flags, and the examples-server toggle with URL `http://127.0.0.1:4173/`.
- **Entry** ([native-host/index.mjs](../native-host/index.mjs)): Loads bare-process, [messenger.js](../native-host/messenger.js), and [host.js](../native-host/host.js). Wires stdin/stdout to the messenger and passes `handleMessage` and the send callback so the host can reply and emit events.
- **Messenger** ([native-host/messenger.js](../native-host/messenger.js)): Implements the Chrome/Firefox native messaging protocol: 4-byte little-endian length prefix followed by UTF-8 JSON. Max message size from host to browser is 1 MB. Parses incoming messages and calls `onMessage`; provides `send(msg)` to write length-prefixed JSON to stdout.
- **Host** ([native-host/host.js](../native-host/host.js)): Handles all command types (swarm lifecycle, connection, attachment, data API). Runs Hyperswarm, Corestore, Hypercore, Hyperbee, Hyperdrive, Autobase, Hyperdb; can attach Protomux and HRPC to a connection. Calls `reply(result)` to respond and `emit(event, payload)` to push events to the browser.
- **Messenger** ([native-host/messenger.js](../native-host/messenger.js)): Chrome/Firefox native messaging: 4-byte little-endian length + UTF-8 JSON. Max message size from host → browser is **1 MB**.
- **Host** ([native-host/host.js](../native-host/host.js)): Swarm lifecycle, peer firewall/ban, auto-replicate, connection attachment (Protomux/HRPC), Hyper* data API, capability dispatch, and examples-server control (`examplesServer.start` / `stop` / `status`).
- **Examples server** ([native-host/examples-server.js](../native-host/examples-server.js)): Optional `bare-http1` static server on `127.0.0.1:4173` serving synced `examples/` (or `BRIDGESWARM_EXAMPLES_DIR`). Enabled from extension settings.
## Protocol
- **Wire format** (host ↔ browser): Each message is a 4-byte LE unsigned integer (length in bytes) followed by that many bytes of UTF-8 JSON. No CRLF translation (binary I/O).
- **Message kinds**: Requests from browser:`{ id?, type, payload }`. Responses from host:`{ id, type: 'response', payload }`. Events from host:`{ type: 'event', event, payload }`.
- **Wire format** (host ↔ browser): 4-byte LE unsigned length + UTF-8 JSON. Binary I/O (no CRLF translation).
The page’s `BridgeSwarm.request(type, payload)` builds a payload that the content script forwards; the background adds an `id` and sends it to the host. The host’s `reply(result)` is sent back with the same `id` so the background can resolve the matching pending promise and the page receives the response.
`BridgeSwarm.request(type, payload)` builds the payload; the background assigns `id` and resolves the matching pending promise when the host replies.
## Event flow
@@ -52,24 +87,70 @@ sequenceDiagram
participant Page
NativeHost->>NativeHost: emit(event, payload)
NativeHost->>Background: messenger.send type event, event, payload
Page->>Page: api.js _onEvent connection data end error cap-*
```
The host emits events (e.g. `connection`, `data`, `end`, `error`) when swarms or connections change. The background broadcasts them to all subscribed tabs; the content script dispatches a custom event; api.js’s BridgeSwarm instance listens and updates connection state and emits on the swarm/connection objects.
## Connection lifecycle
1.**Init swarm**: Page creates a BridgeSwarm and calls `join(topic)`. That triggers `init` (if not already inited) then `join`. The host creates a Hyperswarm and joins the topic.
2.**Connection**: When a peer connects, the host creates a `connId`, stores the socket, and emits a `connection` event with `connId`, `swarmId`, and `peerInfo`. The page receives it and gets a BridgeSwarmConnection with that `connId`.
3.**Data**: The page can `conn.write(data)` (sent as `write` with base64 data) and receives `data` events (host forwards socket data as base64 in `data` events). Or the page can **attach** the connection:
4.**Attachment**: The page sends `attachReplication({ connId, coreKeyHex? })` or `attachHrpc({ connId })`. The host stops forwarding that connection’s data to the browser, creates a Protomux on the socket, and either runs Hypercore replication or opens the HRPC channel. That connection is then used only by the host for that protocol. The host keeps at most one connection per peer per swarm (duplicate connections from the same peer are dropped) so both sides use the same connection pair for HRPC.
```mermaid
stateDiagram-v2
[*] --> Init: new BridgeSwarm + join(topic)
Init --> Discovering: host Hyperswarm.join
Discovering --> Connected: peer Noise handshake
Connected --> DataForward: page conn.write / data events
Connected --> Attached: attachReplication or attachHrpc
DataForward --> Closed: end / error / destroy
Attached --> Closed: end / destroy
Closed --> [*]
```
1.**Init swarm**: `join(topic)` triggers `init` (once) then `join`. Host creates Hyperswarm and joins the topic.
2.**Connection**: Host allocates `connId`, stores the socket, emits `connection` with `connId`, `swarmId`, `peerInfo`.
3.**Data**: `conn.write` → `write` (base64); host forwards socket bytes as `data` events — **or** the page attaches the connection:
4.**Attachment**: `attachReplication` / `attachHrpc` stops browser data forwarding; host owns Protomux on that socket (Hypercore replication or HRPC). At most one connection per peer per swarm is kept.
@@ -10,9 +10,10 @@ Curated modules shipped in the **default** BridgeSwarm native host. Not every `b
| `bare` | Bare runtime |
| `bare-process` | `process` global for packed host |
| `bare-module` | Addon / module loading |
| `bare-fs` | Storage, cap-jobs, Hyper* on disk |
| `bare-fs` | Storage, cap-jobs, Hyper* on disk, serve example files |
| `bare-path` | Path join/resolve under allowlists |
| `bare-stream` | Duplex bridges for NMH / HRPC |
| `bare-http1` | Local examples server (`127.0.0.1:4173`) when enabled in Settings |
### Holepunch data + swarm
| Module | Why |
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.