Rooms: coordinate two seats over a channel
Goal: two distinct seats exchanging messages neither the relay nor anyone else can read. A coordination channel is an MLS group (RFC 9420, the IETF standard for end-to-end encrypted groups): membership is explicit and cryptographic, and the relay between members stores and forwards ciphertext it cannot decrypt. This guide walks the owner and the joiner through the whole handshake.
Before you start
Section titled “Before you start”You need two distinct seats: two machines, or two tally homes, each with its own identity (tally init). A channel between two seats is not the same thing as syncing one seat across devices; for that, see Multi-device sync.
Both sides need a Skytale API key, read from $SKYTALE_API_KEY or passed as --api-key where the command accepts it.
1. Create the channel (owner)
Section titled “1. Create the channel (owner)”Mint the channel. You become its first member, and the MLS group is created with your seat as the initial roster:
tally channel create pronoic/dev/coordChannel names follow org/namespace/service, like pronoic/dev/coord. The command reads the owner key from $SKYTALE_API_KEY; --api-key, --api-url, and --relay-url override the defaults if you run your own endpoints.
2. Mint an invite token (owner)
Section titled “2. Mint an invite token (owner)”tally channel invite pronoic/dev/coordThis prints an invite token. Hand it to the other seat’s owner out of band. The token is an invitation to request joining; it does not add anyone to the group by itself.
3. Join with the token (joiner)
Section titled “3. Join with the token (joiner)”On the other seat:
tally channel join <token>The join blocks until the owner admits you, up to --timeout-secs (default 300). The joiner’s API key comes from $SKYTALE_API_KEY or --api-key.
4. Admit the joiner (owner)
Section titled “4. Admit the joiner (owner)”tally channel admit pronoic/dev/coordThis admits a pending joiner into the MLS group (up to the beta’s 2-seat cap). You rarely need to run it by hand: admission also happens automatically whenever the owner runs tally msg send or tally msg recv on that channel. Once admitted, the joiner’s blocked join returns and both seats are members.
5. Send and receive
Section titled “5. Send and receive”Any member can broadcast to the channel; every message is MLS-encrypted before it leaves the machine:
# send (either seat)tally msg send pronoic/dev/coord "spec ready for review"
# receive: decrypt, print, and persist new messagestally msg recv pronoic/dev/coordFor a long-lived inbox, watch blocks until a real message arrives, prints it like recv, and exits 0. Your own sends and MLS epoch noise never wake it, so it is safe to script against:
# block until a peer's message arrives, then exit 0tally msg watch pronoic/dev/coord
# or keep streaming until Ctrl-C, polling every 5 seconds by defaulttally msg watch pronoic/dev/coord --follow --interval 5What the relay sees
Section titled “What the relay sees”The relay stores and forwards ciphertext envelopes plus the minimal routing it needs (which channel, in what order). It holds no group key, so a relay breach leaks what the relay holds, which is ciphertext. Membership is the MLS-derived roster, not a server-side member list. This is a property of the construction, not a policy; the wording discipline for these claims is the honesty ledger.
The relay does see traffic shape: that a seat published or fetched on a channel, and when. That is the honest boundary of the claim.
Where next
Section titled “Where next”- tally channel : create, invite, join, and admit in full
- tally msg : send, recv, and watch flags
- What syncs, and what the cloud can see : the same operator-blind story for your own seat’s sync