Files

4.5 KiB

Agent handshake — Bookmarks API for Scripting and AI

An API add-on for Firefox to allow agentic and script-based management of user bookmarks.

Use this when working in this repo. The shipped product is extension/ only.

Do not edit places.sqlite while Firefox is open if this API is reachable.

Preconditions

  1. Add-on loaded. Secrets are issued and stored in the add-on (toolbar → Manage clients). The options page does not need the native host.
  2. Native host installed only if scripts should call in: powershell -NoProfile -File tools/install-native-host.ps1
  3. Firefox running for those calls

If http://127.0.0.1:17634/health fails, the host is not up or the listen port was changed in Manage clients. If /health works but calls 401, you do not have a current token. Use FAB_URL when the port is not the default.

Auth

This is a normal API token, not an SSH key.

  1. In Firefox, open the Bookmarks API toolbar icon → Manage clients.
  2. Name the client and click Generate secret.
  3. Copy the fab_… value once. Store it with your other secrets (environment variable, password manager, .env, or CI secret).
  4. Give tooling only that string:
set FAB_TOKEN=fab_…
python tools/client.py meta.methods

FAB_TOKEN_FILE pointing at a file that contains only the token is also fine. --token on the CLI overrides both.

The add-on stores SHA-256(token) only. Revoke from the same page. A local process that never received the secret cannot call the API.

Preferred caller

python tools/client.py METHOD [ARGS_JSON]

ARGS_JSON is a JSON array matching the WebExtension function arguments.

python tools/client.py bookmarks.search "[{\"title\":\"Research\"}]"

HTTP:

POST /v1/call
Authorization: Bearer fab_…
Content-Type: application/json

{"method": "bookmarks.search", "args": [{"title": "Research"}]}

Allowlisted methods

Method Args Notes
meta.ping (none) Bridge alive
meta.methods (none) This table
bookmarks.getTree (none) Full tree
bookmarks.getSubTree id
bookmarks.getChildren id
bookmarks.get id or [id, …]
bookmarks.search {title? url? query?} or string
bookmarks.create {parentId?, title, url?} Omit url to create a folder
bookmarks.update id, {title? url?}
bookmarks.move id, {parentId? index?}
bookmarks.remove id Bookmark or empty folder
bookmarks.removeTree id Folder and descendants
bookmarks.getRecent numberOfItems

Do not invent other browser.* names. Adding an API means editing ALLOWED in extension/background.js and ALLOWED_METHODS in host/firefox_agent_bridge_host.py, then reloading the extension.

REST aliases use the same Bearer token. GET /health has no token (loopback liveness only).

Typical folder replace

  1. bookmarks.search {title: "…"} — take the hit without a url. Fail if zero or many.
  2. bookmarks.getChildren on that id.
  3. bookmarks.remove each bookmark; bookmarks.removeTree each child folder.
  4. bookmarks.create each new item with parentId set.

examples/replace_named_folder.py does that (FAB_TOKEN must be set).

Failure modes

Symptom Cause What to do
Connection refused on the listen port Firefox closed, add-on/host missing, or port changed Open Firefox; load add-on; install native host; match FAB_URL to Manage clients
401 Bearer token required Unsigned request Set FAB_TOKEN
401 unknown token Revoked, typo, or never generated Manage clients → generate again
502 method not allowed Typo or API not in the allowlist meta.methods
Temporary add-on gone after restart Unsigned on Firefox Release Load again, or sign via AMO

Security boundaries

  • No runtime.onMessageExternal. Other add-ons cannot call the dispatcher.
  • Native host allowed_extensions is pinned to this add-on's gecko id.
  • HTTP is 127.0.0.1 only; browser Origin headers are rejected.
  • Secrets are ordinary Bearer tokens. Do not write them under %LOCALAPPDATA%\firefox-agent-bridge.
  • Another add-on with the bookmarks permission already has Places.
  • Do not add tabs, history, <all_urls>, or onMessageExternal without a new threat review.

What this project is not

  • Not a bidirectional sync implementation
  • Not a general Firefox remote-control surface
  • Not a reason to keep editing places.sqlite on a live profile