---
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:
  ```python
  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.
