Docs
/
what you can do

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.

WhatCallWhy
what CAN be connected herelist_providersGET /api/connectors/providersthe catalogue is per-deployment and grows every release; each entry says which auth it needs and how many operations it has
what IS connectedlist_connectionsGET /api/connectorsnever assume a connection exists - and every other call takes an id from this one
what one connection can dolist_operationsGET /api/connectors/:id/operationsoperations are declared by the provider, so the list differs per provider and per version
what a provider could do BEFORE connecting itGET /api/connectors/providers/:provider/operationsanswers "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 speaksGET /api/adaptersthe 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 catalogueGET /index/capabilitiesone 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

  1. list_providers (or GET /api/connectors/providers) to find the provider and see which auth it needs.
  2. OAuth providers: the deployment must have an OAuth app registered first — GET /api/connectors/oauth/providers says whether it does. Then POST /api/connectors/oauth/start returns a URL the operator opens in a POPUP.
  3. Credential providers: the operator fills the form. POST /api/connectors/test proves an unsaved config works before saving it.
  4. 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

  1. list_connections for the id.
  2. list_operations for what it exposes, then describe_operation for the one you want — its parameters, what it returns, and which capability it answers.
  3. run_operation (or POST /api/connectors/:id/execute with {operation, args}).
  4. Read ok on the result. Chain several with POST /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

  1. read_rows with introspect first — it returns the tables the database itself reports.
  2. read_rows with read and a table name for the rows.
  3. Headless, that is POST /api/connectors/:id/data/introspect then POST /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

  1. Find the transport: GET /api/adapters, keep the systems whose category is comms. Their names are comms-<transport>.
  2. For a bounded sample — HTTP, SOAP, one publish — POST /api/adapters/comms-<transport>/send connects, captures for a window and returns everything it saw.
  3. For a connection that stays open — MQTT, WebSocket, serial — POST /api/protocols/sessions with {transport, args}, then watch GET /api/protocols/sessions/:id/stream.
  4. POST /api/protocols/sessions/:id/send to write on it; DELETE /api/protocols/sessions/:id to 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

  1. Use a capability id — an INTENT, like current weather or send-an-email — rather than a provider operation.
  2. GET /index/capabilities lists them; a provider manifest says which ones it fulfils.
  3. 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.

TabPathGroupWhat it is for
(index)-Ways outSpeak a raw protocol at an address — HTTP, WebSocket, MQTT, UDP, SOAP, CoAP, Serial and the rest.
socials-Ways outThe accounts the operator has linked, and every operation each one exposes.
socials (index)-Ways outThe accounts the operator has linked, and every operation each one exposes.
socials/connect-Ways outLink an account. The OAuth lane — a popup the operator opens, and a callback that runs before the session gate.
socials/actions-Ways outEvery operation a linked account exposes, with its parameters — the honest answer to "what can I do with this".
databases-Ways outConnected databases: what is in them, and what may be called on them.
databases (index)-Ways outConnected databases: what is in them, and what may be called on them.
databases/connect-Ways outStore the credentials for a database the operator already runs. Secrets are sealed on the way in and masked on every read.
databases/browse-Ways outRead schemas, tables and rows.
databases/actions-Ways outThe six data operations, and which of them this connection permits.
integrations-Ways outVendor APIs behind a key, and media buckets.
integrations (index)-Ways outVendor APIs behind a key, and media buckets.
integrations/connect-Ways outAuthorise a third-party service with a key or a credential form.
integrations/actions-Ways outWhat that service exposes once connected, and the arguments each operation takes.
ssh-Ways outRemote hosts reached over SSH and SFTP — the profiles, and what each one is pinned to.
ssh (index)-Ways outRemote hosts reached over SSH and SFTP — the profiles, and what each one is pinned to.
ssh/connect-Ways outAdd a host by address, user and either a password or a private key.
ssh/files-Ways outBrowse, preview, upload, rename and delete files on the host over SFTP.
ssh/run-Ways outRun one command on the host and read stdout, stderr and the exit code.
ssh/actions-Ways outWhat a connected host exposes as callable operations.
email-Ways outMail 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 outMail 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 outAdd 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 outRead what has arrived on a connected account.
email/compose-Ways outWrite and send one message from a connected account.
email/send-Ways outSend to many recipients from a connected account — the bulk lane, separate from Compose because the blast radius is different.
email/actions-Ways outWhat 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

  1. The card grid is every adapter system whose category is comms — there is no declared list.
  2. 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

  1. introspect for the tables the database reports.
  2. read for 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

  1. A host is an ordinary connection of family ssh — provider ssh or sftp, same fields.
  2. Secrets (the private key) are sealed by @leumas/connectors and never returned in the clear.
  3. The optional root field 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

  1. It is the shared @leumas/ui/files FileExplorer with an fs adapter pointed at the host.
  2. Reads always work. Every WRITE is refused 403 until readOnly is set to boolean false on that connection.
  3. A path outside the root a profile is pinned to is refused 403 with code: 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

  1. Each command opens a connection, runs and hangs up — no shell state carries between commands.
  2. exec counts as a WRITE, so it is refused on a read-only host: nothing can tell cat from rm before it runs.
  3. The server kills a command past its deadline and reports killed: true; ok is 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.

  1. 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.
  2. Being a connector is what gives it sealed credentials, owner scoping and a Test that really logs in.
  3. The email.send capability 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.

FromDoesGo to
protocols/transportsFire 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.
ToolArgsDoes
read_rowsconnectionId op table limitRead 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_connectionsEvery ${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_providersWhat 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_operationsconnectionIdEvery 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_operationconnectionId operationOne 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_connectionconnectionIdProve 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_operationconnectionId operation argsCall 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_tabtabMove 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.

Source .claude/skills/leumas-studio/reference/domains/protocols.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt