Skip to content

Access control

Access control decides who may join a server. The server checks it every time someone authenticates, so a client needs to know about it to join a server that isn’t open.

A server is in one of five access modes:

Mode Who gets in
open Anyone who authenticates. This is the default.
whitelist Whitelisted users and existing members.
password Whitelisted users, and anyone who sends the server password.
application Whitelisted users and existing members. Others apply and wait for a reviewer.
invite Whitelisted users, and anyone who sends a usable invite code.

A member is a user whose account on this server is marked as joined. Someone becomes a member the first time they authenticate successfully, and stops being one when they leave. The server owner is always let in, whatever the mode.

The check runs inside auth, login, and register. It runs after the ban check and before the member limit check.

Each of those commands takes two optional fields for access control:

{
"cmd": "login",
"username": "alice",
"password": "<account password>",
"server_password": "<server password>",
"invite_code": "3F9A1C7E52B4"
}
Field Type Description
server_password string The server password. Used only in password mode.
invite_code string An invite code. Used only in invite mode. The server trims it and uppercases it.

When access is refused, authentication fails with an auth_error frame that carries a machine-readable reason and the current mode:

{
"cmd": "auth_error",
"val": "Your join application is pending review",
"reason": "application_pending",
"mode": "application",
"application_id": "5b1f3c1e-8d0a-4d6e-9f43-2a7c9e1b0d55"
}
reason val Mode
not_whitelisted You are not whitelisted on this server whitelist
password_required This server requires a password to join password
password_incorrect Incorrect server password password
application_required This server requires approval to join. Submit an application first. application
application_pending Your join application is pending review application
invite_required This server requires an invite code to join invite
invite_invalid Invalid invite code invite
invite_expired This invite has expired invite

application_id appears only with application_pending. The connection stays open after an auth_error, so the client can retry authentication on it.

User whitelist password application invite
Server owner Admitted Admitted Admitted Admitted
Whitelisted user Admitted Admitted Admitted Admitted
Existing member Admitted Needs the password Admitted Needs an invite
Anyone else Refused Needs the password Must apply Needs an invite

The handshake frame includes the public access settings, so a client can prompt for a password or invite code before it authenticates:

{
"cmd": "handshake",
"val": {
"access_control": {
"mode": "password",
"has_password": true
}
}
}

(Other handshake fields are left out here.) After joining, members can read the same data with access_mode_get.

Open. Authenticate normally.

Whitelist. Authenticate normally. If the user isn’t whitelisted or a member, the server returns not_whitelisted. Someone with manage_access must add them with whitelist_add. The user can also send application_submit, which the server accepts in any mode. An approved application adds the user to the whitelist.

Password.

  1. Read access_control.mode from handshake. If it’s password, ask the user for the server password.
  2. Authenticate with server_password set.
  3. On password_incorrect, ask again and retry.

Application.

  1. Authenticate. A new user gets application_required.
  2. Send application_submit with user_id, username, and an optional message. The refused connection has no identity, so the server can’t fill these in. The response has status: "pending".
  3. Wait for a reviewer. Retrying authentication meanwhile returns application_pending.
  4. When a reviewer approves, the user is added to the whitelist. Authenticate again to join. The server sends application_approved only to connections already authenticated as the applicant, so a waiting client should retry on its own rather than wait for that frame.

Invite.

  1. Get a code, usually from an invite link such as https://chat.example.com/invite/3F9A1C7E52B4.
  2. Optionally call GET /invite/{code}/check to show the server’s name and check that the code works. See Invite pages.
  3. Authenticate with invite_code set. The server spends one use of the invite and adds the user to the whitelist.
  4. Later logins don’t need the code.

Read the mode with access_mode_get and change it with access_mode_set. A change applies to the next authentication. Users already connected stay connected.

When the mode changes, every other authenticated connection receives:

{ "cmd": "access_mode_update", "mode": "invite" }

The server stores access data in access.json in its database directory. The access_control.mode config value seeds the mode only when that file doesn’t exist yet. After that, access.json is the source of truth.

The whitelist is a list of user IDs that are always let in, whatever the mode.

  • whitelist_add adds a user by username or ID. The server must already know the user, which means they have authenticated here before.
  • whitelist_remove removes a user. It doesn’t remove their membership or disconnect them.
  • whitelist_list lists every entry.

The server also adds users to the whitelist when a reviewer approves their application and when they redeem an invite.

server_password_set sets or removes the password. The server stores only a bcrypt hash. Passwords are 1 to 72 characters.

  • password mode needs a password. access_mode_set refuses password until one is set.
  • Removing the password while in password mode switches the server to open.
  • A password can be set in any mode, but the server checks it only in password mode.

An application is a request to join, reviewed by a moderator. It looks like this:

