| name | integrate-sui-stack-messaging |
| description | 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". |
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-walrus-indexer 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:
- For local dev: run the reference Rust relayer locally → see
spin-up-relayer. Point your client at http://localhost:3000.
- 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.
There is no zero-server path. Even the simplest example below assumes a relayer URL is reachable.
Install
pnpm add @mysten/sui-stack-messaging @mysten/sui-groups
pnpm add @mysten/seal @mysten/sui @mysten/bcs
Peer dep minimums (verify against 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). Some docs may show an older name (createMessagingGroupsClient) — that's stale; trust the source.
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), 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) — note seal is registered inline as { name: 'seal' as const, register: (c) => new SealClient({...}) }, not via a seal({...}) factory:
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, but cross-check any factory/extension names against src/factory.ts and 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.
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.
For localnet, pass an explicit packageConfig — see the Configuration Reference section of Setup.md.
Minimal "send + receive one message" example
const { groupId, encryptionHistoryId } = await client.messaging.createAndShareGroup({
signer: keypair,
groupRef: { uuid: 'my-app-channel-1' },
});
await client.messaging.sendMessage({
signer: keypair,
groupRef: { uuid: 'my-app-channel-1' },
text: 'Hello!',
});
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:
import { WalrusHttpStorageAdapter } from '@mysten/sui-stack-messaging';
createSuiStackMessagingClient(base, {
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 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.
- Client constructs without throwing. Sanity check imports, peer deps, and
serverConfigs. Non-mutating.
- Relayer reachable. Independently:
curl http://localhost:3000/health_check (dev) or your prod equivalent. Non-mutating.
createAndShareGroup returns IDs. Confirms signer + Sui RPC work. Mutating — creates a shared on-chain group object; use a fresh dev UUID.
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).
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.
Where to go next
Authoritative docs