← Back to Plugins
Channels

Symphony

ShunsukeTamura06 By ShunsukeTamura06 👁 31 views ▲ 0 votes

OpenClaw channel plugin for Symphony Messaging (REST API + Datafeed v2, RSA-JWT bot auth)

GitHub

Install

npm install
pnpm

Configuration Example

channels:
  symphony:
    podUrl: https://acme.symphony.com
    agentUrl: https://acme-agent.symphony.com
    relayUrl: https://acme.symphony.com   # optional; defaults to podUrl
    username: openclaw-bot
    privateKeyPath: /etc/openclaw/secrets/symphony-bot.pem
    enabled: true

README

# @openclaw/symphony

OpenClaw channel plugin for [Symphony](https://symphony.com/) Messaging.

Lets an OpenClaw on-prem deployment send and receive messages on Symphony using
the [Symphony REST API](https://rest-api.symphony.com/) and **Datafeed v5** for
inbound events. Authentication uses the standard **RSA-JWT bot
(service-account) flow** โ€” no browser, no OAuth.

## Status

**v0.1.0 โ€” initial cut.** Core paths covered: 1:1 IM and room/stream messaging,
attachments, mentions, emojis, Datafeed v5 long-poll. See
[Limitations](#limitations) for what is intentionally out of scope.

## Features

| Capability | Notes |
| --- | --- |
| Send to 1:1 IM | `client.createIm({ userIds })` then `sendMessage({ streamId, messageMl })` |
| Send to rooms / streams | Same `sendMessage` with the room's stream ID |
| Attachments | Multipart upload via `attachments: [{ filename, content, contentType }]` |
| Mentions | `<mention uid="โ€ฆ"/>` and `<mention email="โ€ฆ"/>` produced via `plainToMessageMl({ mentions })` |
| Emojis | `<emoji shortcode="โ€ฆ"/>` |
| Receive (Datafeed v5) | Long-poll loop, auto-recreates feed on 400/404 |
| Inbound โ†’ AI dispatch | Wired via `openclaw/plugin-sdk/inbound-reply-dispatch` (`runInboundReplyTurn`); resolves agent route, records session, dispatches to OpenClaw's AI reply pipeline, delivers reply back to Symphony |
| Token auth | RS512 JWT โ†’ sessionToken + keyManagerToken; transparent refresh on 401 |

## Install

This plugin targets OpenClaw `>=2026.5.6`. Install it into your OpenClaw
deployment as you would any other channel plugin:

```bash
pnpm add @openclaw/symphony
# or, with the OpenClaw CLI
openclaw plugin install @openclaw/symphony
```

> **Note:** the package is currently consumable directly from this repository.
> Publishing to npm is not part of v0.1.0.

## Configuration

Configure under `channels.symphony` in your OpenClaw config (single account):

```yaml
channels:
  symphony:
    podUrl: https://acme.symphony.com
    agentUrl: https://acme-agent.symphony.com
    relayUrl: https://acme.symphony.com   # optional; defaults to podUrl
    username: openclaw-bot
    privateKeyPath: /etc/openclaw/secrets/symphony-bot.pem
    enabled: true
```

Or with multiple accounts:

```yaml
channels:
  symphony:
    defaultAccount: prod
    accounts:
      prod:
        podUrl: https://acme.symphony.com
        agentUrl: https://acme-agent.symphony.com
        username: openclaw-bot
        privateKeyPath: /etc/openclaw/secrets/symphony-bot.pem
      qa:
        podUrl: https://qa.symphony.com
        agentUrl: https://qa-agent.symphony.com
        username: openclaw-qa-bot
        privateKeyPath: /etc/openclaw/secrets/qa-bot.pem
```

### Required Symphony admin steps (one-time)

1. In the Symphony **Admin Portal**, create a **service account** for the bot
   and capture the `username`.
2. Generate an RSA key pair (recommended: 4096-bit, PKCS#8):
   ```bash
   openssl genpkey -algorithm RSA -out symphony-bot.pem -pkeyopt rsa_keygen_bits:4096
   openssl rsa -in symphony-bot.pem -pubout -out symphony-bot.pub
   ```
3. Upload the **public** key to the service account's profile in the Admin
   Portal. Keep the **private** key on the OpenClaw host and reference it via
   `privateKeyPath`.
4. Make sure the bot has any required permissions (e.g. "Create IM", "Send
   Message to Stream") for the streams you want it to use.

## Local smoke tests

You can sanity-check the plugin without an OpenClaw host by talking directly to
your Symphony pod with the included scripts:

```bash
cp .env.example .env
# edit .env with real values
pnpm install
pnpm smoke:send "hello from openclaw"
pnpm smoke:datafeed   # listens for 30s, prints incoming messages
```

## Programmatic use

The Symphony client is exported separately and can be used standalone:

```ts
import { SymphonyClient, plainToMessageMl } from "@openclaw/symphony/api";
import { readFileSync } from "node:fs";

const client = new SymphonyClient({
  env: {
    podUrl: "https://acme.symphony.com",
    agentUrl: "https://acme-agent.symphony.com",
  },
  credentials: {
    username: "openclaw-bot",
    privateKeyPem: readFileSync("./symphony-bot.pem", "utf8"),
  },
});

await client.sendMessage({
  streamId: "<stream-id>",
  messageMl: plainToMessageMl({
    text: "Hello",
    mentions: [{ kind: "email", email: "[email protected]" }],
    emojis: ["wave"],
  }),
});
```

## Architecture

```
index.ts                              defineChannelPluginEntry
โ”œโ”€โ”€ src/plugin.ts                     createChatChannelPlugin (id, meta, adapters)
โ”œโ”€โ”€ src/setup.ts                      setup adapter + setup wizard
โ”œโ”€โ”€ src/outbound.ts                   outbound + message adapter (target parse/format, send)
โ”œโ”€โ”€ src/gateway.ts                    gateway adapter (datafeed loop owner)
โ”œโ”€โ”€ src/status.ts                     status adapter (sessioninfo probe)
โ”œโ”€โ”€ src/config.ts / config-schema.ts  account resolution + zod schema
โ”œโ”€โ”€ src/messageml.ts                  MessageML <-> plain text converters
โ”œโ”€โ”€ src/normalize.ts                  Symphony events -> OpenClaw inbound shape
โ”œโ”€โ”€ src/runtime.ts                    runtime singleton + client cache
โ””โ”€โ”€ src/symphony/                     OpenClaw-independent Symphony API library
    โ”œโ”€โ”€ auth.ts                       RS512 JWT, dual-token authenticate
    โ”œโ”€โ”€ http.ts                       fetch wrapper (auth headers, retry, refresh)
    โ”œโ”€โ”€ client.ts                     SymphonyClient class
    โ”œโ”€โ”€ messages.ts                   send/get message + attachment endpoints
    โ”œโ”€โ”€ streams.ts                    IM/room creation, stream info
    โ”œโ”€โ”€ users.ts                      user lookup, sessioninfo
    โ”œโ”€โ”€ datafeed.ts                   v5 datafeed CRUD endpoints
    โ”œโ”€โ”€ datafeed-loop.ts              long-poll loop with recovery
    โ””โ”€โ”€ types.ts                      Symphony API types
```

## Limitations (v0.1.0)

- **No streaming token-delta replies.** OpenClaw's preview/block streaming
  protocol is honored at the outbound layer (one final message per send), as is
  conventional for chat platforms without typing-level token streams.
- **No room creation UI** โ€” rooms must already exist; the plugin can post to a
  stream/room ID you supply.
- **Pairing / DM allowlist policy** is wired up but not exhaustively tested โ€”
  uses the SDK's standard `createPairingPrefixStripper` and `chatTypes`.
- **Element forms / actions** (Symphony interactive elements with
  `SYMPHONY_ELEMENTS_ACTION` events) are **not** parsed yet. The datafeed loop
  receives them and discards them.

## Testing

```bash
pnpm install
pnpm test          # unit tests for client / auth / messageml / datafeed loop
pnpm typecheck     # TypeScript check
```

The test suite uses fake `fetch` implementations and does not require Symphony
network access. **End-to-end testing against a real Symphony pod is manual**
via the smoke scripts above (Symphony does not offer a free public sandbox).

## License

MIT โ€” see [LICENSE](./LICENSE).
channels

Comments

Sign in to leave a comment

Loading comments...