| 1 | # π€ The integrations registry |
| 2 | |
| 3 | One JSON manifest per third-party service. A manifest is the WHOLE of what an |
| 4 | integration can do on an Eleven Messenger instance: how a member authenticates |
| 5 | to the service, the one API host its operations may touch, and the closed list |
| 6 | of operations β each marked `read` or `write`, writes each carrying the |
| 7 | confirmation line a person taps before it runs. An operation that isn't in the |
| 8 | manifest cannot be expressed, the same fail-closed palette rule as bot recipes |
| 9 | (`docs/bot-recipes.md`). |
| 10 | |
| 11 | This directory is destined to become its own public repository |
| 12 | (`elevenmessenger/integrations`) so integrations can be added β and audited β |
| 13 | without touching the messenger codebase: adding one is a PR with a manifest |
| 14 | (plus, for OAuth services, registering the OAuth app on the connect broker, |
| 15 | which is where client secrets live β `docs/connected-services.md`). Until that |
| 16 | split, it lives here and is embedded into the binaries (`registry.go`), with |
| 17 | the broker serving it at `GET /registry` so web clients read the same catalog. |
| 18 | |
| 19 | ## Manifest schema (v1) |
| 20 | |
| 21 | ```jsonc |
| 22 | { |
| 23 | "schema": 1, |
| 24 | "id": "github", // lowercase slug, unique |
| 25 | "name": "GitHub", |
| 26 | "category": "development", // catalog grouping |
| 27 | "auth": { |
| 28 | "kind": "pat", // "pat" | "oauth" | "both" |
| 29 | "base": "https://api.github.com",// the ONLY host ops may reach (executor-pinned) |
| 30 | "authorization": "Bearer", // Authorization: <scheme> <token>; "none" sends the bare token (Linear) |
| 31 | "headers": { "Accept": "β¦" }, // extra headers on every call |
| 32 | "pat_help": "β¦", // where a member mints a scoped token |
| 33 | "oauth": { "authorize_url": "β¦", "scopes": ["β¦"] } // when the broker has an app |
| 34 | }, |
| 35 | "ops": [ |
| 36 | { |
| 37 | "id": "repos.list", // namespace.verb, unique within the manifest |
| 38 | "label": "List your repositories", |
| 39 | "access": "read", // "read" | "write" β grants are scoped to this |
| 40 | "method": "GET", |
| 41 | "path": "/user/repos?per_page=20", // {arg} placeholders allowed |
| 42 | "args": { "owner": { "required": true } }, // string args only; validated |
| 43 | "body_json": { "title": "{title}" }, // JSON body template ({arg} in leaves) |
| 44 | "encoding": "json", // "json" (default) | "form" (Stripe-style) |
| 45 | "confirm": "Open β{title}β?", // REQUIRED on writes: the consent chip's line |
| 46 | "pick": { // response projection β what may flow BACK |
| 47 | "root": "data", // descend into an envelope first (optional) |
| 48 | "fields": ["full_name", "user.login"] // keep only these dot-paths per object |
| 49 | } |
| 50 | } |
| 51 | ] |
| 52 | } |
| 53 | ``` |
| 54 | |
| 55 | `pick` makes the response as reviewable as the request: provider APIs answer |
| 56 | with far more than an app needs (a GitHub repo object is ~100 fields, several |
| 57 | KB), and an op result travels inside one end-to-end-encrypted message with a |
| 58 | hard size cap β so the manifest declares the fields that may flow back, and the |
| 59 | executor drops everything else before the result ever leaves the sidecar. |
| 60 | Applied to the object at `root` (or to each object of the array there); nested |
| 61 | paths come back flattened (`"user.login": "octocat"`). An op without `pick` |
| 62 | returns the raw body, still subject to the size cap. |
| 63 | |
| 64 | Rules the loader enforces (fail closed β a bad manifest never half-loads): |
| 65 | |
| 66 | - `id`/op ids well-formed; op ids unique; `access` β read/write; every write |
| 67 | has a `confirm` template; `base` is https; methods β GET/POST/PUT/PATCH/DELETE. |
| 68 | - `{arg}` placeholders in `path`/`body_json`/`confirm` must name declared args. |
| 69 | - Arg values are strings, bounded, and escaped into the URL β a value can |
| 70 | never break out of its path segment or query slot. |
| 71 | - `pick` (when present) names at least a root or one field, all well-formed |
| 72 | dot-paths. |
| 73 | |
| 74 | ## Trust model |
| 75 | |
| 76 | The manifest is reviewed CODE (this registry), never user content. The |
| 77 | executor pins every request to `auth.base`'s host, so a manifest op can't be |
| 78 | steered elsewhere. Member credentials stay client-sealed exactly as before |
| 79 | (`docs/connected-services.md`): a bot receives a short-lived grant sealed to |
| 80 | its own key, now carrying `access: "read" | "write"` β a read grant cannot run |
| 81 | a write op, and grants are per-bot by construction (sealed to one keypair). |
| 82 | Writes additionally require the member's tap on a confirmation chip naming the |
| 83 | exact operation ("Register blip.com β this costs real money"). |
| 84 | |