112 lines
4.3 KiB
Markdown
112 lines
4.3 KiB
Markdown
# 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. If `/health` works but calls 401, you do not have a current `FAB_TOKEN`.
|
|
|
|
## 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 (e.g. `cursor-agent`) and click **Generate secret**.
|
|
3. Copy the `fab_…` value **once**. Store it however you already store secrets: env var, Vaultwarden, `.env`, CI secret, Cursor env.
|
|
4. Give tooling only that string:
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
python tools/client.py METHOD [ARGS_JSON]
|
|
```
|
|
|
|
`ARGS_JSON` is a JSON **array** matching the WebExtension function arguments.
|
|
|
|
```text
|
|
python tools/client.py bookmarks.search "[{\"title\":\"10.132.x.x\"}]"
|
|
```
|
|
|
|
HTTP:
|
|
|
|
```http
|
|
POST /v1/call
|
|
Authorization: Bearer fab_…
|
|
Content-Type: application/json
|
|
|
|
{"method": "bookmarks.search", "args": [{"title": "10.132.x.x"}]}
|
|
```
|
|
|
|
## Allowlisted methods (v0.2)
|
|
|
|
| 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 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 `:17634` | Firefox closed or add-on/host missing | Open Firefox; load add-on; install native host |
|
|
| 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 `ALLOWED` | `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
|