This site is being rebuilt and some pages are out of date. For current details, write to reach@inorbit.hr. This notice goes away when the rebuild is done.

No analytics unless you allow it, no tracking. This site keeps in your browser the language you pick, the theme, its colour, which site you chose, the currency on the pricing page and that you closed this notice; signing in adds session cookies. The legal page has the details.

Sign in

← Back to Platform

RFC 0044open2026-10-04

Connectors, a catalogue connected from the console and used everywhere

Connections become a product: a catalogue of apps described as data, an account connected from the console with a key or by signing in, one place to see and reconnect them, and every product of the platform acting on them through a person's grant.

Problem

Connections (RFC 0018) work, and they are plumbing. There are two technical kinds, an HTTPS endpoint and a webhook in. A connection is made in a dialog under the developer settings by typing a URL, an auth scheme and a header name. Our cloud calls a host only when it sits inside a domain the account has verified (RFC 0030), which is the right rule for a customer's own API and the wrong one for PagerDuty, Slack or GitHub: nobody can verify api.pagerduty.com. So the apps a team actually runs incidents in cannot be reached at all.

The products that need those apps are queuing up:

  • Reliability (RFC 0031, 0037) knows when a monitor goes down. It cannot open an incident in the team's incident tool or post in the channel where the incident runs.
  • Lab (RFC 0036) wants a team's repository as the source of truth, which means a GitHub App, not a pasted token.
  • Agents can be granted a connection's actions as tools, but there are no actions worth granting yet.

The shape the rest of the industry settled on is clear. Zapier describes an app by how it authenticates, with an API call that tests the account, and by its triggers and actions; a credential that is expired or revoked is fixed by connecting the account again (Zapier platform docs). Nango keeps every provider it supports in one data file, with its auth mode and URLs (providers.yaml). Both treat an integration as data plus a small amount of code for the special cases.

Proposal

One connector framework inside the connections service, with connectors as data.

The catalogue

