325 lines
24 KiB
HTML
325 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<meta name="theme-color" content="#0c0c10">
|
|
<script>
|
|
(function () {
|
|
try {
|
|
var t = localStorage.getItem('bridgeswarm.site.theme');
|
|
if (t !== 'light' && t !== 'dark') t = 'dark';
|
|
document.documentElement.setAttribute('data-theme', t);
|
|
var meta = document.querySelector('meta[name="theme-color"]');
|
|
if (meta) meta.setAttribute('content', t === 'light' ? '#2dd4bf' : '#0c0c10');
|
|
} catch (e) {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<title>How BridgeSwarm Works — Learn</title>
|
|
<meta name="description" content="A deep, technical walkthrough of BridgeSwarm: native messaging, the Bare host, Hyperswarm topics as rendezvous, Noise encryption, Hyper* storage, Protomux/HRPC, capability packs, the Control Center, and the mental model behind live domains and self-hosted apps.">
|
|
<meta name="author" content="HoneyPeer, LLC">
|
|
<link rel="canonical" href="https://bridgeswarm.dev/learn/">
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=DM+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
|
|
<link rel="stylesheet" href="/src/styles/global.css">
|
|
<link rel="stylesheet" href="/src/styles/theme-light.css">
|
|
<link rel="stylesheet" href="/src/styles/docs.css">
|
|
<link rel="stylesheet" href="/src/styles/learn.css">
|
|
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicons/favicon-16x16.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicons/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="48x48" href="/assets/favicons/favicon-48x48.png">
|
|
<link rel="shortcut icon" href="/assets/favicons/favicon.ico">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicons/apple-touch-icon.png">
|
|
<link rel="manifest" href="/assets/favicons/site.webmanifest">
|
|
<link rel="mask-icon" href="/assets/logo/bridgeswarm-wordmark.svg" color="#2dd4bf">
|
|
<meta name="msapplication-TileImage" content="/assets/favicons/mstile-150x150.png">
|
|
<meta name="msapplication-config" content="/assets/favicons/browserconfig.xml">
|
|
|
|
<meta property="og:type" content="article">
|
|
<meta property="og:site_name" content="BridgeSwarm">
|
|
<meta property="og:title" content="How BridgeSwarm Works — Learn">
|
|
<meta property="og:description" content="Native messaging, the Bare host, Hyperswarm topics as rendezvous, Noise, Hyper* storage, Protomux/HRPC, capabilities, and the Control Center — explained from first principles.">
|
|
<meta property="og:url" content="https://bridgeswarm.dev/learn/">
|
|
<meta property="og:image" content="https://bridgeswarm.dev/assets/brand/og-image.png">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="How BridgeSwarm Works — Learn">
|
|
<meta name="twitter:description" content="Native messaging, the Bare host, Hyperswarm topics as rendezvous, Noise, Hyper* storage, Protomux/HRPC, capabilities, and the Control Center.">
|
|
<meta name="twitter:image" content="https://bridgeswarm.dev/assets/brand/twitter-card.png">
|
|
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@graph": [
|
|
{
|
|
"@type": "Organization",
|
|
"@id": "https://bridgeswarm.dev/#organization",
|
|
"name": "HoneyPeer, LLC",
|
|
"url": "https://bridgeswarm.dev/",
|
|
"logo": "https://bridgeswarm.dev/assets/logo/bridgeswarm-icon-512.png",
|
|
"email": "[email protected]",
|
|
"sameAs": ["https://git.ssh.surf/snxraven/BridgeSwarm"]
|
|
},
|
|
{
|
|
"@type": "SoftwareApplication",
|
|
"@id": "https://bridgeswarm.dev/#software",
|
|
"name": "BridgeSwarm",
|
|
"applicationCategory": "DeveloperApplication",
|
|
"operatingSystem": "macOS, Linux, Windows",
|
|
"url": "https://bridgeswarm.dev/",
|
|
"publisher": { "@id": "https://bridgeswarm.dev/#organization" },
|
|
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
|
"license": "https://www.gnu.org/licenses/agpl-3.0.html",
|
|
"downloadUrl": "https://bridgeswarm.dev/download",
|
|
"softwareVersion": "1.0.0",
|
|
"description": "Brings the Hyperswarm P2P stack into desktop browsers via a Bare native messaging host."
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
</head>
|
|
<body class="page-learn">
|
|
<div class="bg-mesh" aria-hidden="true"></div>
|
|
<div class="bg-grid" aria-hidden="true"></div>
|
|
|
|
<div data-nav data-prefix="../"></div>
|
|
|
|
<main id="main">
|
|
|
|
<header class="page-hero">
|
|
<div class="container">
|
|
<p class="section-label" style="justify-content:center;">Learn</p>
|
|
<h1>How BridgeSwarm works</h1>
|
|
<p class="lead">Everything between <code>window.BridgeSwarm</code> in your page and a live Noise-encrypted socket to a peer's machine — the long version.</p>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<article class="docs-main" style="max-width: var(--docs-max, 58rem); margin-inline: auto; padding-top: 0;">
|
|
|
|
<nav class="toc" aria-label="On this page">
|
|
<strong>On this page</strong>
|
|
<ol>
|
|
<li><a href="#native-messaging">Native messaging</a></li>
|
|
<li><a href="#bare-host">The Bare host</a></li>
|
|
<li><a href="#topics">Hyperswarm topics as rendezvous</a></li>
|
|
<li><a href="#noise">Noise encryption</a></li>
|
|
<li><a href="#hyperstack">Hyper* storage</a></li>
|
|
<li><a href="#protomux-hrpc">Protomux & HRPC</a></li>
|
|
<li><a href="#capabilities">Capability packs</a></li>
|
|
<li><a href="#control-center">Control Center</a></li>
|
|
<li><a href="#mental-model">Live domains & self-hosted apps</a></li>
|
|
<li><a href="#flows">Message flows</a></li>
|
|
<li><a href="#glossary">Glossary</a></li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<section id="native-messaging" aria-labelledby="native-messaging-title">
|
|
<h2 id="native-messaging-title">Native messaging</h2>
|
|
<p>Browser extensions cannot open sockets, spawn processes, or touch the filesystem directly — that sandbox is exactly what keeps a random web page from doing the same. <strong>Native messaging</strong> is the narrow, browser-mediated exception: an extension's background script can launch one specific, pre-registered executable and exchange length-prefixed JSON with it over stdin/stdout.</p>
|
|
<p>BridgeSwarm registers a host named <code>com.bridgeswarm</code>. When you install, the installer writes a manifest — <code>com.bridgeswarm.json</code> — into your browser's native-messaging-hosts directory, pointing at the launcher script for the host binary and listing the extension IDs allowed to connect to it (<code>allowed_origins</code> for Chrome, <code>allowed_extensions</code> for Firefox). The browser enforces that allowlist; nothing else can attach to your BridgeSwarm host.</p>
|
|
<p>The wire format is deliberately boring: a <strong>4-byte little-endian length prefix</strong> followed by UTF-8 JSON, in both directions. Binary payloads (media chunks, Hyperdrive file contents) are base64-encoded inside that JSON, which caps a single host→browser message at roughly <strong>1 MB</strong> — one reason live media encode streams segments rather than raw frames.</p>
|
|
<pre><code>// Message shapes on the wire
|
|
{ id, type, payload } // request (page -> host)
|
|
{ id, type: 'response', payload } // response (host -> page)
|
|
{ type: 'event', event, payload } // event (host -> page, unsolicited)</code></pre>
|
|
</section>
|
|
|
|
<section id="bare-host" aria-labelledby="bare-host-title">
|
|
<h2 id="bare-host-title">The Bare host</h2>
|
|
<p><a href="https://github.com/holepunchto/bare">Bare</a> is a small JavaScript runtime built by Holepunch specifically to run their P2P stack outside the browser — the same runtime underneath Pear and Keet. BridgeSwarm's native host is a Bare process, not a Node process pretending to be one: a release build ships a self-contained binary, while a from-source build runs Bare via a local <code>bare</code> npm dependency.</p>
|
|
<p>On startup the host wires up, in order:</p>
|
|
<ul>
|
|
<li><strong>bare-process</strong> and default capability packs (media / <code>bare-ffmpeg</code>).</li>
|
|
<li><strong>messenger.js</strong> — the native messaging framing described above.</li>
|
|
<li><strong>host.js</strong> — swarm lifecycle, peer firewall/ban, Hyper* data API, Protomux/HRPC attachment, and the optional examples server.</li>
|
|
</ul>
|
|
<p>Because it's a real runtime with real native module support, the host can load <code>bare-ffmpeg</code>, <code>bare-fs</code>, and similar packages that would be meaningless inside a browser sandbox — while the page still only ever sees the plain JS object <code>window.BridgeSwarm</code>.</p>
|
|
<div class="callout warn">
|
|
<div class="callout-icon">!</div>
|
|
<div class="callout-body"><strong>The native host runs with your full user privileges</strong> — it can open sockets and read/write files. Only install BridgeSwarm builds you trust, the same way you'd treat any other native application.</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="topics" aria-labelledby="topics-title">
|
|
<h2 id="topics-title">Hyperswarm topics as rendezvous</h2>
|
|
<p>There is no BridgeSwarm server that brokers connections. Instead, <strong>Hyperswarm</strong> uses a public, distributed hash table (DHT) purely as a rendezvous mechanism: peers <code>announce</code> and <code>lookup</code> a 32-byte <strong>topic</strong> — usually the hash of a string like <code>"my-app-room-42"</code> — and the DHT tells them about each other's network addresses. The actual connection, once addresses are known, is established directly between peers with UDP hole-punching for NAT traversal.</p>
|
|
<p>From the page, this collapses into one call:</p>
|
|
<pre><code>const swarm = new BridgeSwarm({ appName: 'my-app' });
|
|
await swarm.join('my-topic');
|
|
swarm.on('connection', (conn, peerInfo) => {
|
|
console.log('peer:', peerInfo.publicKey);
|
|
});</code></pre>
|
|
<p>Because the topic is just a shared secret string (or its hash), you can think of it as an address peers "dial" — see <a href="#mental-model">Live domains</a> below for where that idea leads. Each browser tab gets its own cryptographic identity, so <code>peerInfo.publicKey</code> is a stable way to recognize a specific peer across reconnects within a session.</p>
|
|
</section>
|
|
|
|
<section id="noise" aria-labelledby="noise-title">
|
|
<h2 id="noise-title">Noise encryption</h2>
|
|
<p>Once two peers find each other, the connection they get is already a <strong>Noise protocol</strong> secret stream — encrypted and authenticated before a single application byte crosses the wire. You do not configure TLS certificates, manage a CA, or think about key exchange; it's the default and only mode of a Hyperswarm connection.</p>
|
|
<p>Each connection the page receives via <code>conn.write(data)</code> / <code>conn.on('data', ...)</code> is one decrypted frame per host-side chunk. If you attach Protomux (see below), <code>wrapRawFrames(conn)</code> preserves that one-chunk-per-frame boundary so message-oriented protocols built on top don't need their own length-prefixing.</p>
|
|
</section>
|
|
|
|
<section id="hyperstack" aria-labelledby="hyperstack-title">
|
|
<h2 id="hyperstack-title">Hyper* storage</h2>
|
|
<p>The host also runs Holepunch's storage stack on a single <strong>Corestore</strong>, reachable from the page through <code>BridgeSwarm.request(type, payload)</code>. You can use the default instance of each, or open named/keyed resources and pass a <code>resourceId</code> on subsequent calls.</p>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead><tr><th>Module</th><th>Shape</th><th>Typical use</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><strong>Hypercore</strong></td><td>Append-only log</td><td>Event logs, audit trails, replicated feeds</td></tr>
|
|
<tr><td><strong>Hyperbee</strong></td><td>Key/value B-tree over a Hypercore</td><td>Structured state, lookups (<code>beeGet</code>/<code>beePut</code>)</td></tr>
|
|
<tr><td><strong>Hyperdrive</strong></td><td>P2P filesystem</td><td>Serving files, drag-and-drop sync (<code>driveGet</code>/<code>drivePut</code>/<code>driveList</code>)</td></tr>
|
|
<tr><td><strong>Autobase</strong></td><td>Multi-writer linearized log</td><td>Multiple peers appending to one causal history</td></tr>
|
|
<tr><td><strong>Hyperdb</strong></td><td>Schema-based P2P database</td><td>Typed collections with query/find (<code>hyperdbInsert</code>/<code>hyperdbFindToArray</code>)</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p>Two connection-attachment commands turn a live swarm connection into a replication channel instead of a raw data pipe: <code>attachReplication</code> (Hypercore replicates over that socket; the page stops receiving raw <code>data</code> events on it) and <code>swarm.setAutoReplicate(...)</code>, which does the same automatically for every new connection on a swarm — see <a href="/use-cases/#sync">the data-sync use case</a> for a worked example.</p>
|
|
</section>
|
|
|
|
<section id="protomux-hrpc" aria-labelledby="protomux-hrpc-title">
|
|
<h2 id="protomux-hrpc-title">Protomux & HRPC</h2>
|
|
<p>Two different problems, two different layers:</p>
|
|
<ul>
|
|
<li><strong>Protomux</strong> runs <em>in the page</em>. BridgeSwarm injects a bundle of Protomux, <code>compact-encoding</code>, and <code>b4a</code> so you can multiplex several message-oriented protocols over one connection: <code>swarm.createProtomux(conn)</code> gives you a <code>Protomux</code> instance you open channels on with your own <code>protocol</code> name and message encodings.</li>
|
|
<li><strong>HRPC</strong> runs <em>on the host</em>. Call <code>BridgeSwarm.request('attachHrpc', { connId })</code> on both ends of a connection, then invoke schema-defined methods with <code>swarm.hrpcCall(connId, method, args)</code> — unary, request-stream, response-stream, and duplex modes are all supported, surfaced to the page as <code>hrpc-chunk</code> / <code>hrpc-end</code> / <code>hrpc-error</code> events.</li>
|
|
</ul>
|
|
<pre><code>// Protomux, in the page
|
|
const mux = swarm.createProtomux(conn);
|
|
const ch = mux.createChannel({ protocol: 'my-app/v1' });
|
|
const msg = ch.addMessage({ encoding: c.string, onmessage: console.log });
|
|
ch.open();
|
|
msg.send('hello');
|
|
|
|
// HRPC, via the host
|
|
await BridgeSwarm.request('attachHrpc', { connId: conn.connId });
|
|
const res = await swarm.hrpcCall(conn.connId, 'ping', { value: 'hi' });</code></pre>
|
|
<p>Use Protomux when you're happy hand-rolling a small message protocol in the page. Reach for HRPC when you want typed methods and streaming without writing your own dispatcher.</p>
|
|
</section>
|
|
|
|
<section id="capabilities" aria-labelledby="capabilities-title">
|
|
<h2 id="capabilities-title">Capability packs</h2>
|
|
<p>Not every Bare module the host depends on is exposed to the page — that's intentional. <strong>Capability packs</strong> are the curated, explicit surface: <code>BridgeSwarm.capabilities.list()</code>, <code>.has(name)</code>, and <code>.call(pack, cmd, payload)</code>, plus convenience wrappers like <code>BridgeSwarm.media.*</code>.</p>
|
|
<p>The default host ships the <strong>media</strong> pack (<code>bare-media</code> + <code>bare-ffmpeg</code>): image/video probing, transforms, batch transcoding, and <strong>live VP9/WebM encode</strong> from a capture source straight through to MSE playback or a peer connection. Streaming results arrive as <code>cap-chunk</code> / <code>cap-end</code> / <code>cap-error</code> events to stay under the native-messaging size limit. Filesystem access for packs is allowlisted under <code>$BRIDGE_SWARM_STORAGE/cap-jobs/</code> — absolute paths outside that tree are rejected by the host.</p>
|
|
<p>See <a href="/use-cases/#live-media">live media</a> for the full flow from camera capture to encoded output.</p>
|
|
</section>
|
|
|
|
<section id="control-center" aria-labelledby="control-center-title">
|
|
<h2 id="control-center-title">Control Center</h2>
|
|
<p>The extension's dashboard is a live view into the background service worker's state over a dedicated port (<code>bridgeswarm-dashboard</code>): <strong>Overview</strong> (host connection health), <strong>Swarms</strong>, <strong>Connections</strong>, <strong>Activity</strong> (a log viewer), and <strong>Settings</strong>. Every setting — default app name, max peers, request/ready timeouts, whether to skip injection on <code>file://</code>, disconnect notifications, the examples-server toggle, debug logging — <strong>autosaves on change</strong>. There is no Save button and no separate config file to hand-edit.</p>
|
|
</section>
|
|
|
|
<section id="mental-model" aria-labelledby="mental-model-title">
|
|
<h2 id="mental-model-title">Mental model: live domains & self-hosted apps</h2>
|
|
<p>Two ideas that make more sense once the pieces above click into place:</p>
|
|
<p><strong>A topic as a live domain.</strong> A DNS name resolves to an IP once, statically, through infrastructure you don't control. A Hyperswarm topic is closer to an address you dial directly: whoever currently has the host running and has joined that topic is reachable, with no registrar, no hosting bill, and no server between you and them. You can treat a topic string as a lightweight, ephemeral "domain" for a specific room, session, or shared document — it exists exactly as long as at least one peer is announcing it.</p>
|
|
<p><strong>Self-hosted web apps, for free.</strong> The native host's optional <strong>examples server</strong> is a small <code>bare-http1</code> static server bound to <code>127.0.0.1:4173</code>. It's a working demonstration of a broader pattern: once BridgeSwarm is installed, the host can serve real HTML/CSS/JS on localhost, and the extension's content script still injects <code>window.BridgeSwarm</code> into pages loaded from it (deliberately <em>not</em> into <code>file://</code> pages, which the browser treats as opaque, unrelated origins). That means a fully self-hosted, offline-capable app — with P2P networking built in — can live entirely on a user's machine with no cloud hosting step at all. See <a href="/use-cases/#self-hosted">self-hosted web apps</a>.</p>
|
|
</section>
|
|
|
|
<section id="flows" aria-labelledby="flows-title">
|
|
<h2 id="flows-title">Message flows</h2>
|
|
<p>A request from the page to the host and back:</p>
|
|
<pre class="mermaid">
|
|
sequenceDiagram
|
|
participant Page
|
|
participant ContentScript
|
|
participant Background
|
|
participant NativeHost
|
|
Page->>ContentScript: postMessage (bridge-swarm-bridge)
|
|
ContentScript->>Background: runtime.sendMessage (action: send)
|
|
Background->>NativeHost: port.postMessage {id, type, payload}
|
|
NativeHost->>NativeHost: handleMessage -> reply(result)
|
|
NativeHost->>Background: messenger.send {id, type: response}
|
|
Background->>ContentScript: sendResponse(payload)
|
|
ContentScript->>Page: dispatchEvent (bridge-swarm-bridge-response)
|
|
</pre>
|
|
<p>An unsolicited event from the host (a new connection, a capability chunk) reaching the page:</p>
|
|
<pre class="mermaid">
|
|
sequenceDiagram
|
|
participant NativeHost
|
|
participant Background
|
|
participant ContentScript
|
|
participant Page
|
|
NativeHost->>NativeHost: emit(event, payload)
|
|
NativeHost->>Background: messenger.send {type: event}
|
|
alt swarm-scoped event
|
|
Background->>ContentScript: tabs.sendMessage (only tabs owning swarmId)
|
|
else capability / global event
|
|
Background->>ContentScript: tabs.sendMessage (all subscribed tabs)
|
|
end
|
|
ContentScript->>Page: dispatchEvent (bridge-swarm-event)
|
|
Page->>Page: api.js _onEvent (connection / data / end / error / cap-*)
|
|
</pre>
|
|
<p>And the connection lifecycle underneath every swarm:</p>
|
|
<pre class="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 --> [*]
|
|
</pre>
|
|
</section>
|
|
|
|
<section id="glossary" aria-labelledby="glossary-title">
|
|
<h2 id="glossary-title">Glossary</h2>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<tbody>
|
|
<tr><td style="white-space:nowrap;"><strong>Bare</strong></td><td>A small JavaScript runtime for native modules, built by Holepunch to run Pear, Keet, and BridgeSwarm's native host outside the browser sandbox.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Hyperswarm</strong></td><td>Peer discovery and connection library built on a DHT; peers announce/lookup a topic and connect directly, with NAT traversal handled automatically.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Topic</strong></td><td>A 32-byte value (often a hash of a human-readable string) that peers join to discover each other. No central registration required.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Noise protocol</strong></td><td>The handshake and encryption framework used to secure every Hyperswarm connection by default.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Corestore</strong></td><td>A factory/manager for multiple named Hypercores backed by one storage directory.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Hypercore</strong></td><td>An append-only, cryptographically verifiable log — the base primitive under Hyperbee, Hyperdrive, and Autobase.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Hyperbee</strong></td><td>A key/value B-tree store built on a Hypercore.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Hyperdrive</strong></td><td>A P2P filesystem built on Hypercore, with paths, directory listings, and file content.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Autobase</strong></td><td>A multi-writer linearized log that merges several Hypercores from different peers into one causal history.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Hyperdb</strong></td><td>A schema-based (Hyperschema-generated) P2P database with typed collections and queries.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Protomux</strong></td><td>A library for multiplexing multiple named protocols/channels over one connection, used in-page by BridgeSwarm.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>HRPC</strong></td><td>Schema-defined RPC (unary and streaming) that BridgeSwarm runs on the host side of an attached connection.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Native messaging</strong></td><td>The browser API that lets an extension exchange length-prefixed JSON with one specific, registered local executable.</td></tr>
|
|
<tr><td style="white-space:nowrap;"><strong>Capability pack</strong></td><td>A curated set of host-side commands (e.g. <code>media</code>) exposed to the page through <code>BridgeSwarm.capabilities</code>.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="docs-pager">
|
|
<span></span>
|
|
<a class="next" href="/use-cases/"><span>Keep going</span><strong>Use cases →</strong></a>
|
|
</div>
|
|
|
|
</article>
|
|
</div>
|
|
|
|
<section class="cta-band reveal">
|
|
<div class="container">
|
|
<div class="cta-card">
|
|
<h2>See it running</h2>
|
|
<p class="lead">Every concept above has a runnable demo on the examples server once BridgeSwarm is installed.</p>
|
|
<div class="cta-actions">
|
|
<a class="btn btn-primary btn-lg" href="/download">Install BridgeSwarm</a>
|
|
<a class="btn btn-ghost btn-lg" href="/use-cases/">Browse use cases</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
|
|
<div data-footer data-prefix="../"></div>
|
|
|
|
<script type="module" src="/src/js/site.js"></script>
|
|
</body>
|
|
</html>
|