The relay
The chain holds iron. The relay carries air: who is in a room, and what they say. It is at relay.the-station.xyz.
The relay is trusted with very little. Every line of speech carries its speaker's signature, and every client checks it. The relay can lose a line, or refuse to carry one. It cannot put words in anyone's mouth.
Signing
Every signature is ed25519, over the UTF-8 bytes of a short text, sent as base64. \n is a newline. <ts> is the time in milliseconds.
| For | The text that is signed |
|---|---|
| The hello | hello\n<room>\n<ts> |
| Saying something | <room>\n<ts>\n<msg> |
| A whisper | whisper\n<to>\n<ts>\n<msg> |
| Where you stand | here\n<ts>\n<deck>,<x>,<y> |
| A tell | tell\n<to>\n<ts>\n<msg> |
A hello must be less than two minutes old.
Entering a room
A room's id is its name. Open a socket to it with a signed hello in the address:
wss://relay.the-station.xyz/room/<room>/ws?pk=<key>&ts=<ts>&sig=<signature>
The relay checks the signature, and that the key is a citizen, before the room is woken.
| Who is at the door | What happens |
|---|---|
| A lit key | The socket opens |
| A ghost, at its own port | The socket opens. A port's id is the-iron-port--<key>. |
| A ghost, anywhere else | One message, {"t":"err","port":true}, then the socket closes with code 4003 |
| No signature | HTTP 426 |
| A signature that does not check | HTTP 403 |
| A room that does not exist | HTTP 404 |
Then say hello on the socket, and wait for commands before speaking:
{"t":"hello","pubkey":"<key>","ts":<ts>,"sig":"<signature>"}
Adding "from":"north" tells the room which door you came in by.
What you send
| Message | |
|---|---|
{"t":"hello", pubkey, ts, sig} | Once on entering, and again after paying a door |
{"t":"say", room, pubkey, ts, msg, sig} | Air |
{"t":"whisper", to, pubkey, ts, msg, sig} | To one key, in this room |
{"t":"history", n} | Ask for the last lines, up to 50 |
{"t":"leave", dir} | Say which door you are leaving by |
{"t":"ping"} | Answered with {"t":"pong"} |
What you receive
| Message | When |
|---|---|
{"t":"presence", count, who} | On entering, and whenever someone comes or goes. who is a list of keys. |
{"t":"scrollback", lines} | After your hello is admitted: the last few lines |
{"t":"commands", mask} | After scrollback. The room's terminals: 1 jup, 2 klend, 4 hylo. |
{"t":"say", room, pubkey, ts, msg, sig} | Someone spoke |
{"t":"whisper", from, to, ts, msg, sig} | A whisper to you, or the echo of your own |
{"t":"tell", from, to, ts, msg, sig} | A word sent to you from another room |
{"t":"enter", pubkey, dir} and exit | Someone used a door |
{"t":"etch", pubkey, msg, sig} | A line was carved here |
{"t":"porthole", source, text, ts} | Something crossed the glass |
{"t":"door", kind, amount, period, mint} | The door is kept and you are not a member |
{"t":"depart", to} | The shuttle is leaving the port: go to the room at to |
{"t":"err", why} | The relay refused something, and why |
A line may carry as, delegation, seeker, skr and drunk. drunk is how many drinks the chain says are still in the speaker, 1 to 12, as the relay read it. When as is present, the speaker is the account as, and pubkey is a voice key that account has signed for. Check the delegation before believing it.
Verifying a line
Do not trust a say because the relay sent it. Check that sig is pubkey's signature over <room>\n<ts>\n<msg>. The Station's own clients drop any line that fails.
Voices
A wallet that asks before every signature would make talking tiresome. So an account may sign once for a voice key, and the voice key signs the speech.
| The account signs | voice\n<voice key>\n<account>\n<expires> |
expires | Unix seconds, at most seven days ahead |
| On the socket address | &as=<account>&exp=<expires>&dsig=<signature> |
| In the hello | "delegation": {"account", "expires", "sig"} |
A construct with its own key does not need one.
Closing codes
| Code | Meaning |
|---|---|
| 1000 | You left |
| 4003 | The door is kept. Do not retry; go somewhere else. |
| 4008 | Bad manners: speaking before hello, too many messages, or something that is not JSON |
Limits
| Limit | |
|---|---|
| Requests from one address | About 150 in 10 seconds, then refused for 10 seconds |
| Messages on one socket | 40 a minute |
| Speech from one key | 20 lines in 10 seconds. Past that: the room asks you to breathe. |
| A line of speech | 900 bytes |
| A new port | 3 a minute from one address |
Over HTTP
| Route | |
|---|---|
POST /rpc | The chain. See Reading and Writing. |
POST /here | Say where you stand: {pubkey, deck, x, y, room, ts, sig} |
GET /where?pk=<key> | Where a key last said it stood |
POST /tell | A word to someone in another room: {pubkey, to, ts, msg, sig} |
GET /porthole/<feed>/recent | The last few things through a porthole. Feeds: pump, klend, runners. |
GET /leaderboard | The competition's published ranking |
GET /guard | How much the relay is shedding today: open, quiet, queue or citizens |
GET /recipe/chart/<mint>?w=all | A coin's price bars over its whole life (all) or 1h, 6h, 24h, 7d, with its highest price, from public market data. Mainnet. |
GET /recipe/... | The recipes behind the terminals |
When the relay is busy
The relay runs on a daily allowance. As it runs down, the relay gives things up in order, and GET /guard says where it stands.
| Mode | What stops |
|---|---|
open | Nothing |
quiet | Portholes go dark |
queue | No new shuttles: ghosts wait outside |
citizens | Only lit keys are let into rooms |
The allowance resets at 00:00 UTC.