{
"id": "5b1f3c1e-8d0a-4d6e-9f43-2a7c9e1b0d55",
"user_id": "USR:local_alice",
"username": "USR:local_alice",
"message": "Friend of Bob's, here for the game nights.",
"status": "pending",
"created_at": 1767225600.123,
"reviewed_at": null,
"reviewed_by": null
}
Field Description
id Application ID, a UUID.
user_id, username The applicant.
message The applicant’s message, up to 500 characters. May be empty.
status pending, approved, or rejected.
created_at When it was submitted, in Unix seconds.
reviewed_at, reviewed_by When and by whom it was reviewed. null while pending. reviewed_by is a user ID.

An application moves through these states:

  1. application_submit creates it as pending. A user has at most one pending application; submitting again returns the existing one.
  2. application_review moves it to approved or rejected. Only pending applications can be reviewed. Approval adds the user to the whitelist.
  3. A rejected user may submit a new application. An approved user can’t, because they are already whitelisted.

application_list lists applications, optionally by status. application_delete removes one from the record. Deleting an approved application doesn’t remove the user from the whitelist. Applications are never removed automatically.

When a pending application is submitted, every authenticated connection with manage_access or review_applications receives:

{
"cmd": "application_new",
"application": {
"id": "5b1f3c1e-8d0a-4d6e-9f43-2a7c9e1b0d55",
"user_id": "USR:local_alice",
"username": "USR:local_alice",
"message": "Friend of Bob's, here for the game nights.",
"status": "pending",
"created_at": 1767225600.123,
"reviewed_at": null,
"reviewed_by": null
},
"pending": 3
}

pending is the number of pending applications.

An invite is a code that lets someone join in invite mode.

{
"code": "3F9A1C7E52B4",
"max_uses": 5,
"uses": 1,
"created_by": "USR:local_bob",
"created_at": 1767225600.123,
"expires_at": 1767830400.123
}
Field Description
code 12 uppercase hexadecimal characters.
max_uses How many people can redeem it. 0 means unlimited.
uses How many times it has been redeemed.
created_by User ID of the creator.
created_at Unix seconds.
expires_at Unix seconds, or null if it never expires.

An invite is usable while it hasn’t expired and uses is below max_uses (or max_uses is 0).

  • invite_create makes an invite. max_uses defaults to 1 and can be up to 1000. expires_in is optional and can be up to 30 days.
  • invite_list lists every invite, including expired and used-up ones.
  • invite_delete deletes one. Spent and expired invites stay until someone deletes them.

Invites can be created in any mode, but the server accepts invite_code only in invite mode. A whitelisted user who sends a code doesn’t use it up.

The server has two public HTTP routes for invite links.

GET /invite/{code} redirects with 302 to https://originchats.com/invite/{host}/{code}, where {host} is the host of the server’s configured server.url. It doesn’t check the code. It returns 404 with {"error": "Invite not found"} if the code is blank or the server has no URL configured.

GET /invite/{code}/check checks a code without spending a use. It always returns the server’s public info in server.

{
"valid": true,
"code": "3F9A1C7E52B4",
"expires_at": 1767830400.123,
"server": {
"name": "Game Night",
"icon": "https://chat.example.com/server-assets/icon.png",
"banner": "",
"url": "https://chat.example.com",
"owner": { "name": "bob" }
}
}

An unusable code returns 200 with valid: false:

{
"valid": false,
"reason": "invite_expired",
"server": { "name": "Game Night", "icon": "", "banner": "", "url": "https://chat.example.com", "owner": { "name": "bob" } }
}
  • reason is invite_expired if the code exists but has expired, otherwise invite_invalid. A used-up code that hasn’t expired returns invite_invalid.
  • The check is case-sensitive. Send the code in uppercase. Authentication uppercases it for you; this route doesn’t.
  • The response doesn’t say whether the server is in invite mode. Read that from handshake.
Permission Allows
manage_access Everything on this page: changing the mode, managing the whitelist and password, listing and deleting invites, and reviewing and deleting applications. access_mode_get also returns extra counts.
review_applications application_list and application_review, plus application_new events.
create_invite invite_create. The default user role has this permission.

The owner and any role with administrator have all three. See roles and permissions.

Other rules:

  • Only application_submit works before a connection is admitted. Every other command here needs an authenticated member.
  • A user in timeout can’t change access settings, review or delete applications, or create or delete invites. They get a rate_limit frame instead. Reads still work. See the protocol model.
  • Every change is written to the audit log under the server_management category. Removing the password is logged as server_password_remove.
Command Purpose
access_mode_get Read the access mode.
access_mode_set Change the access mode.
whitelist_add Add a user to the whitelist.
whitelist_remove Remove a user from the whitelist.
whitelist_list List whitelisted users.
server_password_set Set or remove the server password.
application_submit Apply to join.
application_list List applications.
application_review Approve or reject an application.
application_delete Delete or withdraw an application.
invite_create Create an invite code.
invite_list List invite codes.
invite_delete Delete an invite code.