← Commit history

Upload 1 file(s)

John Lauer ·e2884fe88c ·4mo ago ·parent abbeb37
1 file changed +309−231
README.md+309−231
@@ -1,232 +1,310 @@-# Adom Desktop - Bridge SDK--![How the Adom Desktop bridges bridge works](arch.png)--![Adom Desktop bridges verbs Claude can call](verbs.png)--![What you can ask Claude to do](outcomes.png)--> **Public version of this guide:** [`apps/adom-desktop-bridges` on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges). The wiki page is the canonical user-facing copy and is what the GUI's "+ Bridge" install modal links to (the adom-inc GitHub repo is private). When you update this file with breaking schema changes or new lifecycle commands, also republish the wiki page via `adom-wiki page edit apps/adom-desktop-bridges --field content --body-md <this file>`.--v1.8.0+ supports dynamic bridges: any zip with a `bridge.json` at its root can be installed via `adom-desktop bridge_install --manifestUrl <wiki-or-github-url>`. This lets community authors ship bridges for Altium, MATLAB, Rohde & Schwarz oscilloscopes, Keysight, OrCAD, anything — without coordinating with the Adom Desktop release cycle.--## Try it now — the `hello-python` and `hello-rust` sample bridges--Two parallel reference bridges are hosted on the Adom Wiki, one per language. Both expose the same two verbs (`*_ping` / `*_echo`) under different prefixes (`hellopy_` / `hellors_`), so they can be installed simultaneously for a side-by-side demo. Both use `port: 0` (dynamic OS-assigned ports — v1.8.31+) so no collision is possible.--| Flavor | Manifest URL | Verb prefix | Port | When to fork |-|---|---|---|---|---|-| **Python** (stdlib only, ~80 lines) | `…/hello-python-bridge-manifest.json` | `hellopy_` | 8890 | Vendor has Python bindings, fast dev iter, I/O-bound work |-| **Rust** (`tiny_http` + serde, single static binary, ~120 lines) | `…/hello-rust-bridge-manifest.json` | `hellors_` | 8891 | No Python runtime on user's machine, CPU-bound, type safety |--Both manifest URLs are under `https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/`.--### Install from the GUI--1. Click **+ Bridge** in Adom Desktop's bridge bar-2. Paste one (or both) of the manifest URLs into the dialog, click **Install**-3. New chips appear ~2 seconds later with a small ✦ marker (third-party indicator)--### Install both from the CLI--```bash-# Install the Python flavor-adom-desktop bridge_install '{"manifestUrl":"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-python-bridge-manifest.json"}'-adom-desktop hellopy_ping-# → {"success":true,"output":"Hello from the Python sample bridge v1.1.0!","language":"python",...}--# Install the Rust flavor (can coexist with hello-python)-adom-desktop bridge_install '{"manifestUrl":"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-rust-bridge-manifest.json"}'-adom-desktop hellors_ping-# → {"success":true,"output":"Hello from the Rust sample bridge v1.1.0!","language":"rust",...}--# Echo through both-adom-desktop hellopy_echo '{"message":"world"}'-# → {"success":true,"output":"echo (python): world",...}-adom-desktop hellors_echo '{"message":"world"}'-# → {"success":true,"output":"echo (rust): world",...}--# Both appear in bridge_list-adom-desktop bridge_list-# → bundled three + "hello-python" + "hello-rust"-```--### Uninstall--```bash-adom-desktop bridge_uninstall '{"name":"hello-python"}'-adom-desktop bridge_uninstall '{"name":"hello-rust"}'-```--Each bridge's cache dir + registry entry are removed; the chips disappear from the bar.--The sources live at:-- [`scripts/sample-bridges/hello-python/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-python) — `bridge.json` + `server.py` (~120 lines of `http.server.BaseHTTPRequestHandler`)-- [`scripts/sample-bridges/hello-rust/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-rust) — `bridge.json` + `Cargo.toml` + `src/main.rs` (~140 lines of `tiny_http`) + pre-built `hello-rust.exe` (~300 KB)--Fork whichever flavor matches your target's natural ecosystem.--## Directory layout--```-my-bridge/-├── bridge.json         ← REQUIRED — schema below-├── BRIDGE_VERSION      ← optional, semver line (kept in sync with bridge.json's version field)-├── server.py           ← or server.js, or my-bridge.exe — your bridge's entrypoint-├── handlers/           ← your code-│   ├── ...-└── README.md           ← optional, recommended-```--## `bridge.json` schema--```json-{-  "manifest_version": 1,-  "name": "altium",-  "displayName": "Altium Designer",-  "version": "1.0.0",-  "description": "One-line description shown in bridge_list output.",-  "homepage": "https://github.com/your-org/adom-bridge-altium",-  "author": "Your Org",-  "license": "MIT",-  "spawn": {-    "kind": "python",-    "entrypoint": "server.py",-    "port": 0,-    "healthEndpoint": "/status",-    "stopMethod": "kill",-    "killImageName": "python.exe"-  },-  "verbPrefixes": ["altium_"],-  "verbs": [-    "altium_open_project",-    "altium_export_step",-    "altium_run_drc"-  ],-  "dependencies": {-    "python": ">=3.11",-    "altium-designer": ">=24"-  },-  "category": "EDA",-  "tags": ["pcb", "altium", "commercial"]-}-```--### Field reference--- **`name`** (required, lowercase, no spaces). The directory name + cache key. Must be unique across the registry.-- **`displayName`** — human-friendly shown in `bridge_list` + future GUI bridge panel.-- **`version`** (required, semver). Each ship bumps this.-- **`spawn.kind`** — `python` | `node` | `exe`. Determines how Adom Desktop launches the bridge.-    - `python` → Adom Desktop finds Python in PATH and runs `python <entrypoint> --port <port>`. Good when the vendor's SDK has Python bindings.-    - `node` → Adom Desktop finds Node in PATH and runs `node <entrypoint> --port <port>`. Good for ecosystem reuse (Puppeteer, Playwright, etc.).-    - `exe` → Adom Desktop runs `<entrypoint> --port <port>` directly. The entrypoint must be a pre-built executable in the zip. Good for Rust, Go, .NET single-file builds, etc. — no runtime install required on the user's machine.-- **`spawn.entrypoint`** — relative path inside the bridge dir (e.g. `server.py`, `server.js`, `my-bridge.exe`).-- **`spawn.port`** — **set to `0`** (v1.8.31+ recommended). Adom Desktop picks an OS-assigned ephemeral port at spawn time and passes it to your `--port` arg. No collision possible between bridges. Hardcoded ports still work for back-compat but are deprecated — anything you pick will eventually collide with someone (Hydrogen Desktop's bridges already used 8772/8773/8851; static-port collisions are the #1 source of bridge "won't start" bugs). Bridge ports are internal plumbing — callers always route through `adom-desktop <prefix>_<verb>`, never reach a bridge directly.-- **`spawn.healthEndpoint`** — path Adom Desktop polls to confirm the bridge is alive (e.g. `/status`, `/health`). v1.8.31+ accepts a path-only fragment (recommended) and constructs the URL using the runtime port. Legacy full URLs (`http://127.0.0.1:8881/status`) still parse — the host+port get replaced at runtime.-- **`spawn.stopMethod`** — `kill` (process-handle Child::kill, NOT `taskkill /im` — that would nuke ALL Pythons on the box), `sigterm` (graceful), or `graceful_endpoint` (POST to an endpoint).-- **`spawn.killImageName`** — legacy field, only used if Adom Desktop falls back to image-name kill. With v1.8.31's dynamic ports + process-handle kill this is rarely needed. Set to your process name for safety (`python.exe` / `node.exe` / your binary name).-- **`verbPrefixes`** — list of CLI verb prefixes this bridge owns (e.g. `["altium_"]`). All `altium_*` verbs route here.-- **`verbs`** — explicit verb list. Used in `bridge_list` to advertise capabilities; doesn't affect routing.--## Packaging + shipping--Use the same `scripts/release-bridge.sh` flow Adom Desktop uses for its own bridges. Adapted for third parties:--```bash-# In your bridge repo:-python3 -c "-import zipfile, os, fnmatch-EXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*', '*/node_modules/*']-with zipfile.ZipFile('my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:-    for root, dirs, files in os.walk('.'):-        for f in files:-            rel = os.path.relpath(os.path.join(root, f), '.')-            if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):-                continue-            zf.write(os.path.join(root, f), rel)-"--# Compute SHA256 of the zip-sha256sum my-bridge-v1.0.0.zip--# Write the manifest the GUI polls-cat > my-bridge-manifest.json <<EOF-{-  "manifest_version": 1,-  "version": "1.0.0",-  "url": "https://your-host/my-bridge-v1.0.0.zip",-  "sha256": "<sha256 from above>",-  "size": <bytes>,-  "released_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"-}-EOF-```--Host both files anywhere reachable by HTTPS — GitHub Pages, your own CDN, the Adom Wiki if you contribute to the official registry.--## Installing--Users install your bridge with one command:--```bash-adom-desktop bridge_install '{"manifestUrl":"https://your-host/my-bridge-manifest.json"}'-```--The GUI:-1. Fetches `my-bridge-manifest.json`-2. Downloads the referenced zip-3. SHA256-verifies against the manifest-4. Extracts to `%LOCALAPPDATA%\Adom Desktop\bridges-cache\<name>\` (the `name` comes from your `bridge.json`)-5. Rescans the runtime registry — your bridge is now discoverable via `bridge_list`--The next CLI call to one of your `verbPrefixes` auto-spawns the bridge process.--## Updating--Bump `bridge.json`'s `version` + `BRIDGE_VERSION`, rebuild the zip, re-upload, update the manifest. Users:--```bash-adom-desktop refresh_bridges   # picks up new versions from manifest URLs they previously installed from-```--(Currently `refresh_bridges` only polls the three core wiki manifests. The follow-up work — tracking third-party manifest URLs in user state so they auto-refresh — lands in v1.8.x.)--## Lifecycle controls (from cloud Docker, or local GUI)--```bash-adom-desktop bridge_list                              # see everything-adom-desktop bridge_pause '{"name":"altium"}'         # stop routing verbs-adom-desktop bridge_resume '{"name":"altium"}'-adom-desktop bridge_uninstall '{"name":"altium"}'     # remove from cache-```--## Trust + security--For v1.8.0 the trust model is SHA256-verification of the wiki-hosted zip against the manifest. Bridges run with the user's privileges; install from sources you trust.--Future work (v1.9.0+):-- Optional Ed25519 signing of bridge zips with the Adom-Inc public key for "verified" status-- Per-bridge sandboxing (limited filesystem / network access declared in manifest `permissions` field)-- Community submission flow on the wiki so contributors don't need a GitHub account--## Reference implementations--Every bridge below has its own dedicated wiki page with hero, install/uninstall recipes, and a "fork this" walkthrough. The **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** is the canonical directory — both humans browse it and the GUI's Browse tab parses it.--**Hello-world starter templates** — fork these for a 5-minute "does this thing work?" loop:-- **[hello-python](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-python-bridge)**: `scripts/sample-bridges/hello-python/` — ~80 lines, stdlib only, `kind:python`-- **[hello-rust](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-rust-bridge)**: `scripts/sample-bridges/hello-rust/` — ~140 lines, `tiny_http` + serde, `kind:exe`, single 300-KB static binary--**Adom-shipped bridges** — canonical full-complexity examples; fork their layout for a real vendor integration:-- **[kicad](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge)**: `plugins/kicad/` — Python, complex, multi-instance (one bridge per KiCad GUI exe)-- **[puppeteer](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge)**: `plugins/puppeteer/` — Node, single-instance, recording-capable-- **[fusion360](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge)**: `plugins/fusion360/` — Python, simpler, command-passthrough to Fusion add-in--## Get listed--Once your bridge is stable, add it to the **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** — that's the canonical machine-readable directory the GUI's Browse tab parses to discover what's installable. Two paths:--1. **PR-based** (preferred): open a PR against [`adom-inc/adom-desktop`](https://github.com/adom-inc/adom-desktop) adding your row to the "Community bridges" table in this file (`plugins/BRIDGES.md`). A maintainer mirrors it to the wiki on the next sync. See the registry page's [submission section](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry#how-to-submit-a-bridge) for the row format + review criteria.+# Adom Desktop - Bridge SDK
+
+## Build your own bridge
+
+A **bridge** is any small local program that exposes a few verbs over a tiny HTTP server. Adom Desktop (AD) discovers it from a `bridge.json`, spawns it, and relays your verbs to it from anywhere \u2014 including a cloud Claude session. If you can write a 50-line HTTP server in any language, you can ship a bridge.
+
+The fastest path is to **let Claude build it for you**. Open a cloud Claude (or Claude Code) session that has the `adom-desktop` CLI installed, and paste a prompt like the one below.
+
+### Starting prompt (paste into a cloud Claude session)
+
+```
+I want to build an Adom Desktop bridge for <YOUR APP / DEVICE / SERVICE>.
+
+A bridge is a small local HTTP server described by a bridge.json; AD spawns it on a
+stable port and relays verbs to it. Before writing anything, study the reference
+bridges for design ideas and copy the conventions you see:
+
+  - START SIMPLE \u2014 read the two "hello" reference bridges first (one Rust, one
+    Python). They are the minimal, correct shape: a /command POST handler, a /status
+    health endpoint that self-reports its LED, and a bridge.json. Mirror them.
+  - THEN study an advanced bridge for the patterns you need: a long-running native
+    app (KiCad / Fusion 360), a process you supervise, or an external client that
+    dials back in (native-browser). Steal their bridge.json fields and verb shapes.
+
+My bridge should expose these verbs: <list them, e.g. foo_status, foo_do_thing>.
+
+Follow the Adom Desktop Bridge SDK conventions on this page:
+  - Self-report {led, summary, tooltip} from the health endpoint (AD is a pure
+    renderer \u2014 it never invents your status).
+  - Return rich, structured JSON with an _hint on every response so the calling AI
+    knows what to do next; on a slow operation, return a statusVerb to poll.
+  - Declare statusVerb in bridge.json if any verb can run longer than ~60s (AD will
+    relay it so a timeout reads as "still running, poll this", not "failed").
+
+When it works locally, package it with release-bridge.sh and tell me the
+bridge_install command so I can install it on my Adom Desktop from here.
+```
+
+Claude will read the reference bridges, scaffold your `bridge.json` + server, run it locally, and hand you a one-line install command. You stay in the cloud session the whole time.
+
+### Reference bridges \u2014 copy these
+
+Start with a **hello** bridge (minimal + correct), then borrow from an **advanced** one for the hard parts.
+
+| Bridge | Language | Why read it |
+|---|---|---|
+| **hello-python** | Python | The minimal bridge: `/command` + `/status` self-report + `bridge.json`. The shape every bridge follows. |
+| **hello-rust** | Rust | Same minimal shape in Rust \u2014 single-binary, no runtime deps. |
+| **native-browser** | \u2014 | Advanced: an **external client** (a Chrome extension) dials back into the bridge; shows the stable-port + reconnect pattern. |
+| **kicad** / **fusion360** | Python | Advanced: supervising a **long-running native app**, multi-screenshot arrays, `statusVerb` for slow cloud operations. |
+
+The hello bridges live in the AD repo under `scripts/sample-bridges/`; each advanced bridge has its own wiki page (open it from AD's Bridges panel, or the apps index).
+
+### The whole dev loop is remote \u2014 install & uninstall from anywhere
+
+This is the part that makes bridge development easy: **you never touch the Windows box by hand.** From any cloud Claude session with the `adom-desktop` CLI:
+
+```bash
+# Install (or update) a bridge from its published wiki manifest:
+adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/api/v1/pages/<your-bridge-page>/files/<your>-bridge-manifest.json"}'
+
+# It appears in AD's Bridges panel immediately and starts on first verb call.
+adom-desktop bridge_list                         # see it registered + its LED
+adom-desktop <your_verb> '{...}'                  # call it \u2014 AD spawns + relays
+
+# Iterate: re-publish a higher version, then re-install \u2014 AD reaps the old
+# instance and streams the new one onto the SAME stable port (no port churn).
+adom-desktop bridge_install '{"manifestUrl":"...","force":true}'
+
+# Remove it cleanly when done:
+adom-desktop bridge_uninstall '{"name":"<your-bridge>"}'
+```
+
+In the AD GUI, the same flows are one click: **"+ Bridge"** (top of the Bridges panel) installs from a manifest URL, and each bridge card has **Uninstall** in its pop-out. Publishing the manifest + zip is a single `release-bridge.sh <name>` run (see the SDK reference below).
+
+That's the full toolchain \u2014 author in a cloud prompt, publish once, `bridge_install` from anywhere, iterate by re-installing. No RDP, no copying files to the desktop.
+
+---
+
+
+![How the Adom Desktop bridges bridge works](arch.png)
+
+![Adom Desktop bridges verbs Claude can call](verbs.png)
+
+![What you can ask Claude to do](outcomes.png)
+
+> **Public version of this guide:** [`apps/adom-desktop-bridges` on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges). The wiki page is the canonical user-facing copy and is what the GUI's "+ Bridge" install modal links to (the adom-inc GitHub repo is private). When you update this file with breaking schema changes or new lifecycle commands, also republish the wiki page via `adom-wiki page edit apps/adom-desktop-bridges --field content --body-md <this file>`.
+
+v1.8.0+ supports dynamic bridges: any zip with a `bridge.json` at its root can be installed via `adom-desktop bridge_install --manifestUrl <wiki-or-github-url>`. This lets community authors ship bridges for Altium, MATLAB, Rohde & Schwarz oscilloscopes, Keysight, OrCAD, anything \u2014 without coordinating with the Adom Desktop release cycle.
+
+## Try it now \u2014 the `hello-python` and `hello-rust` sample bridges
+
+Two parallel reference bridges are hosted on the Adom Wiki, one per language. Both expose the same two verbs (`*_ping` / `*_echo`) under different prefixes (`hellopy_` / `hellors_`), so they can be installed simultaneously for a side-by-side demo. Both use `port: 0` (dynamic OS-assigned ports \u2014 v1.8.31+) so no collision is possible.
+
+| Flavor | Manifest URL | Verb prefix | Port | When to fork |
+|---|---|---|---|---|
+| **Python** (stdlib only, ~80 lines) | `\u2026/hello-python-bridge-manifest.json` | `hellopy_` | 8890 | Vendor has Python bindings, fast dev iter, I/O-bound work |
+| **Rust** (`tiny_http` + serde, single static binary, ~120 lines) | `\u2026/hello-rust-bridge-manifest.json` | `hellors_` | 8891 | No Python runtime on user's machine, CPU-bound, type safety |
+
+Both manifest URLs are under `https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/`.
+
+### Install from the GUI
+
+1. Click **+ Bridge** in Adom Desktop's bridge bar
+2. Paste one (or both) of the manifest URLs into the dialog, click **Install**
+3. New chips appear ~2 seconds later with a small \u2726 marker (third-party indicator)
+
+### Install both from the CLI
+
+```bash
+# Install the Python flavor
+adom-desktop bridge_install '{"manifestUrl":"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-python-bridge-manifest.json"}'
+adom-desktop hellopy_ping
+# \u2192 {"success":true,"output":"Hello from the Python sample bridge v1.1.0!","language":"python",...}
+
+# Install the Rust flavor (can coexist with hello-python)
+adom-desktop bridge_install '{"manifestUrl":"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-rust-bridge-manifest.json"}'
+adom-desktop hellors_ping
+# \u2192 {"success":true,"output":"Hello from the Rust sample bridge v1.1.0!","language":"rust",...}
+
+# Echo through both
+adom-desktop hellopy_echo '{"message":"world"}'
+# \u2192 {"success":true,"output":"echo (python): world",...}
+adom-desktop hellors_echo '{"message":"world"}'
+# \u2192 {"success":true,"output":"echo (rust): world",...}
+
+# Both appear in bridge_list
+adom-desktop bridge_list
+# \u2192 bundled three + "hello-python" + "hello-rust"
+```
+
+### Uninstall
+
+```bash
+adom-desktop bridge_uninstall '{"name":"hello-python"}'
+adom-desktop bridge_uninstall '{"name":"hello-rust"}'
+```
+
+Each bridge's cache dir + registry entry are removed; the chips disappear from the bar.
+
+The sources live at:
+- [`scripts/sample-bridges/hello-python/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-python) \u2014 `bridge.json` + `server.py` (~120 lines of `http.server.BaseHTTPRequestHandler`)
+- [`scripts/sample-bridges/hello-rust/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-rust) \u2014 `bridge.json` + `Cargo.toml` + `src/main.rs` (~140 lines of `tiny_http`) + pre-built `hello-rust.exe` (~300 KB)
+
+Fork whichever flavor matches your target's natural ecosystem.
+
+## Directory layout
+
+```
+my-bridge/
+\u251c\u2500\u2500 bridge.json         \u2190 REQUIRED \u2014 schema below
+\u251c\u2500\u2500 BRIDGE_VERSION      \u2190 optional, semver line (kept in sync with bridge.json's version field)
+\u251c\u2500\u2500 server.py           \u2190 or server.js, or my-bridge.exe \u2014 your bridge's entrypoint
+\u251c\u2500\u2500 handlers/           \u2190 your code
+\u2502   \u251c\u2500\u2500 ...
+\u2514\u2500\u2500 README.md           \u2190 optional, recommended
+```
+
+## `bridge.json` schema
+
+```json
+{
+  "manifest_version": 1,
+  "name": "altium",
+  "displayName": "Altium Designer",
+  "version": "1.0.0",
+  "description": "One-line description shown in bridge_list output.",
+  "homepage": "https://github.com/your-org/adom-bridge-altium",
+  "author": "Your Org",
+  "license": "MIT",
+  "spawn": {
+    "kind": "python",
+    "entrypoint": "server.py",
+    "port": 0,
+    "healthEndpoint": "/status",
+    "stopMethod": "kill",
+    "killImageName": "python.exe"
+  },
+  "verbPrefixes": ["altium_"],
+  "verbs": [
+    "altium_open_project",
+    "altium_export_step",
+    "altium_run_drc"
+  ],
+  "dependencies": {
+    "python": ">=3.11",
+    "altium-designer": ">=24"
+  },
+  "category": "EDA",
+  "tags": ["pcb", "altium", "commercial"]
+}
+```
+
+### Field reference
+
+- **`name`** (required, lowercase, no spaces). The directory name + cache key. Must be unique across the registry.
+- **`displayName`** \u2014 human-friendly shown in `bridge_list` + future GUI bridge panel.
+- **`version`** (required, semver). Each ship bumps this.
+- **`spawn.kind`** \u2014 `python` | `node` | `exe`. Determines how Adom Desktop launches the bridge.
+    - `python` \u2192 Adom Desktop finds Python in PATH and runs `python <entrypoint> --port <port>`. Good when the vendor's SDK has Python bindings.
+    - `node` \u2192 Adom Desktop finds Node in PATH and runs `node <entrypoint> --port <port>`. Good for ecosystem reuse (Puppeteer, Playwright, etc.).
+    - `exe` \u2192 Adom Desktop runs `<entrypoint> --port <port>` directly. The entrypoint must be a pre-built executable in the zip. Good for Rust, Go, .NET single-file builds, etc. \u2014 no runtime install required on the user's machine.
+- **`spawn.entrypoint`** \u2014 relative path inside the bridge dir (e.g. `server.py`, `server.js`, `my-bridge.exe`).
+- **`spawn.port`** \u2014 **set to `0`** (v1.8.31+ recommended). Adom Desktop picks an OS-assigned ephemeral port at spawn time and passes it to your `--port` arg. No collision possible between bridges. Hardcoded ports still work for back-compat but are deprecated \u2014 anything you pick will eventually collide with someone (Hydrogen Desktop's bridges already used 8772/8773/8851; static-port collisions are the #1 source of bridge "won't start" bugs). Bridge ports are internal plumbing \u2014 callers always route through `adom-desktop <prefix>_<verb>`, never reach a bridge directly.
+- **`spawn.healthEndpoint`** \u2014 path Adom Desktop polls to confirm the bridge is alive (e.g. `/status`, `/health`). v1.8.31+ accepts a path-only fragment (recommended) and constructs the URL using the runtime port. Legacy full URLs (`http://127.0.0.1:8881/status`) still parse \u2014 the host+port get replaced at runtime.
+- **`spawn.stopMethod`** \u2014 `kill` (process-handle Child::kill, NOT `taskkill /im` \u2014 that would nuke ALL Pythons on the box), `sigterm` (graceful), or `graceful_endpoint` (POST to an endpoint).
+- **`spawn.killImageName`** \u2014 legacy field, only used if Adom Desktop falls back to image-name kill. With v1.8.31's dynamic ports + process-handle kill this is rarely needed. Set to your process name for safety (`python.exe` / `node.exe` / your binary name).
+- **`verbPrefixes`** \u2014 list of CLI verb prefixes this bridge owns (e.g. `["altium_"]`). All `altium_*` verbs route here.
+- **`verbs`** \u2014 explicit verb list. Used in `bridge_list` to advertise capabilities; doesn't affect routing.
+
+## Packaging + shipping
+
+Use the same `scripts/release-bridge.sh` flow Adom Desktop uses for its own bridges. Adapted for third parties:
+
+```bash
+# In your bridge repo:
+python3 -c "
+import zipfile, os, fnmatch
+EXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*', '*/node_modules/*']
+with zipfile.ZipFile('my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
+    for root, dirs, files in os.walk('.'):
+        for f in files:
+            rel = os.path.relpath(os.path.join(root, f), '.')
+            if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):
+                continue
+            zf.write(os.path.join(root, f), rel)
+"
+
+# Compute SHA256 of the zip
+sha256sum my-bridge-v1.0.0.zip
+
+# Write the manifest the GUI polls
+cat > my-bridge-manifest.json <<EOF
+{
+  "manifest_version": 1,
+  "version": "1.0.0",
+  "url": "https://your-host/my-bridge-v1.0.0.zip",
+  "sha256": "<sha256 from above>",
+  "size": <bytes>,
+  "released_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+}
+EOF
+```
+
+Host both files anywhere reachable by HTTPS \u2014 GitHub Pages, your own CDN, the Adom Wiki if you contribute to the official registry.
+
+## Installing
+
+Users install your bridge with one command:
+
+```bash
+adom-desktop bridge_install '{"manifestUrl":"https://your-host/my-bridge-manifest.json"}'
+```
+
+The GUI:
+1. Fetches `my-bridge-manifest.json`
+2. Downloads the referenced zip
+3. SHA256-verifies against the manifest
+4. Extracts to `%LOCALAPPDATA%\Adom Desktop\bridges-cache\<name>\` (the `name` comes from your `bridge.json`)
+5. Rescans the runtime registry \u2014 your bridge is now discoverable via `bridge_list`
+
+The next CLI call to one of your `verbPrefixes` auto-spawns the bridge process.
+
+## Updating
+
+Bump `bridge.json`'s `version` + `BRIDGE_VERSION`, rebuild the zip, re-upload, update the manifest. Users:
+
+```bash
+adom-desktop refresh_bridges   # picks up new versions from manifest URLs they previously installed from
+```
+
+(Currently `refresh_bridges` only polls the three core wiki manifests. The follow-up work \u2014 tracking third-party manifest URLs in user state so they auto-refresh \u2014 lands in v1.8.x.)
+
+## Lifecycle controls (from cloud Docker, or local GUI)
+
+```bash
+adom-desktop bridge_list                              # see everything
+adom-desktop bridge_pause '{"name":"altium"}'         # stop routing verbs
+adom-desktop bridge_resume '{"name":"altium"}'
+adom-desktop bridge_uninstall '{"name":"altium"}'     # remove from cache
+```
+
+## Trust + security
+
+For v1.8.0 the trust model is SHA256-verification of the wiki-hosted zip against the manifest. Bridges run with the user's privileges; install from sources you trust.
+
+Future work (v1.9.0+):
+- Optional Ed25519 signing of bridge zips with the Adom-Inc public key for "verified" status
+- Per-bridge sandboxing (limited filesystem / network access declared in manifest `permissions` field)
+- Community submission flow on the wiki so contributors don't need a GitHub account
+
+## Reference implementations
+
+Every bridge below has its own dedicated wiki page with hero, install/uninstall recipes, and a "fork this" walkthrough. The **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** is the canonical directory \u2014 both humans browse it and the GUI's Browse tab parses it.
+
+**Hello-world starter templates** \u2014 fork these for a 5-minute "does this thing work?" loop:
+- **[hello-python](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-python-bridge)**: `scripts/sample-bridges/hello-python/` \u2014 ~80 lines, stdlib only, `kind:python`
+- **[hello-rust](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-rust-bridge)**: `scripts/sample-bridges/hello-rust/` \u2014 ~140 lines, `tiny_http` + serde, `kind:exe`, single 300-KB static binary
+
+**Adom-shipped bridges** \u2014 canonical full-complexity examples; fork their layout for a real vendor integration:
+- **[kicad](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge)**: `plugins/kicad/` \u2014 Python, complex, multi-instance (one bridge per KiCad GUI exe)
+- **[puppeteer](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge)**: `plugins/puppeteer/` \u2014 Node, single-instance, recording-capable
+- **[fusion360](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge)**: `plugins/fusion360/` \u2014 Python, simpler, command-passthrough to Fusion add-in
+
+## Get listed
+
+Once your bridge is stable, add it to the **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** \u2014 that's the canonical machine-readable directory the GUI's Browse tab parses to discover what's installable. Two paths:
+
+1. **PR-based** (preferred): open a PR against [`adom-inc/adom-desktop`](https://github.com/adom-inc/adom-desktop) adding your row to the "Community bridges" table in this file (`plugins/BRIDGES.md`). A maintainer mirrors it to the wiki on the next sync. See the registry page's [submission section](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry#how-to-submit-a-bridge) for the row format + review criteria.
 2. **Direct wiki edit**: if you have an Adom account, `adom-wiki page edit apps/adom-desktop-bridges-registry --field content --body-md <patch>`. Maintainers review and either keep or revert.\ No newline at end of file