Skip to content

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.

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.

Mint the channel. You become its first member, and the MLS group is created with your seat as the initial roster:

Terminal window
tally channel create pronoic/dev/coord

Channel 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.

Terminal window
tally channel invite pronoic/dev/coord

This 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.

On the other seat:

Terminal window
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.

Terminal window
tally channel admit pronoic/dev/coord

This 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.

Any member can broadcast to the channel; every message is MLS-encrypted before it leaves the machine:

Terminal window
# send (either seat)
tally msg send pronoic/dev/coord "spec ready for review"
# receive: decrypt, print, and persist new messages
tally msg recv pronoic/dev/coord

For 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:

Terminal window
# block until a peer's message arrives, then exit 0
tally msg watch pronoic/dev/coord
# or keep streaming until Ctrl-C, polling every 5 seconds by default
tally msg watch pronoic/dev/coord --follow --interval 5

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.