APP

Writing a third-party Adom Desktop bridge

Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.

Writing a third-party Adom Desktop bridge
๐Ÿ’ฌ Sample prompts Paste any of these into Claude Code to use this app
Write a bridge Help me write a third-party Adom Desktop bridge for X
Install bridge Install the hello-python sample bridge so I can see how third-party bridges work
Try Rust bridge Install the hello-rust sample bridge โ€” I want to see a single-binary Rust example
Package bridge Package my bridge dir as a wiki-installable manifest+zip pair
โšก Open this app

Paste this into Claude Code (VS Code panel, Adom editor, or terminal) to install:

Open the Adom Wiki page for the "Writing a third-party Adom Desktop bridge" app at https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges and tell me how to use it.

Writing a third-party Adom Desktop bridge

Public version of this guide: apps/adom-desktop-bridges on the Adom Wiki. 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.

FlavorManifest URLVerb prefixPortWhen to fork
Python (stdlib only, ~80 lines)โ€ฆ/hello-python-bridge-manifest.jsonhellopy_8890Vendor has Python bindings, fast dev iter, I/O-bound work
Rust (tiny_http + serde, single static binary, ~120 lines)โ€ฆ/hello-rust-bridge-manifest.jsonhellors_8891No 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

# 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

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:

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

{
  "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:

# 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:

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:

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)

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 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: scripts/sample-bridges/hello-python/ โ€” ~80 lines, stdlib only, kind:python
  • hello-rust: 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: plugins/kicad/ โ€” Python, complex, multi-instance (one bridge per KiCad GUI exe)
  • puppeteer: plugins/puppeteer/ โ€” Node, single-instance, recording-capable
  • fusion360: plugins/fusion360/ โ€” Python, simpler, command-passthrough to Fusion add-in

Get listed

Once your bridge is stable, add it to the 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 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 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.
MD
bridges-SDK.md 5 days ago
Bridge SDK guide v1.8.31 (dynamic ports)
v1.8.31: dynamic port allocation (spawn.port:0) is now the recommended pattern; hardcoded ports deprecated. healthEndpoint accepts path-only fragment. Updated examples + back-compat notes for community bridge authors.John Lauer ยท 5 days ago
11.3 KB

Sub-Skills
?
What are Sub-Skills?

Sub-skills are community-contributed AI skill extensions for this component. They teach AI assistants about specific tools, configurators, or workflows.

Examples:

  • A manufacturer’s configuration tool for a motor controller
  • A community-written design guide for an amplifier circuit
  • An automated test/validation script for a sensor module

How to add one: Click Add Sub-Skill, provide the URL to your skill and a brief description. Submissions are reviewed by the Adom team before going live.

No sub-skills yet. Be the first to contribute one!

๐Ÿ”Ž How Claude finds this page (discovery snippet)

This page opts into Adom Wiki auto-discovery. When a user working in Claude Code mentions any of the trigger phrases below, Claude can proactively suggest this page. The pitch is exactly what Claude will say.

Pitch
"Write a third-party bridge for adom-desktop โ€” ship a custom EDA / lab / vendor integration as a stdlib-Python or Node server, install it via a wiki manifest URL, no fork of adom-desktop required."
Triggers
"third-party bridge", "adom-desktop bridge", "bridge SDK", "bridge.json", "bridge manifest", "write a bridge", "custom bridge", "altium bridge", "matlab bridge", "oscilloscope bridge", "hello sample bridge", "bridge_install", "bridge_uninstall", "bridge_list", "bridge_pause", "bridge_resume", "refresh_bridges", "dynamic bridge", "ship a bridge", "host a bridge", "bridges-registry", "extend adom-desktop"

Recent activity

5 commits