# Integrate Sui Stack Messaging

> Use when a developer wants to add Sui Stack Messaging to their own dapp / project — installing the @mysten/sui-stack-messaging npm package, picking the createSuiStackMessagingClient factory vs manual $extend chain, wiring up session keys, and the minimum config to send one message. Trigger phrases - "add messaging to my app", "install sui-stack-messaging", "integrate the SDK", "how do I use sui-stack-messaging from React/Next/Node", "minimum setup to send a message", "@mysten/sui-stack-messaging quickstart", "wire up the messaging client".

- Skill: `mystenlabs/integrate-sui-stack-messaging` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mystenlabs/integrate-sui-stack-messaging`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mystenlabs/integrate-sui-stack-messaging/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: MystenLabs (https://skillmd.com/u/mystenlabs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mystenlabs/integrate-sui-stack-messaging

---


# Integrate Sui Stack Messaging into your dapp

You are consuming the canonical npm SDK in your own application. You are **not** modifying anything in this repo. (If you want to fork the relayer or indexer, use [`develop-relayer`](../develop-relayer/SKILL.md) / [`develop-walrus-indexer`](../develop-walrus-indexer/SKILL.md) instead.)

## Hard prerequisite — flag this first

**Message-related operations require a running relayer.** `sendMessage`, attachment upload, fetching messages, etc. all go through the relayer's HTTP wire protocol. Without one configured and reachable, those calls will fail.

Two ways to satisfy this prerequisite:

1. **For local dev:** run the reference Rust relayer locally → see [`spin-up-relayer`](../spin-up-relayer/SKILL.md). Point your client at `http://localhost:3000`.
2. **For production:** point at a deployed relayer (your own deployment of the reference, a forked Rust relayer, or a custom `RelayerTransport` you implemented) → see [`configure-custom-relayer-transport`](../configure-custom-relayer-transport/SKILL.md).

There is no zero-server path. Even the simplest example below assumes a relayer URL is reachable.

## Install

```bash
pnpm add @mysten/sui-stack-messaging @mysten/sui-groups
# Peer deps (most Sui dapps already have these):
pnpm add @mysten/seal @mysten/sui @mysten/bcs
```

Peer dep minimums (verify against [`ts-sdks/packages/sui-stack-messaging/package.json`](../../../ts-sdks/packages/sui-stack-messaging/package.json) — source is truth, `Installation.md` may lag): `@mysten/seal ^1.1.1`, `@mysten/sui ^2.6.0`, `@mysten/bcs ^2.0.2`. Node ≥ 22, pnpm ≥ 10.17.0.

## Pick a setup path

Two equivalent paths. Default to the factory unless you specifically need per-extension control.

### Path A (recommended) — `createSuiStackMessagingClient` factory

One call handles `suiGroups` + `seal` + `suiStackMessaging` extension chaining for you. The exact exported name is `createSuiStackMessagingClient` ([`ts-sdks/packages/sui-stack-messaging/src/factory.ts`](../../../ts-sdks/packages/sui-stack-messaging/src/factory.ts)). Some docs may show an older name (`createMessagingGroupsClient`) — that's stale; trust the source.

```ts
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { createSuiStackMessagingClient } from '@mysten/sui-stack-messaging';

const client = createSuiStackMessagingClient(
  new SuiGrpcClient({
    baseUrl: 'https://fullnode.testnet.sui.io:443',
    network: 'testnet',
  }),
  {
    seal: {
      serverConfigs: [
        { objectId: '0x...', weight: 1 },
        { objectId: '0x...', weight: 1 },
      ],
    },
    encryption: { sessionKey: { signer: keypair } },
    relayer: { relayerUrl: 'https://your-relayer.example.com' },
  },
);
```

