Architecture
Skytale splits the server side into two planes, and neither one can read your messages. The directory (an HTTP API) handles accounts, channel registration, and the join handshake; the relay store-and-forwards ciphertext. Everything confidential (keys, group state, plaintext) lives in the clients. This page explains each plane, what it stores, and how the pieces fit.
The two planes
Section titled “The two planes”| Plane | Host | Role |
|---|---|---|
| Directory | https://api.skytale.sh |
Accounts and API keys, channel registration, invite tokens, the key-package directory and Welcome handoff, and the device-grant flow. Auth is your account API key (sk_live_...) or a short-lived JWT minted from it. |
| Relay | https://relay.skytale.sh |
The message plane. Store-and-forward for opaque ciphertext: a WebSocket for live publish and subscribe, plus two HTTP catch-up endpoints (GET /v1/messages for app-message backfill, GET /v1/sync for the full log including MLS commits). Auth is a Bearer JWT. |
The split is the point. Coordination (who is allowed to join, where a Welcome gets handed off) needs a stateful service; carrying messages needs a pipe. Neither job needs the keys, so neither plane gets them.
Channels are MLS groups
Section titled “Channels are MLS groups”A channel is one MLS group (RFC 9420), exactly one group per channel. The channel name is a 3-part org/namespace/service string; the transport group-id is SHA-256 of that name, computed identically by the relay and the SDK.
Membership is enforced cryptographically, not administratively. Joining means: the joiner publishes an MLS key package to the directory, the channel owner runs add_member client-side and publishes the resulting MLS Welcome back through the directory, and the joiner processes that Welcome to enter the group. The directory brokers this handshake but never parses the MLS bytes; key packages and Welcomes cross it as opaque blobs.
A consequence worth stating plainly: the relay does not gate reads by channel ownership. A valid JWT plus a well-formed channel name is enough to fetch a channel’s ciphertext. The real gate is the MLS group itself; you cannot decrypt a channel you were never added to. Ownership checks live in the directory plane, on the owner-only operations.
What each plane stores
Section titled “What each plane stores”The directory stores (in Postgres):
- Account records and hashed API keys. Raw keys are shown once at mint and never stored.
- Channel registrations: the readable name and which account owns it.
- Invite tokens (hashed) and pending join requests: the joiner’s identity string plus its key package and, later, its Welcome, both opaque MLS bytes in transit. Completed requests are garbage-collected.
- Device grants, with the granted credential stored sealed (see Authentication).
The relay stores: a per-channel archive of ciphertext entries with monotonic sequence numbers, so offline members can catch up. The archive is persisted on disk but bounded by a per-channel cap and a TTL (defaults: 1000 entries per channel, 7 days); evicted messages are gone. The relay holds no key material, no plaintext, and no MLS group state.
The clients store: everything that matters. Identity keys, MLS group state, and plaintext exist only on member devices.
What the planes can see
Section titled “What the planes can see”Both planes see metadata: channel names, who registered and joined what, message sizes and timing, account email addresses. Neither sees content or keys. The full honest accounting, including what metadata implies, is in the security model.
Building a custom client
Section titled “Building a custom client”The whole client-facing surface is eight operations: five directory ops (register, invite, publish key package, Welcome handoff, ack) and three message ops (WebSocket publish/subscribe, message backfill, epoch sync). You bring an MLS engine; the planes bring coordination and transport. The complete endpoint reference, with request and response shapes and the end-to-end owner, joiner, and reconnect flows, is at Directory and relay API.
Where next
Section titled “Where next”- Directory and relay API: the full endpoint reference for custom clients
- Authentication: API keys, JWTs, and the device-grant flow
- Security model: what the operator can and cannot see, precisely