Browse topics
Relay API
Real-time cross-device messaging for resources via the encrypted relay channel.
Relay API
The Relay API gives resources a real-time messaging channel across devices. Messages are routed through the RightPlace relay server and are end-to-end encrypted using the shared master key.
Capability required: relay:channel
How it works
- Your resource sends a message via
relay.send() - The message is encrypted and broadcast to all connected devices through the existing relay WebSocket
- Other devices receive the message and forward it to the matching resource iframe
- Messages are scoped by manifest ID — only instances of the same resource type receive each other’s messages
Frontend SDK
Connect to the relay
const rp = createResourceClient();
await rp.ready();
const channel = rp.relay.connect();
Send a message
channel.send({ type: "cursor_move", x: 100, y: 200, userId: "alice" });
The data argument can be any JSON-serializable value. It is encrypted before being sent over the wire.
Receive messages
channel.on("message", (data, deviceId) => {
console.log("From device:", deviceId);
console.log("Data:", data);
// { type: "cursor_move", x: 100, y: 200, userId: "alice" }
});
The deviceId parameter identifies which device sent the message. Messages you send are also delivered back to your own handler, so you can use deviceId to filter out your own messages if needed.
Disconnect
channel.disconnect();
Call disconnect() when your component unmounts or when you no longer need real-time updates.
Backend SDK
The backend SDK provides a simpler fire-and-forget send() method:
const server = createResourceServer({
methods: {
async syncData(params, { rp }) {
// Broadcast to all connected devices
await rp.relay.send({ type: "data_updated", rows: params.rows });
},
},
});
Full example: collaborative cursors
import { createResourceClient } from "@rightplace/resource-sdk";
const rp = createResourceClient();
await rp.ready();
const channel = rp.relay.connect();
// Broadcast cursor position on mouse move
document.addEventListener("mousemove", (e) => {
channel.send({
type: "cursor",
x: e.clientX,
y: e.clientY,
});
});
// Render remote cursors
channel.on("message", (data, deviceId) => {
if (data.type === "cursor") {
renderCursor(deviceId, data.x, data.y);
}
});
// Cleanup on unmount
window.addEventListener("beforeunload", () => {
channel.disconnect();
});
Security
All relay messages are encrypted with AES-256-GCM using the shared master key before being sent over the WebSocket. The relay server never sees plaintext message content. Only devices that share the same master key (i.e. devices belonging to the same user) can decrypt messages.
Manifest configuration
Add the relay:channel capability to your resource.json:
{
"id": "my-resource",
"version": "1.0.0",
"capabilities": ["relay:channel"]
}