Skip to content
CompozyOS RuntimeBridges

Bridges Overview

How Compozy bridge instances connect workspace agents to external messaging platforms.

Audience
Operators running durable agent work
Focus
Bridges guidance shaped for scanability, day-two clarity, and operator context.

A bridge is a workspace-facing adapter between Compozy sessions and an external platform. The Compozy source repository contains in-tree providers for Slack, Discord, Telegram, WhatsApp, Microsoft Teams, Google Chat, GitHub, and Linear. They are not embedded in the released compozy binary: build and install the provider from a trusted source checkout before configuring its bridge instances. The bridge instance stores the operator-owned configuration, while the provider extension owns webhook validation, message normalization, and outbound delivery.

That split keeps Compozy responsible for sessions, routing, persistence, and delivery bookkeeping while letting each platform adapter speak the platform API directly.

In this section

Choose a provider

ProviderBest fitInbound boundaryOutbound behaviorProgress default
SlackWorkspace chat with channels and reply threadsSigned Events API, commands, and interactionsCreate, edit, delete; Slack mrkdwnnew
TelegramDirect chat, groups, and forum topicsBot API webhook secret tokenCreate, edit, delete; MarkdownV2 fallbacknew
DiscordServer channels, threads, interactions, and componentsEd25519-signed HTTP webhooksCreate, edit, delete; 2,000-code-point chunksnew
WhatsAppBusiness-number direct messaging through Cloud APIMeta verification challenge and signed POSTCreate only; 4,096-code-point chunksoff
Microsoft TeamsBot Framework personal, group, and channel conversationBot Framework bearer JWT and activity service URLCreate, edit, delete; 28,000-code-point chunksoff
Google ChatWorkspace spaces through direct or Pub/Sub ingressGoogle direct JWT or Pub/Sub OIDC JWTCreate, edit, delete; 32,000 UTF-8 bytesoff
GitHubRepository issue and pull-request comment threadsRepository/App webhook HMACIssue/review comment create, edit, deleteoff
LinearIssue comments or Linear Agent SessionsLinear webhook HMACComments editable; Agent Activities appendoff

Start with Bridge setup, which links to one dedicated guide per provider. A provider is complete only after configuration, runtime, public transport, inbound route, and real outbound-delivery proofs all pass.

Runtime model

ObjectOwned byWhat it does
Provider extensionExtension catalogDeclares bridge.adapter, platform name, display name, required secret slots, and provider config schema hints.
Bridge instanceCompozy bridge registryStores scope, workspace, provider selection, routing policy, delivery defaults, provider config, status, and degradation metadata.
Secret bindingCompozy daemonMaps a provider slot such as bot_token to a vault-backed secret reference and stores the write-only secret value encrypted.
Bridge routeCompozy routing layerMaps one canonical platform identity to one Compozy session and agent name.
Target directoryCompozy daemonStores provider-discovered immutable target identities and resolver indexes for operator-friendly names.
Delivery targetDelivery brokerIdentifies where a response should be posted, replied to, edited, or deleted on the platform.

The provider catalog is dynamic. GET /api/bridges/providers returns installed extensions that provide bridge.adapter, including disabled providers. Inspect each descriptor's enabled, state, and health before creating an instance. The source repository contains provider manifests for Slack, Discord, Telegram, WhatsApp, Microsoft Teams, Google Chat, GitHub, and Linear. The setup section covers every in-tree provider and calls out mode-dependent credentials explicitly.

Message flow

Rendering diagram…

An inbound platform event is normalized by the adapter, routed to a Compozy session, and streamed back through the delivery broker to the same platform.

Adapter architecture

The bridge adapter runs as an extension subprocess. During initialization, Compozy gives it the bridge instances assigned to that extension and a Host API surface. The adapter then:

  1. Resolves bridge secret bindings from the daemon-provided cache.
  2. Starts webhook listeners for configured bridge instances.
  3. Verifies incoming platform requests before ingestion.
  4. Converts platform payloads into normalized bridge envelopes.
  5. Reports bridge state back to Compozy as ready, degraded, auth_required, or error.
  6. Delivers response events back to the platform API and acknowledges delivery to Compozy.

Compozy does not parse provider webhook payloads in the daemon. The daemon accepts the normalized envelope only after the provider extension authenticates it and confirms that the bridge instance is enabled for that extension.

Bridge instance format

Bridge instances are persisted runtime records. They are created through the bridge API or the compozy bridge create command, not through config.toml.

