name: shipping-a-bridge description: How to ship + UPDATE a third-party Adom Desktop bridge so AD actually picks it up. The cardinal fact authors get wrong: AD reads ONLY the bridge RELEASE ARTIFACT (a manifest + a zip), NEVER the git Files tab of your wiki page - so pushing your source to the repo updates what humans browse but does NOT reach AD. Covers building the manifest + versioned zip (files at zip root), the wiki .zip-upload trap (host the bytes as .bin), the canonical owner-scoped file URL (not the legacy /api/v1 alias), bridge_install (first time - records the manifest URL for auto-update, AD 1.9.26+) vs refresh_bridges (after; no manual kill on AD 1.9.27+), verifying with bridge_check_updates, and the rule that a new verb must be added to bridge.json verbs[] (and to your describe verb) or AD won't route it. Read when cutting a new bridge version, adding a verb, or when "AD is still running the old bridge". Companion to the bridge SDK guide (bridge.json schema + hello-python/hello-rust templates).

Companion to the adom-desktop-bridges SDK guide (bridge.json schema, kind:python/node/exe, the hello-python / hello-rust templates, scripts/release-bridge.sh). This page is the release + update flow and the traps.

Shipping a bridge update (so adom-desktop picks it up)

Your bridge runs on the user's machine, spawned by AD from its writable bridges-cache. Updating it is NOT a normal git push.

⛔ The cardinal fact: AD reads the RELEASE ARTIFACT, not your git Files

Your wiki page has two independent layers (same split as a README vs a package registry):

  • Git Files (adom-wiki repo push) = what humans browse. AD never reads this for bridge code.
  • The release artifact = a <bridge>-bridge-manifest.json (version + zip url + sha256) plus a versioned zip. This is the ONLY thing AD pulls.

So pushing your server.js to the repo changes nothing for AD. You must publish a new artifact and bump the version.

The artifact

  • <bridge>-bridge-manifest.json = { "manifest_version":1, "version", "url", "sha256", "size", "released_at" }.
  • A zip whose ROOT contains bridge.json + your server file(s) (no top-level folder). AD requires bridge.json at the zip root. The easiest path is scripts/release-bridge.sh <name> (it builds the zip, sha256s it, writes the manifest). The manual equivalent:
    import zipfile,hashlib,os
    out="/tmp/<bridge>-bridge-v<ver>.zip"
    with zipfile.ZipFile(out,"w",zipfile.ZIP_DEFLATED) as z:
        for f in ["bridge.json","server.js","package.json","BRIDGE_VERSION"]:   # your files, at ROOT
            z.write(f, arcname=os.path.basename(f))
    raw=open(out,"rb").read(); sha=hashlib.sha256(raw).hexdigest(); size=len(raw)
    
    (Note: in the Adom cloud container there is no zip binary - use Python zipfile as above.)

⛔ Trap: the wiki silently rejects .zip uploads

POST .../files with a .zip returns {ok:true, commit:null} but the file never lands (a .png commits fine through the same path). So host the zip bytes under a .bin name (<bridge>-bridge-v<ver>.bin) and point the manifest url at that. AD just HTTP-GETs the url and unzips the bytes - the extension is cosmetic. (adom-wiki repo push also drops binaries except PNG; push the .bin with adom-wiki api -X POST .../files as base64, and the manifest + source as text.)

⛔ Use the CANONICAL file URL (not the legacy alias)

Give AD the owner-scoped, no-/v1 route:

https://wiki.adom.inc/api/pages/<owner>/<slug>/files/<bridge>-bridge-manifest.json

The /api/v1/pages/<slug>/... form is a legacy slug-only alias that still resolves but is fragile (breaks on fork / when v1 retires). Older docs show the /api/v1/ form; prefer the canonical one.

Bump, build, publish

  1. Bump the version in every place your runtime + manifest read it - typically bridge.json version, BRIDGE_VERSION, and your package.json version. A half-bump = AD sees no update, or a mismatched runtime version string.
  2. Build the zip (root layout), compute sha256 + size, write the manifest with url:"<...>.bin".
  3. Upload the .bin (base64) + push the manifest + source.
  4. Verify the artifact is live + intact (AD sha256-checks on download): GET the .bin, confirm bytes == size and sha256sum == the manifest sha256.

Make AD pick it up

  • First time on a machine: adom-desktop bridge_install '{"manifestUrl":"<canonical manifest url>"}'. Downloads + sha-verifies + extracts into bridges-cache, AND persists the manifest URL so the bridge auto-updates from its own page. (AD 1.9.26+ tracks per-bridge manifest URLs - this is what lets a community bridge on its OWN wiki page auto-refresh, not just the bundled ones.)
  • Every update after that: adom-desktop refresh_bridges '{"name":"<bridge>"}' (or AD's on-launch + 4-hourly auto-sync). AD 1.9.27+ auto-reaps the running process and respawns on the new code - NO manual bridge_kill. Result: action:"updated", from, to. The running bridge respawns on the next verb call.

Verify the update landed

  • adom-desktop bridge_check_updates -> your bridge shows current == new version, wikiHosted:true, updateAvailable:false. (If wikiHosted:false/latest:null, AD has no tracked manifest URL - re-run bridge_install from the canonical URL once to register it.)
  • Call one of your verbs (or your <prefix>_describe if you have one) to confirm the new code is running.

Adding a verb? Update routing (and docs)

  • bridge.json verbs[] - AD ROUTES a verb to your bridge only if it is declared here. Miss this and the verb 404s no matter what your server does.
  • If you expose a <prefix>_describe verb (AD 1.9.23+ renders it in the GUI Verbs tab), add the new verb's schema there too, so it shows up with input/output docs + the inline runner.

Mental model + the traps, in one line each

  • Two layers: git Files = humans; release artifact = AD. A repo push is NOT shipping.
  • .zip is silently blocked on the wiki API - host as .bin.
  • No zip binary in the cloud container - Python zipfile, files at the zip root.
  • Canonical owner-scoped URL for the manifest, not /api/v1/<slug>.
  • Bump every version file together.
  • refresh_bridges self-reaps (AD 1.9.27+) - re-running bridge_install per update is no longer needed.