Protocols
<!-- GENERATED by ops/tools/skill-sync — do not edit by hand. Run pnpm skills:sync. -->
<!-- GENERATED by ops/tools/skill-sync — do not edit by hand. Run pnpm skills:sync. -->
Protocols — what you can do
/admin/d/protocols · pillar protocols
Connect accounts, databases and services - then call what they expose, or speak a raw protocol.
Fetch this, never remember it
These change per install and per release. Call for them - a list written here would be a future lie.
| What | Call | Why | |
|---|---|---|---|
| what CAN be connected here | list_providers | GET /api/connectors/providers | the catalogue is per-deployment and grows every release; each entry says which auth it needs and how many operations it has |
| what IS connected | list_connections | GET /api/connectors | never assume a connection exists - and every other call takes an id from this one |
| what one connection can do | list_operations | GET /api/connectors/:id/operations | operations are declared by the provider, so the list differs per provider and per version |
| what a provider could do BEFORE connecting it | GET /api/connectors/providers/:provider/operations | answers "is it worth connecting" without credentials; a provider with no catalogue answers 200 with a reason, not an error. route-ok: registered at shared/engines/connectors/src/router.js as GET /providers/:provider/operations; the recorded route table on this branch is behind, which pnpm check:routes reports separately | |
| which raw transports this install speaks | GET /api/adapters | the transport list is declared NOWHERE - it is every adapter system whose category is comms, so dropping a pack on disk adds one | |
| the vendor-neutral capability catalogue | GET /index/capabilities | one id per INTENT; which provider answers depends on what the operator connected |
How to actually do things here
Written by hand, because the order and the traps are the part no file can derive.
Connect one of the user's accounts
list_providers(orGET /api/connectors/providers) to find the provider and see which auth it needs.- OAuth providers: the deployment must have an OAuth app registered first —
GET /api/connectors/oauth/providerssays whether it does. ThenPOST /api/connectors/oauth/startreturns a URL the operator opens in a POPUP. - Credential providers: the operator fills the form.
POST /api/connectors/testproves an unsaved config works before saving it. - Either way the row lands in
GET /api/connectors, read-only, with its secrets masked.
Trap: YOU CANNOT DO THIS FOR THEM. The OAuth popup needs a real click, and a credential must never travel through a chat message. Send the operator to the Connect tab. The callback is deliberately un-gated because the provider's cross-site redirect may not carry the session cookie — the one-shot state is the identity and is burned on use.
Perform an action through a connected account
list_connectionsfor the id.list_operationsfor what it exposes, thendescribe_operationfor the one you want — its parameters, what it returns, and which capability it answers.run_operation(orPOST /api/connectors/:id/executewith{operation, args}).- Read
okon the result. Chain several withPOST /api/connectors/:id/pipeline, which refreshes the token once for the whole run and feeds each response into the next call.
Trap: [critical] Read the ok field, never the HTTP status — a vendor that fails inside a 200 is a successful request and a failed operation. Metering happens BEFORE execution, so a failed call still charges. Anything that posts, sends or deletes is visible to other people the moment it works: confirm it with the operator first.
Read from a connected database
read_rowswithintrospectfirst — it returns the tables the database itself reports.read_rowswithreadand a table name for the rows.- Headless, that is
POST /api/connectors/:id/data/introspectthenPOST /api/connectors/:id/data/read.
Trap: Every write is refused unless the connection was explicitly unlocked, and the page tools will not write even then. Identifiers are quoted AND checked against the schema the database itself reported, so a table missing from the browse list is outside the ALLOWLIST, not outside the database. A no_data_plane refusal on Redis, Firebase or Cloudinary is honest — those connect fine and are not a browsable set of tables.
Speak a raw protocol at an address
- Find the transport:
GET /api/adapters, keep the systems whose category iscomms. Their names arecomms-<transport>. - For a bounded sample — HTTP, SOAP, one publish —
POST /api/adapters/comms-<transport>/sendconnects, captures for a window and returns everything it saw. - For a connection that stays open — MQTT, WebSocket, serial —
POST /api/protocols/sessionswith{transport, args}, then watchGET /api/protocols/sessions/:id/stream. POST /api/protocols/sessions/:id/sendto write on it;DELETE /api/protocols/sessions/:idto close it.
Trap: A refused broker still RESOLVES with ok:false — read the field. Sessions are per-user, capped in number, and close themselves when idle; opening one is metered exactly like running the tool, because a session that streams for an hour should not be cheaper than the sample that costs one call. An empty event list means "listened, nothing arrived", never a failure.
Ask for something without caring which vendor answers
- Use a capability id — an INTENT, like current weather or send-an-email — rather than a provider operation.
GET /index/capabilitieslists them; a provider manifest says which ones it fulfils.- The resolver picks a provider from what the operator actually connected.
Trap: Required outputs are a contract: a mapping that cannot produce them fails at definition time, which is what makes swapping providers safe. Parity gaps are DERIVED, not declared, so a provider cannot overstate what it covers.
Tabs
Every one is a real URL. A tab with its own section below carries a real job.
| Tab | Path | Group | What it is for |
|---|---|---|---|
(index) | - | Ways out | Speak a raw protocol at an address — HTTP, WebSocket, MQTT, UDP, SOAP, CoAP, Serial and the rest. |
socials | - | Ways out | The accounts the operator has linked, and every operation each one exposes. |
socials (index) | - | Ways out | The accounts the operator has linked, and every operation each one exposes. |
socials/connect | - | Ways out | Link an account. The OAuth lane — a popup the operator opens, and a callback that runs before the session gate. |
socials/actions | - | Ways out | Every operation a linked account exposes, with its parameters — the honest answer to "what can I do with this". |
databases | - | Ways out | Connected databases: what is in them, and what may be called on them. |
databases (index) | - | Ways out | Connected databases: what is in them, and what may be called on them. |
databases/connect | - | Ways out | Store the credentials for a database the operator already runs. Secrets are sealed on the way in and masked on every read. |
databases/browse | - | Ways out | Read schemas, tables and rows. |
databases/actions | - | Ways out | The six data operations, and which of them this connection permits. |
integrations | - | Ways out | Vendor APIs behind a key, and media buckets. |
integrations (index) | - | Ways out | Vendor APIs behind a key, and media buckets. |
integrations/connect | - | Ways out | Authorise a third-party service with a key or a credential form. |
integrations/actions | - | Ways out | What that service exposes once connected, and the arguments each operation takes. |
ssh | - | Ways out | Remote hosts reached over SSH and SFTP — the profiles, and what each one is pinned to. |
ssh (index) | - | Ways out | Remote hosts reached over SSH and SFTP — the profiles, and what each one is pinned to. |
ssh/connect | - | Ways out | Add a host by address, user and either a password or a private key. |
ssh/files | - | Ways out | Browse, preview, upload, rename and delete files on the host over SFTP. |
ssh/run | - | Ways out | Run one command on the host and read stdout, stderr and the exit code. |
ssh/actions | - | Ways out | What a connected host exposes as callable operations. |
email | - | Ways out | Mail accounts you can read, send from, and send in bulk from — each an ordinary connector of family mail, so credentials are sealed and owner-scoped like every other connection. |
email (index) | - | Ways out | Mail accounts you can read, send from, and send in bulk from — each an ordinary connector of family mail, so credentials are sealed and owner-scoped like every other connection. |
email/connect | - | Ways out | Add a mail account by its provider preset or by raw server settings. Secrets are sealed on the way in and masked on every read. |
email/inbox | - | Ways out | Read what has arrived on a connected account. |
email/compose | - | Ways out | Write and send one message from a connected account. |
email/send | - | Ways out | Send to many recipients from a connected account — the bulk lane, separate from Compose because the blast radius is different. |
email/actions | - | Ways out | What a connected mail account exposes as callable operations, including email.send. |
Tabs in focus
(index) - Transports
Speak a raw protocol at an address — HTTP, WebSocket, MQTT, UDP, SOAP, CoAP, Serial and the rest.
Read with: GET /api/adapters | GET /api/protocols/sessions
Write with: POST /api/protocols/sessions | POST /api/protocols/sessions/:id/send
- The card grid is every adapter system whose category is
comms— there is no declared list. - Bounded capture and a live session take the SAME connection arguments, so a console needs no second form.
Trap: Streaming transports need the session lane; a bounded capture just samples and returns. Never assume a transport exists — read the catalogue.
socials - Socials
The accounts the operator has linked, and every operation each one exposes.
Read with: list_connections | list_operations | describe_operation
Write with: run_operation | test_connection
Trap: This is /api/connectors with family: "social". It is NOT /social, which is Leumas Social — the in-house network of posts, feeds and friends. Posting to a linked account and posting to Leumas Social are different products.
socials/connect - Socials → Connect
Link an account. The OAuth lane — a popup the operator opens, and a callback that runs before the session gate.
Trap: An agent cannot complete this. The popup needs a real click and the state ticket is one-shot.
socials/actions - Socials → Actions
Every operation a linked account exposes, with its parameters — the honest answer to "what can I do with this".
Read with: list_operations | describe_operation
databases - Databases
Connected databases: what is in them, and what may be called on them.
Read with: list_connections | read_rows
Trap: A connection is read-only unless explicitly unlocked, and even an unlocked one cannot be written through the page tools.
databases/browse - Databases → Browse
Read schemas, tables and rows.
Read with: read_rows
introspectfor the tables the database reports.readfor rows from one of them.
Trap: The schema IS the allowlist — a table it did not report cannot be queried, and that answers as "unknown table" rather than an error about permissions.
integrations - Integrations
Vendor APIs behind a key, and media buckets.
Read with: list_providers | list_connections | list_operations
Write with: run_operation
integrations/actions - Integrations → Actions
What that service exposes once connected, and the arguments each operation takes.
Read with: describe_operation
ssh - SSH
Remote hosts reached over SSH and SFTP — the profiles, and what each one is pinned to.
Read with: GET /api/connectors
Write with: POST /api/connectors | PUT /api/connectors/:id | DELETE /api/connectors/:id
- A host is an ordinary connection of family
ssh— providersshorsftp, same fields. - Secrets (the private key) are sealed by
@leumas/connectorsand never returned in the clear. - The optional
rootfield pins the profile to one directory; every path is resolved against it.
ssh/files - SSH → Files
Browse, preview, upload, rename and delete files on the host over SFTP.
Read with: POST /api/connectors/:id/ssh/list | POST /api/connectors/:id/ssh/stat | POST /api/connectors/:id/ssh/read | POST /api/connectors/:id/ssh/usage | GET /api/connectors/:id/ssh/file
Write with: POST /api/connectors/:id/ssh/write | POST /api/connectors/:id/ssh/mkdir | POST /api/connectors/:id/ssh/rename | POST /api/connectors/:id/ssh/remove
- It is the shared
@leumas/ui/filesFileExplorer with anfsadapter pointed at the host. - Reads always work. Every WRITE is refused 403 until
readOnlyis set to boolean false on that connection. - A path outside the
roota profile is pinned to is refused 403 withcode: path_outside_root, never 404.
ssh/run - SSH → Run
Run one command on the host and read stdout, stderr and the exit code.
Write with: POST /api/connectors/:id/ssh/exec
- Each command opens a connection, runs and hangs up — no shell state carries between commands.
execcounts as a WRITE, so it is refused on a read-only host: nothing can tellcatfromrmbefore it runs.- The server kills a command past its deadline and reports
killed: true;okis the exit code, never "it did not throw".
ssh/actions - SSH → Actions
What a connected host exposes as callable operations.
Read with: describe_operation
email - Email
Mail accounts you can read, send from, and send in bulk from — each an ordinary connector of family mail, so credentials are sealed and owner-scoped like every other connection.
- A mail account is a connection of family
mail; it is not a setting under Accounts, because it is a way OUT of the building. - Being a connector is what gives it sealed credentials, owner scoping and a Test that really logs in.
- The
email.sendcapability puts an operator's own server beside Resend and Brevo, so a workflow written against one runs on whichever this install actually has.
Ways in
Declared cross-links. Every address is matched against the real route table by check:huds.
| From | Does | Go to |
|---|---|---|
protocols/transports | Fire a rule on one | /admin/dynamic/triggers |
Tools an agent can call here
These are live page capabilities: they exist only while that surface is open, and they call the page's own mutators, so a change made through one is a change the operator can see. A tool missing from this list is not callable — reach for the API or an adapter instead.
protocols:… — Protocols → …s
Source: protocols/model/useConnectorCapabilities.js
What the page tells an agent about itself:
This page holds the operator's connected ${noun}s. Call list_connections first; every other tool takes an id from it. OPERATIONS ARE DATA. "Can this connection do X" is answered by list_operations and describe_operation — never by calling something to see what happens. RUNNING AN OPERATION IS REAL. It is metered before it executes, so even a failed call is charged, and anything that posts, sends or deletes is visible to other people the moment it works. Confirm with the operator before running one that writes; read-only operations you may run freely. A vendor that fails inside a 200 still comes back with ok:false. Read the ok field, never the HTTP status. You cannot connect an account. OAuth needs a popup only a real click opens, and credentials must never pass through a chat message. Send the operator to the Connect tab instead. A refusal is an answer. read_only_connection, no_data_plane, credential_missing and unknown_table each mean something specific — report the refusal as given rather than looking for a way around it. Never repeat a credential, a token or any part of one, even masked. The connection id is the only handle you need.
| Tool | Args | Does |
|---|---|---|
read_rows | connectionId op table limit | Read from a connected database: introspect lists the tables the database itself reports, read returns rows from one of them. Writing is not available from here even on an unlocked connection. |
list_connections | — | Every ${noun} connected on this page: id, name, provider, whether it is read-only, and whether its last test passed. Call this FIRST — every other tool takes an id from it. Credentials are never included. |
list_providers | — | What CAN be connected here but is not yet: provider id, name, which auth it needs (oauth or a credential form), and how many operations it exposes. Use it to answer "can I connect X" without guessing. |
list_operations | connectionId | Every operation one connection exposes, with its id and what it does. Read this BEFORE calling anything — "can this connection do X" is answered here, never by trying it. |
describe_operation | connectionId operation | One operation in full: its parameters, what it returns, and which capability it answers. Call this before run_operation so the arguments are right the first time. |
test_connection | connectionId | Prove a connection still answers, and record the result on the row. Safe to run at any time; it calls the provider's own test operation, not a real one. |
run_operation | connectionId operation args | Call ONE operation on a connection. Call describe_operation first so the arguments match. The result is summarised — the page holds the full payload. Anything that writes needs the operator's agreement first. |
open_sub_tab | tab | Move the operator to one of this page's tabs so they can see what you mean: the list, connect (to add one), actions (the operation manifests), or browse (databases only). |
API
Mounted prefixes that serve this domain: /api/protocols
The full route table is leumas-capabilities/reference/api-routes.md.
Traps
Prefixes: /api/connectors for connections and their operations, /api/protocols for live raw sessions, /api/adapters for bounded transport captures. [critical] /social is Leumas Social, the in-house social network. It has nothing to do with the Socials tab, whose connections live under /api/connectors. A connection's provider and family are fixed at creation; only its name, config and read-only flag can change. A secret sent back as the masked placeholder keeps the stored value — that is how an edit form saves without re-typing a key. Never repeat a credential, or any part of one, even masked. The connection id is the only handle anything needs. The integration invoke ref is <connectionId>.<operation> split at the FIRST dot, and caller arguments beat the stored config — the inverse of an action, where config wins.