Server accounts
A server account is a username and password stored by one OriginChats server, instead of a Rotur account. The protocol calls these accounts cracked. This guide shows how to find out whether a server supports them, how to register and log in, and what changes afterwards for uploads, avatars, and signing.
A server account exists only on the server that created it. It isn’t linked to a Rotur account, even when the names match.
Check what the server supports
Section titled “Check what the server supports”Read auth_mode and capabilities from the handshake before you show any sign-in controls:
auth_mode |
What to offer |
|---|---|
rotur |
Rotur sign-in only. |
cracked |
Rotur sign-in and local login. |
cracked-only |
Local login only. |
When local accounts are enabled, the server advertises login, local_attachment_auth_v1, and server_account_avatars_v1. It adds register only when the operator allows new registrations. Show a sign-up form only when register is present.
Save credentials under the server’s URL, not its display name. A local username means nothing on another server.
Register or log in
Section titled “Register or log in”Both commands take the same fields:
{ "cmd": "register", "username": "alice", "password": "correct horse battery staple", "client": "mychat", "device": "computer"}Send cmd: "login" to sign in to an existing account. client and device are optional labels, as with auth.
The server trims the username and lowercases it. Registration then requires:
- A username of 2 to 32 characters, using only
a-z,0-9,-, and_. - A password of 4 to 72 characters.
register also accepts bot: true to create a bot account. The server stores only a bcrypt hash of the password, and never sends it to clients.
If the server uses access control, include server_password or invite_code as that page describes.
Read the result
Section titled “Read the result”Success produces the same frames as Rotur sign-in:
{ "cmd": "auth_success", "val": "Authentication Successful" }{ "cmd": "ready", "user": { "username": "USR:local_alice", "nickname": "alice", "roles": ["user"], "color": null, "cracked": true, "clients": ["mychat"], "devices": ["computer"], "pfp": "https://chat.example.com/avatar/USR%3Alocal_alice", "pfp_url": "https://chat.example.com/avatar/USR%3Alocal_alice", "status": { "status": "online", "text": "" } }, "local_upload_token": "k3J9x2QvA7mB0cR5tY8wZ1nL4pH6sD9fG2jK5eU8iO1aV3bN7qW0xC4zM6yT2rE5"}The account’s protocol username is USR:local_ plus the name you registered. Use that full value in commands and as the account’s key. Show nickname to people. Don’t strip the prefix and merge the account with a Rotur user of the same name.
Failures come back as auth_error with one of these val strings:
val |
When |
|---|---|
Username and password required |
A field is missing or empty. |
User not found |
login named an account that doesn’t exist. |
Invalid password |
login used the wrong password. |
Username must be 2-32 characters |
register username is too short or too long. |
Username can only contain letters, numbers, hyphens, and underscores |
register username has other characters. |
Password must be 4-72 characters |
register password is too short or too long. |
bot must be a boolean |
register sent bot with a non-boolean value, including null. |
Username already taken |
register chose a name that exists. |
This server is full |
The server has reached its member limit. |
Registration is disabled |
The operator turned off registration. |
Cracked authentication is disabled |
The server’s auth_mode is rotur. |
Show val next to the form. Don’t log passwords, even on failure.
Keep identity types apart
Section titled “Keep identity types apart”After ready, check ready.user.cracked to learn which kind of account signed in. In cracked mode, the same server accepts both kinds, so the handshake alone doesn’t tell you.
For a server account:
- Don’t sign messages or slash calls. The server ignores proofs from server accounts.
- Use
local_upload_tokenfor attachment and avatar uploads. - Keep profile data scoped to this server.
For a Rotur account, ignore any old upload token and use the Rotur validator flow for uploads.
When you reconnect, log in again and replace the upload token with the one in the new ready. The old token stops working as soon as its connection closes.
Upload attachments
Section titled “Upload attachments”HTTP requests don’t share the WebSocket’s sign-in. Send local_upload_token in the upload body:
const form = new FormData();form.append("file", file);form.append("name", file.name);form.append("mime_type", file.type || "application/octet-stream");form.append("local_upload_token", localUploadToken);
const response = await fetch(new URL("/attachments/upload", serverUrl), { method: "POST", body: form,});Uploading attachments covers the rest of the flow.
Upload and display avatars
Section titled “Upload and display avatars”Server accounts can set their own avatar. Only use this when the handshake advertises server_account_avatars_v1.
Send POST /avatar/{username} as multipart form data with file and local_upload_token:
const form = new FormData();form.append("file", file);form.append("local_upload_token", localUploadToken);
const response = await fetch( new URL(`/avatar/${encodeURIComponent(me.username)}`, serverUrl), { method: "POST", body: form },);The rules are:
{username}must be the account that owns the token.- The file must be a PNG or JPEG from 1 byte to 5 MB, no more than 4096 pixels on either side.
- Each account can upload 5 avatars per minute.
The server crops the image to a centred square, resizes it to 512 by 512, and saves it as PNG. A successful upload returns the avatar URL and your updated user object (shortened here):
{ "url": "https://chat.example.com/avatar/USR%3Alocal_alice", "user": { "username": "USR:local_alice", "nickname": "alice", "cracked": true }}Every signed-in connection also receives a user_update event:
{ "cmd": "user_update", "user": "USR:local_alice", "pfp": "https://chat.example.com/avatar/USR%3Alocal_alice"}The URL doesn’t change when the image does, so reload the image when you get this event. GET /avatar/{username} sends an ETag and Cache-Control: public, no-cache, so a normal revalidating fetch picks up the new image. HTTP routes lists the upload errors.
Next steps
Section titled “Next steps”- Signing and verification: prove authorship for Rotur accounts.
- Server account protocol: the wire rules in one place.
- Server accounts for operators: how to turn the feature on.