After construction, four namespaces are available: `client.messaging` (E2EE messaging), `client.groups` (permissions, [Sui Groups](https://github.com/MystenLabs/sui-groups)), `client.seal` (encryption), `client.core` (base Sui RPC).

### Path B (advanced) — manual `$extend` chain

Use this only if you need separate references to the intermediate clients, or if you're injecting a pre-built `SealClient`. The chain order matters: `suiGroups` + `seal` first, then `suiStackMessaging` on top. The canonical shape (mirrored from [`factory.ts`](../../../ts-sdks/packages/sui-stack-messaging/src/factory.ts)) — note `seal` is registered inline as `{ name: 'seal' as const, register: (c) => new SealClient({...}) }`, not via a `seal({...})` factory:

```ts
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { SealClient } from '@mysten/seal';
import { suiGroups } from '@mysten/sui-groups';
import { suiStackMessaging } from '@mysten/sui-stack-messaging';

const client = new SuiGrpcClient({
  baseUrl: 'https://fullnode.testnet.sui.io:443',
  network: 'testnet',
})
  .$extend(
    suiGroups({ witnessType: `${MESSAGING_PACKAGE_ID}::messaging::Messaging` }),
    {
      name: 'seal' as const,
      register: (c) => new SealClient({ suiClient: c, serverConfigs: [...] }),
    },
  )
  .$extend(
    suiStackMessaging({
      encryption: { sessionKey: { signer: keypair } },
      relayer: { relayerUrl: 'https://your-relayer.example.com' },
    }),
  );
```

See also [`docs/sui-stack-messaging/Setup.md`](../../../docs/sui-stack-messaging/Setup.md), but cross-check any factory/extension names against [`src/factory.ts`](../../../ts-sdks/packages/sui-stack-messaging/src/factory.ts) and [`src/client.ts`](../../../ts-sdks/packages/sui-stack-messaging/src/client.ts) — that doc is known to lag the source.

## Picking session-key tier

Three tiers, pick by your auth situation:

| Tier | When | What you pass |
|---|---|---|
| **1 — signer-based** | Keypair, Enoki, dapp-kit-core's `CurrentAccountSigner`, server-side Node with a key | `encryption: { sessionKey: { signer: keypair } }` |
| **2 — callback-based** | wallet integrations without a `Signer` abstraction | `encryption: { sessionKey: { address, onSign: async (msg) => signPersonalMessage(msg) } }` |
| **3 — manual** | you already manage `SessionKey` lifecycle externally | `encryption: { sessionKey: { getSessionKey: () => myManaged } }` |

Tier-1 is the default. Tier-2 is what most current React-wallet integrations end up needing. Tier-3 is rare — only choose if you have a strong reason to manage the key elsewhere.

For deeper coverage (TTL, refresh buffer, key rotation, MVR), see [`configure-session-keys`](../configure-session-keys/SKILL.md).

## Network auto-detection

Package IDs for `sui_stack_messaging` are auto-detected for testnet and mainnet — you don't need to pass `packageConfig` unless you're on localnet or a custom deployment. Constants live in [`ts-sdks/packages/sui-stack-messaging/src/constants.ts`](../../../ts-sdks/packages/sui-stack-messaging/src/constants.ts).

For localnet, pass an explicit `packageConfig` — see the `Configuration Reference` section of [`Setup.md`](../../../docs/sui-stack-messaging/Setup.md).

## Minimal "send + receive one message" example

```ts
// 1. Create a group (one-time setup)
const { groupId, encryptionHistoryId } = await client.messaging.createAndShareGroup({
  signer: keypair,
  groupRef: { uuid: 'my-app-channel-1' },
});

// 2. Send
await client.messaging.sendMessage({
  signer: keypair,
  groupRef: { uuid: 'my-app-channel-1' },
  text: 'Hello!',
});

// 3. Fetch
const { messages } = await client.messaging.getMessages({
  groupRef: { uuid: 'my-app-channel-1' },
  limit: 20,
});
```

The `GroupRef` pattern (UUID-derived IDs vs explicit object IDs) is documented in `Setup.md#groupref-pattern`. UUIDs are the recommended default.

## Optional: attachments

By default `sendMessage` cannot attach files. To enable file attachments, configure a storage adapter:

```ts
import { WalrusHttpStorageAdapter } from '@mysten/sui-stack-messaging';

createSuiStackMessagingClient(base, {
  // ...other config
  attachments: {
    storageAdapter: new WalrusHttpStorageAdapter({
      publisherUrl: 'https://publisher.walrus-testnet.walrus.space',
      aggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',
      epochs: 5,
    }),
  },
});
```

If you don't want to depend on public Walrus publishers/aggregators, see [`configure-walrus-storage-via-sdk`](../configure-walrus-storage-via-sdk/SKILL.md) for the programmatic alternative.

## Verification — the order to test things in

> **Safety:** steps (3) and (4) are **mutating** — they create a real on-chain group and write a real message via the relayer. Run them with a **dev signer on testnet (or localnet)** against a **throwaway group** (e.g. `uuid: \`integration-test-${Date.now()}\``). Never verify against a production group, a production relayer, or a wallet holding real value. None of the on-chain side effects can be undone.

1. **Client constructs without throwing.** Sanity check imports, peer deps, and `serverConfigs`. Non-mutating.
2. **Relayer reachable.** Independently: `curl http://localhost:3000/health_check` (dev) or your prod equivalent. Non-mutating.
3. **`createAndShareGroup` returns IDs.** Confirms signer + Sui RPC work. **Mutating — creates a shared on-chain group object; use a fresh dev UUID.**
4. **`sendMessage` returns without throwing.** Confirms relayer auth + encryption. **Mutating — writes ciphertext via the relayer (and, depending on relayer config, may touch on-chain state too).**
5. **`getMessages` returns the message you just sent, with `text` correctly decrypted.** Confirms full round-trip including Seal session-key flow. Non-mutating (read-only).

If any step past (1) fails, the most common causes are:
- (2) failure → relayer not running, wrong URL, CORS misconfig.
- (3) failure → signer doesn't have gas; wrong network in `SuiGrpcClient`; localnet without `packageConfig`.
- (4) failure → relayer's `GROUPS_PACKAGE_ID` doesn't match the network you're on; Seal `serverConfigs` invalid; session key creation failed.
- (5) decrypt failure → see [`debug-encryption-flow`](../debug-encryption-flow/SKILL.md).

## Where to go next

- Custom Seal access control (token-gating, subscriptions): [`extend-smart-contracts`](../extend-smart-contracts/SKILL.md).
- Bring your own relayer instead of running the reference Rust one: [`configure-custom-relayer-transport`](../configure-custom-relayer-transport/SKILL.md).
- Programmatic Walrus storage (skip public publishers/aggregators): [`configure-walrus-storage-via-sdk`](../configure-walrus-storage-via-sdk/SKILL.md).
- Production-ready session key handling: [`configure-session-keys`](../configure-session-keys/SKILL.md).
- Debug decryption / encryption failures: [`debug-encryption-flow`](../debug-encryption-flow/SKILL.md).

## Authoritative docs

- [`docs/sui-stack-messaging/Installation.md`](../../../docs/sui-stack-messaging/Installation.md) — install + peer deps.
- [`docs/sui-stack-messaging/Setup.md`](../../../docs/sui-stack-messaging/Setup.md) — full configuration reference.
- [`docs/sui-stack-messaging/Examples.md`](../../../docs/sui-stack-messaging/Examples.md) — end-to-end usage flows.
- [`docs/sui-stack-messaging/APIRef.md`](../../../docs/sui-stack-messaging/APIRef.md) — every public method.

