Chat & profiles
raresats.net has a chat, at /chat, and display names for addresses. Anyone can read. Posting and naming need a wallet that signs BIP-322 messages (Xverse, UniSat, or your own key): there are no accounts and no passwords, only signatures.
Topics #
The chat is one room for now: General. /chat shows its messages directly; /chat/general works too, and a link to any other topic (/chat/market, from the first weeks' topics) goes to /chat, its ?thread= and ?m= kept.
Topics stay in the API, so more can come back: when there is more than one, each has its page, /chat/<topic>, its own messages and threads, and the page lists them down the left (on a phone, /chat is the list). The first weeks' other topics (Market & prices, Palindromes and the rest) are archived: hidden and closed to posts. Their messages are kept, and a link to one still opens its thread, read only.
GET /api/chat/topicslists them in order, each withlast_message_idandlast_activity_at(just General for now). The page keeps the last message id you read in each topic (in your browser only) and marks a topic unread when itslast_message_idis larger; the header's Chat link has a dot while one is.GET /api/chat/messages?topic=marketlists a topic's messages;POST /api/chat/messagestakes{text, topic}. Withouttopic, both are General's (main, the one room before topics, is General too: its messages are General's now).- Only moderators create topics (
POST /api/chat/topics) and archive them (DELETE /api/chat/topics/{id}: hidden and closed to new messages; the messages stay). A topic's id is its URL: 1 to 32 ofa-z,0-9and inner hyphens. Creating one of the first weeks' archived topics brings it back, its messages with it.
Links to messages #
Every message's time links to it: /chat?m=<id> (/chat/<topic>?m=<id> while there are topics) scrolls to the message and highlights it, and a reply's link, /chat?thread=<root>&m=<id>, opens its thread. ?thread=<id> alone opens a thread. A link naming the wrong topic finds the right one: GET /api/chat/messages/{id}/replies answers any message's thread with its topic.
Signing in #
GET /api/session/nonce?address=<address>returns anonce, usable once, for 10 minutes, by that address only.- Sign
raresats.net login <address> <nonce> <unix seconds>with the address (BIP-322 simple or full). POST /api/sessionwith{address, message, signature}.
The answer sets the session cookie __Host-rs_chat: HttpOnly, Secure, SameSite=Lax, Path=/, for 30 days. The server keeps only a hash of its token. GET /api/session says who is signed in; DELETE /api/session signs out.
The site signs in with your wallet's ordinals address by default; the payment address works too. Whichever address signs is the one shown in chat.
Posting #
POST /api/chat/messages with {text, topic} and the cookie. Messages are plain text:
- normalized to NFC; control, zero-width, bidi-override and private-use characters are dropped, and line breaks become spaces, so a message cannot spoof its direction or hide text;
- 1 to 500 characters after that;
- shown as text only: never HTML or Markdown, no images. Links are shown as links that open in a new tab, marked
nofollow ugc, labelled with their real host. A sat number writtensat 1953421509541, or a bare number of 10 to 16 digits up to the last sat, links to its sat page.
Limits: one message every 3 seconds and 20 a minute per address, and the same text twice within 2 minutes is refused (duplicate); the whole chat takes at most 5 messages a second (room_busy). Per IPv4 address or IPv6 /64: 20 sign-ins and 10 display-name changes an hour. Messages are kept for 30 days, at most 10,000 a topic.
Replies and threads #
Send {text, reply_to: <id>} to reply to a message; the reply is in its root's topic (a topic sent with it must be that one). Threads are one level deep: a reply to a reply joins its root's thread, and keeps the reply it answered as its quote (text only, cut at 140 characters). Every message has reply_to (its root, or null on a root), and a root has reply_count and last_reply_at.
GET /api/chat/messageslists a topic's roots only, each with its thread's counts.GET /api/chat/messages/{id}/replieslists a thread: itsrootand its replies, newest first, paged like a topic.- A reply's answer, and its live
chatevent, carry the root's new counts asthread: {id, reply_count, last_reply_at}.
Deleting a root deletes its whole thread: replies read wrong without what they answered, and what a moderator deletes is often what they quote. Bans, mutes and deletions apply to replies like any message.
Reading and live updates #
GET /api/chat/messages pages newest first; pass the answer's next as before for older messages. New messages and replies, deletions, topics and name changes are pushed on the event stream as chat and profile events: {type: "message", topic, message, thread?}, {type: "delete", ids, threads, topics} (the roots whose replies went, with their new counts, and the topics touched) and {type: "topic", topic, archived}. A page showing another topic marks the message's topic unread.
Display names #
Sign raresats.net profile <address> display_name=<name> <unix seconds> and send it to Set a display name with {address, display_name, message, signature}. Each change is signed on its own: a chat session is not enough. An empty name clears it.
| Rule | |
|---|---|
| Length | 3 to 24 characters, trimmed, inner spaces collapsed |
| Characters | letters, digits, spaces and _ . -; starts and ends with a letter or digit |
| Unique | compared without case, separators or look-alike digits: Rare_Hunter is rare hunter, B0B is bob |
| Not an address | nothing starting bc1, no base58 address-like strings, hex blobs or .. (a shortened address) |
| Not reserved | raresats, admin, moderator, support, official, satoshi and the like, anywhere in the name |
| How often | at most one change in 24 hours |
A name given up stays its last owner's for 7 days.
Moderation #
Chat is on only where the operator turned it on with moderation: AI moderation, moderators (admins), or both. GET /api/session says whether the AI moderation is on (moderated).
AI moderation. Every message and every new display name is checked by a language model before it is posted or taken, against the chat's rules. Refused: harassment and hate, sexual content, threats and violence, doxxing and personal data, scams and phishing (fake support, "send BTC to ...", asking for a seed phrase or private key, wallet-drainer and claim links, posing as raresats.net staff), spam and flooding, malware links, illegal goods. Allowed: trading talk, prices, sat numbers, addresses and txids in context, links to well-known explorers and marketplaces, mild profanity and criticism.
| Answer | Meaning |
|---|---|
422 moderated | Refused; category says why. Nothing was posted. |
403 muted | Three refusals in 10 minutes mute the address for an hour; six from one IPv4 address or IPv6 /48 within an hour pause that network. until is when it ends. |
429 rate_limited | At most 6 texts a minute are checked per address and per IPv4 address or IPv6 /48. |
503 moderation_unavailable | The text could not be checked right now (timeout or model error). Nothing was posted: try again. |
429 chat_busy | Too many texts are being checked across the site at once; wait retry-after seconds. |
Links must name their real host in plain ASCII: a host with letters from other scripts (a look-alike of a known site), punycode (xn--) or user@host is refused before the check (422 suspicious_link), and the page shows every link by its host name.
The check never fails open: a message that could not be checked is not posted. The rate limits come first, so a refused flood costs nothing. Moderators' own messages are not checked.
Moderators sign in like anyone else. Their sessions may delete messages (a thread's root with its replies), remove a display name (it is never given out again) and ban addresses: a banned address cannot post or set a display name, its name is removed, and its recent messages can be removed with the ban. Unbanning also ends a mute. Every moderator action is logged.