{
  "scope": "workspace",
  "workspace_id": "ws_8f33a913d23c4fd1",
  "platform": "slack",
  "extension_name": "slack",
  "display_name": "Slack support",
  "enabled": false,
  "status": "disabled",
  "dm_policy": "allowlist",
  "routing_policy": {
    "include_peer": false,
    "include_thread": false,
    "include_group": true
  },
  "provider_config": {
    "webhook": {
      "public_url": "https://bridge.example.com/slack/support",
      "listen_addr": "127.0.0.1:18081",
      "path": "/slack/support"
    },
    "dm": {
      "allow_user_ids": ["U0123456789"]
    }
  },
  "delivery_defaults": {
    "group_id": "C0123456789",
    "mode": "direct-send"
  }
}
FieldRequiredNotes
scopeyesworkspace or global. Use workspace for inbound sessions.
workspace_idworkspace onlyRequired when scope is workspace; forbidden for global.
platformyesPlatform identifier such as slack, discord, or telegram.
extension_nameyesInstalled extension that owns the provider, usually the same as the platform.
display_nameyesOperator-facing name shown by CLI/API output.
enabledyesDisabled instances do not accept routing work. Create disabled, bind secrets, then enable.
statusyesdisabled, starting, ready, degraded, auth_required, or error.
dm_policynoDirect messages only: open, allowlist, or pairing; empty normalizes to permissive open. See access control.
routing_policyyesChooses which platform identity dimensions join the canonical route key.
provider_confignoProvider-owned JSON object. The transport accepts any JSON object or null; each adapter validates the keys it uses.
delivery_defaultsnoJSON object with route defaults plus an optional typed progress block. thread_id requires peer_id or group_id.

Package-managed instances can be created by extension bundles. Those records report source: "package" and reject direct spec mutation through the generic update API.

Supported provider slots

ProviderRequired secretsOptional secretsWebhook auth
Slackbot_token, signing_secretnoneSlack request signing secret.
Discordbot_token, public_keynoneDiscord Ed25519 interaction signature headers.
Telegrambot_tokenwebhook_secretTelegram X-Telegram-Bot-Api-Secret-Token header when configured.
WhatsAppaccess_token, app_secret, verify_tokennoneMeta X-Hub-Signature-256 plus the GET verification challenge.
Microsoft Teamsapp_id, app_passwordapp_tenant_idBot Framework bearer JWT validated against the activity serviceUrl.
Google Chatcredentials_jsonproject_numberGoogle direct-webhook JWT, Pub/Sub OIDC token, or both.
GitHubwebhook_secrettoken, app_id, private_keyGitHub X-Hub-Signature-256.
Linearwebhook_secretapi_key, client_id, client_secretLinear linear-signature HMAC.

Bind each slot to a Compozy-managed vault secret before enabling the bridge. The API accepts the plaintext only through the write-only secret_value field and never returns it:

BRIDGE_ID=brg_123

printf '%s' "$SLACK_BOT_TOKEN" | compozy bridge secret-bindings put "$BRIDGE_ID" bot_token \
  --secret-ref "vault:bridges/$BRIDGE_ID/bot_token" \
  --kind token \
  --secret-value-stdin

The reference must be scoped to the edited bridge: vault:bridges/<bridge>/<slot>. Restart or enable the bridge after changing a binding so the provider receives fresh launch material.

Bridge Vault refs are visible as redacted metadata through the Vault page, compozy vault list --namespace bridges, and /api/vault/secrets?namespace=bridges. The plaintext secret_value is accepted only on write.

Operational states

StatusMeaningIngest behavior
disabledOperator disabled the instance.Rejected as unavailable.
startingExtension is launching, restarting, or reconnecting.Rejected until the provider reports readiness.
readyProvider is healthy and can ingest and deliver.Accepted.
degradedProvider can still work but reported a known issue.Accepted; health payload includes the degradation reason.
auth_requiredRequired credential or platform authorization is missing or invalid.Rejected until fixed and restarted.
errorProvider reached a terminal or repeated fault.Rejected until restarted or repaired.

Bridge health is exposed with each bridge payload and through the bridge health stream. It includes route count, delivery backlog, dropped delivery count, delivery failures, auth failures, last success, last error, and degradation metadata.

Target directory

Bridge adapters can report platform targets such as channels, rooms, users, groups, and threads. Adapters that do not implement native target enumeration use Compozy's fallback snapshot from the bridge instance's operator-declared delivery defaults. The daemon validates snapshots, writes bridge_target_directory, and records the latest successful refresh time. The directory refreshes when an extension runtime connects and then every five minutes while that runtime remains connected.

The in-tree provider walkthroughs use operator-declared delivery defaults for target snapshots. Native platform discovery such as Slack conversations.list is not part of the v1 target-directory contract.

Each target stores the provider-derived immutable route, display name, normalized lookup key, optional qualifier, type, capabilities, updated_at, and last_seen_at. Missing targets are not deleted during refresh; their last_seen_at remains the last time the provider saw them so operators can distinguish stale but known targets from new unknown names.

Use the target APIs before wiring notification presets or explicit outbound targets:

compozy bridge targets brg_123 --query launch -o json
compozy bridge resolve brg_123 "launch room" -o json

The HTTP/UDS equivalents are GET /api/bridges/:id/targets and POST /api/bridges/:id/resolve. Resolve returns a structured diagnostic for ambiguous or unknown names instead of selecting a candidate silently.

Notification suppression

Each bridge instance has a live notification_suppress flag. When it is true, the common bridge notification delivery path suppresses notification deliveries for that bridge and returns a deterministic suppression result to the caller. Notification presets treat suppression as a terminal skip and advance their cursor, so a muted bridge does not replay the same preset event forever. Task bridge subscriptions and direct notifiers use the same common check.

Use suppression for planned bridge maintenance or noisy downstream systems. It is not a replacement for disabling the bridge: disabled or unhealthy bridges reject delivery as unavailable and leave cursors unadvanced for retry, while suppression intentionally records that the event was skipped for that bridge.

On this page