A connector is a file in the repository, compiled into the service and checked by a test. It says:

  • who it is: an id (which is also the connection's kind), a name, a category (incident, chat, code, observability, enterprise, ai, generic), a description, its API documentation and an icon;
  • how it authenticates: one or more auth modes, each with the fields a person gives (the secret ones are sealed and never answered) and how they go on a request;
  • its settings, the values that are not secret: a Datadog site, a PagerDuty service region, a stack name;
  • its hosts: the only hosts its calls may reach, as templates over the settings (api.{site}, );
  • a test request that proves the credential before anything is stored, and a label read from its answer, so a connection says which workspace or organisation it is;
  • its actions as HTTP templates: method, URL, query, body, the parameters with their types, the output picked from the answer by JSON pointers, the class (read, write_reversible, write_irreversible) and the three flags the rule against the trifecta needs (RFC 0018);
  • later, its triggers: how the provider signs a webhook, and which provider event becomes which trigger.

A test reads every file: it parses with unknown keys refused, every placeholder is a parameter, a setting or a credential field where a credential may go, every URL's host is in the list, and every mode and model named exists. A connector that is available is also a kind, so its actions are granted, checked, offered as tools over the API and MCP, and recorded, exactly as an endpoint's are. Nothing about grants or audit is new.

Auth modes

Mode How Phase
api_key, bot_token fields the person pastes, sent in a header the connector names 1
basic a user name and password as HTTP Basic 1
routing_key a key that goes in the body (PagerDuty's Events API) 1
webhook a URL the provider issued that is itself the secret (Microsoft Teams Workflows) 2
OAuth 2.0 the person signs in at the provider; we keep and refresh the token 2
github_app the account installs our GitHub App; tokens are minted per call 3

The OAuth broker (phase 2). Starting a connection creates a single-use connect session bound to the person, with the state and a PKCE verifier, valid for ten minutes, and answers the provider's authorize URL. The provider sends the person back to a console page, which completes the exchange as the signed-in person, so no open route takes an authorization code. We follow the OAuth 2.0 Security Best Current Practice (RFC 9700): authorization code with PKCE for every client, exact redirect URIs, single-use state. A refresher renews tokens before they expire, one refresh in flight per connection, and stores a rotated refresh token before using it. A refused refresh marks the connection needs_reauth rather than retrying forever. Deleting a connection revokes the token where the provider supports revocation (RFC 7009). Our own OAuth app credentials live only in the cluster's secret store, never in a file or the database.

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

The GitHub App (phase 3). The account installs our App on the repositories it chooses. We keep the installation id, never a token: each call mints an installation token for one repository with only the permissions that action needs, and drops it after the call (GitHub). Lab (RFC 0036) is its first user and needs:

  • read_tree and read_file (read): one repository, a ref, under a path prefix.
  • propose (write, reversible): a branch from the followed branch, one commit with any number of files, then a pull request opened or updated; it answers the pull request's number.
  • status (read): the installation and the repository as the App sees them.
  • push and pull_request events, delivered to the consumer the repository is bound to.
  • Permissions per call: Metadata read, Contents read or write, Pull requests write, and nothing else.

RFC 0041 also reads pull_request events for every repository an account installs the App on: the Implements: line, whether it merged, and the merge commit. Lab and RFC 0041 use all of this as the product consumer product:lab, through a person's grant.

Credentials

A connector's credential is the auth mode's fields, sealed as one map in the vault RFC 0018 built: under the connection's own data key, with associated data of its own, so it never opens as any other secret of the row. It is opened for one call and dropped. It is never answered, logged, put in a URL or in an error. A connection keeps who it is (label), which mode it uses and its health: active, paused, needs_reauth or error. A provider's 401 or 403 makes it needs_reauth, publishes connection.needs_reauth once, and stops its calls until a new credential, or a test, passes.

Reachability

A connector calls only its declared hosts. The rendered URL must be https:// and its host exactly one of the connector's hosts rendered with the connection's settings. A value that lands in a host is a declared choice or one DNS label; a value in a path is one segment; a credential never lands in a URL. Then the rules every cloud call follows: public addresses only, resolved once and pinned, no redirects, bounded in time and size. The verified-domain rule stays for endpoint connections, which point at the customer's own hosts.

Used everywhere: product consumers

A grant's consumer can now be a product: product:reliability, product:lab, product:signals, product:llm. A person grants a product the actions it needs on a connection ("Reliability may open and resolve incidents in PagerDuty"). The product's service calls the action naming the product, and the connections service checks that this service may speak for that product and that a live grant names it. It is the same grant, trifecta check and audit path as for a key or an agent; there is no second mechanism.

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

Triggers and the payload (phase 4)

Each connection gets its own inbound address, verified by the connector's scheme: an HMAC header, Svix, Slack's v0 signature, GitHub's, or Ed25519. The provider's event type maps to one of the connector's triggers, and connection.triggered carries the trigger's name and ids. The payload is customer data. It is kept sealed for 24 hours, for a consumer with a grant to read once, and never logged.

Category models

A product should say "open an incident", not "call PagerDuty's Events API". Actions name the model operation they implement:

  • incidents: incident.create, incident.acknowledge, incident.resolve, incident.add_note, with triggers incident.created, incident.acknowledged, incident.resolved;
  • chat: chat.post.

PagerDuty's Events API maps cleanly (trigger, acknowledge, resolve by dedup key). incident.io has no acknowledge, and resolving is moving an incident to a status of category closed, which needs the organisation's status ids first (incident.io). The model layer that hides this is phase 4, with its first user.

Enterprise and AI connectivity

Phase 2 adds the systems a larger customer runs: Jira and Confluence (signing in with Atlassian, or an API token), ServiceNow (its Table API on the instance's own host), Microsoft Teams (a channel's Workflows webhook, accepted only on the hosts Microsoft documents for it) and Okta (read only).

AI providers, bring your own key. Anthropic, OpenAI, Google Gemini, Mistral and Azure OpenAI connect with the customer's own key, and an OpenAI-compatible endpoint connects a model server the customer runs, on a host inside a domain they verified. The rules:

  • The customer's own agreement with the provider covers what is sent. We do not resell or proxy a model account.
  • Nothing is sent until the customer connects a provider and a person grants an action; the connector is off until then.
  • Only granted actions run. generate sends data out and reads untrusted text (the model's answer), so the rule against the trifecta applies to every grant of it.
  • It is labelled as AI wherever it appears (AI Act Art. 50): the catalogue marks it "AI model", and connecting says that prompts go to the provider under the customer's own agreement.
  • The answer goes back to the caller only. It is never logged or stored, not even sealed for a retry; only the token counts the provider reports are kept, to meter it.

Our compliance programme's control AI-03 reads "customer data reaches only self-hosted models or providers under a DPA". We read a customer's own provider account, under the customer's own agreement and used at the customer's instruction, as within it: we act as the customer's processor and the provider is theirs, not a sub-processor of ours. Models we choose and pay for stay under the control as written.

Assistants that use InOrbit. The catalogue also lists the assistants a team already uses, as clients rather than connections: Claude, ChatGPT, Cursor, GitHub Copilot and Gemini CLI. Each shows how to point it at our MCP server (RFC 0005) at https://api.<domain>/mcp with the person's own API token, read-only scopes suggested. Claude Code, Cursor, Copilot and Gemini CLI send the token as a header today; claude.ai, Claude Desktop and ChatGPT need OAuth on the MCP server, which RFC 0050 builds.

The command line

Everything the console does with connections, an owner or an SRE does with iohr (repo inorbithr/sdk), with --json on every command:

  • iohr connectors list [--category ...] and iohr connectors show <id>: modes, fields, actions, hosts, the AI label.
  • iohr connections list, show, test, history, pause, resume, rename and delete (confirmed unless --yes).
  • iohr connections add <connector>: a key mode prompts for each secret with hidden input, or reads it from a file or stdin, never from the command line. A mode that signs in calls StartConnect, opens the browser at the authorize URL, and waits on GetConnectSession while the person finishes on the console's callback page. reconnect does the same for an existing connection.
  • iohr connections grant <name> --to product:reliability|key:<id>|agent:<name> --actions a,b [--expires 90d], grants and revoke-grant.

The generated SDKs (RFC 0020) take the new routes from the OpenAPI document.

The console product

Connections becomes a top-level product (admin-only first, like Lab and Reliability):

  • Catalogue: a gallery with search and categories; each connector's auth modes, scopes, actions and triggers; coming-soon connectors listed.
  • Connected: every connection with its health, label, owner and the products using it.
  • A connection's page: overview, the products and grants using it, activity, test, reconnect, rename and disconnect.
  • The connect form, generated from the auth mode's fields and the settings.
  • Activity: uses across connections.

Alternatives considered

Embed Nango. Its data model is close to what we want and we borrow it. Its licence is the Elastic License 2.0 (licence), which restricts offering it as a hosted service. Its free self-hosted edition is "a limited free self-hosting option ... for hobby projects ... that need Auth and Proxy"; syncs, webhooks, triggers and tool calls need its enterprise edition or its cloud (self-hosting). It would also be one more stateful service with its own database and its own credential store beside our vault. We already run sealed credentials, grants and audit; the auth broker is the part we lack, and it is small.

A hosted integration platform. Pipedream Connect offers more than 3,000 integrations and keeps the tokens on its side (Pipedream); Composio manages the auth by default, on its own API (Composio). Either makes a third party hold our customers' tokens and see their data: a sub-processor in every customer's records and an exit plan for every bank among them (RFC 0018 rejected this for the foundation too).

A unified API. Unified APIs offer one common model per category. Merge's ticketing model is tickets, comments, accounts, contacts, collections, tags, teams and users (Merge). Incidents are not a ticket with a different name: paging, acknowledging by dedup key, severities and status categories do not fit, and we did not find these vendors covering PagerDuty or incident.io as an incident model. We keep a small model of our own over native actions instead, so a product uses the common verb and a person can still grant the native one.

Code per connector. It is how finance's connectors are written today, and it is the right escape hatch for the special cases (the GitHub App's JWT and installation tokens). For the common case, a request template with a host list is less code to review and is checked by one test.

Decision

Open. The direction proposed, in five phases, each its own set of pull requests and a line in the status log:

  1. The framework and key-based connectors. The catalogue and its test, the generic HTTP engine with the host allow-list, structured credentials, product consumers, needs_reauth, the catalogue API, the console product's catalogue and connect form. Connectors: incident.io, PagerDuty (API key and Events routing key), Datadog, Cloud, Discord. Slack, Linear, GitHub and PagerDuty's OAuth listed as coming soon.
  2. The OAuth broker, with Slack, PagerDuty's Scoped OAuth and Linear (rotating refresh tokens); the enterprise connectors, the AI providers (bring your own key) and the assistants that use InOrbit.
  3. The GitHub App, built with Lab as its first user (RFC 0036, 0041).
  4. Triggers, the incident and chat models, and the first use: Reliability routes monitor.down, agent.check.down and agent.guard.open to an incident connection and a chat channel, and resolves on recovery. Our own monitors page us.
  5. Later: an MCP server as a connector, finance's mailbox and banks onto the one vault, approvals for irreversible writes, polling triggers, more connectors.

Publication

The developer docs gain "Connect an app": the catalogue, each connector's auth modes, settings, actions and hosts, and how a product is granted actions. The API reference gains the catalogue routes. The sub-processor list gains a provider when the first customer connects it through us.

Status log

  • 2026-10-04: opened; phase 1 built. Connectors are files compiled into the connections service and checked by a test. Five are available: incident.io (open, update and annotate incidents), PagerDuty (services over the REST API with an API key; trigger, acknowledge and resolve over the Events API with a routing key), Datadog (events and monitors, on any of its nine sites), Cloud (annotations and dashboards) and Discord (a bot's messages). Slack, Linear, GitHub and PagerDuty's OAuth are listed as coming soon.
    • Connecting. A connection is made from a connector with its auth mode's fields and settings. The connector's test runs first: a refused key stores nothing, and a passing one names the account.
    • The engine. Every call goes only to the connector's own hosts, rendered from the connection's settings, under the cloud's address, pinning and size rules. A provider's 401 or 403 marks the connection needs_reauth and publishes connection.needs_reauth; a new key, tested first, brings it back.
    • Product consumers. A person can grant actions to product:reliability, product:lab, product:signals or product:llm. A service may call only for the products it is configured to speak for; only the model service has one so far.
    • The API. GET /v1/connectors and GET /v1/connectors/{connector_id} for the catalogue, and the account's history of uses across its connections, filtered by connection, action, outcome and consumer, all under connections:read. A reconnect tests the new key before it replaces the old one, and may switch the auth mode. A connection says who made it and which products use it; a test that fails for another reason than the key shows it as error.
    • Not yet. The console product is being built alongside, against the same contract. OAuth, the GitHub App, triggers and the incident model come in phases 2 to 4.
  • 2026-10-04: phase 2 built.
    • Signing in. OAuth 2.0 with PKCE (S256) on every sign-in: a single-use session bound to the person, its state kept as a hash and its verifier sealed, ten minutes. The console's callback page completes it as the same person; the command line waits on the session. Tokens are sealed in the vault; a connection keeps the granted scopes, the token's expiry and the provider's id for the workspace, which is unique per account, so the same workspace is never connected twice.
    • Refreshing. One refresh in flight per connection (a row lock skipped by other refreshers), the rotated refresh token stored before the lock is released, a refused refresh marks the connection needs_reauth, other failures back off. A call that finds its token expired renews it first under the same lock. Deleting a connection revokes its token where the provider has a revocation endpoint.
    • Our OAuth apps live only in the cluster's secret store, set by an operator with the client secret on stdin; a connector without its app shows needs_app.
    • Connectors. Slack, PagerDuty (Scoped OAuth), Linear, Jira and Confluence (OAuth or an API token), ServiceNow, Microsoft Teams, Okta; Anthropic, OpenAI, Google Gemini, Mistral, Azure OpenAI and an OpenAI-compatible endpoint (bring your own key; answers never kept, token counts metered); Claude, Cursor, GitHub Copilot and Gemini CLI as clients of our MCP server, ChatGPT after RFC 0050. Amazon Bedrock is listed as coming soon (it needs SigV4 signing).
    • Not yet. Choosing among several Atlassian sites after consent, ServiceNow OAuth, AI calls through the account's agent, and the console and command line, which are built alongside against the same contract.
  • 2026-10-07: Checked: phases 1 and 2 (#226, #222, #246, #240) run in connections at dab02d1a and console-ui at 3a40912a. Phases 3 to 5 are not verified as built; the GitHub App exists (RFC 0077). Open.

← Back to Platform