Requests
MessagePack formatted requests sent by the client. All Requests are required to get a sequence id assigned by the client. Responses to requests always contain the same sequence id as their corresponding request. The structure of a request is as follows:
{
type: [REQUEST TYPE],
s: [SEQUENCE ID],
data: { [REQUEST PARAMETERS] }
}
The structure of a response looks like this:
{
type: [REQUEST TYPE],
s: [SEQUENCE ID],
data: { [RESPONSE DATA] }
}
In the case of an error the server returns a JSON object with the following structure:
{
type: "error",
s: [SEQUENCE ID],
data: {
message: [ERROR MESSAGE],
code: [ERROR CODE]
}
}
auth
Authorization. (Most) important step of the entire thing rn. requires multiple steps, all of them get sent via type: "auth".
Auth process step by step:
Prerequisites
The client must possess an ECDH keypair, generated on the curve secp521r1
(this may change or become more flexible in the future).
The client needs to be able to compute shared ECDH keys and create SHA-512 hashes.
Step 1
After connecting, the client sends their public key to the server in the following format:
{
type: "auth",
s: [SEQUENCE ID],
data: {
key: [ECDH PUBLIC KEY:BIN]
}
}
The ECDH key must represent a valid public key for the secp521r1 (short: P-521) elliptic curve.
The server now computes the shared secret, hashes it and uses that hash to cipher some random bytes.
After that it responds like this:
{
type: "auth",
s: [SEQUENCE ID],
data: {
key: [SERVER PUBLIC KEY:BIN],
ciphertext: [AES-256-CBC ENCRYPTED RANDOM BYTES:BIN],
iv: [IV USED FOR THE AES CIPHER:BIN]
}
}
The ciphertext is random bytes ciphered using AES-256-CBC with a SHA256 hash of the ECDH shared secret as key and the passed iv as iv.
Step 2
Now that the client has the servers public key, it too can compute the shared secret.
After doing so, it hashes the shared secret with SHA256.
This hash is used to decrypt the passed ciphertext.
It is to be decrypted using the hash as key and the passed iv.
After deciphering the random bytes they are hashed using SHA256.
The hash is then sent to the server in the following format:
{
type: "auth",
s: [SEQUENCE ID],
data: {
hash: [SHA256 HASHED BYTES:BIN],
name: [USERNAME], /* can only be freely picked on first connect.
for changes the user needs the required permissions */
avi: [STRING], // not implemented yet. Just pass any string value right now
status: [USER STATUS, STRING]
}
}
If the hash is correct the client has now successfully confirmed that they own the fitting private key to their public key.
If everything worked out the server returns a User object of the requesting client.
getusers
getuserbyid
Gets a user by user-id. Currently not implemented. Possible complete removal.
Parameters:
uId: [USER ID]
Response:
getchannels
getroles
gethistory
Gets message history in specified channel. This request is important to execute as early as possible because upon execution the user gets registered as a listener for events in all channels they can see
Parameters:
cId: [CHANNEL-ID],
amount: [AMOUNT (1-100)]
before: [OPTIONAL, LOAD MESSAGES BEFORE THIS MESSAGE ID]
Response:
Array of message ojects
getpinned
Gets all pinned messages for specified channel.
Parameters:
cId: [CHANNEL ID]
Response:
Array of message objects
togglepinmsg
Toggles the pin state of a message. (if pinned gets unpinned, if unpinned gets pinned)
Events:
Parameters:
mId: [MESSAGE ID]
Response:
the pinned message object
sendmsg
Sends a message to a channel.
Event:
Parameters:
cId: [CHANNEL ID],
content: [MESSAGE TEXT],
attachments: {
files: [ARRAY OF SHA256 FILEHASHES],
embeds: [LEAVE EMPTY, WIP]
}
Response:
deletemsg
deletes a specified message.
Event:
Parameters:
mId: [MESSAGE ID]
Response:
message object of edited message
editmsg
edits a specified messages text.
Event:
Parameters:
mId: [MESSAGE ID],
content: [NEW MESSAGE TEXT]
Response:
message object of edited message
addchannel
adds a channel.
Event:
Parameters:
name: [NAME OF CHANNEL], // 1-64 chars
description: [DESCRIPTION OF CHANNEL], // 0-4000 chars
pId: [PARENT-ID OF CHANNEL], // Can only be a category channel (type 1)
oId: [ORDER-ID OF CHANNEL], /* Reflects channel position in scope.
Lower number means higher position. */
type: [TYPE OF CHANNEL], // 0: Text channel, 1: Category
perms: {
[ROLE ID]: [PERM STRING],
[ROLE ID]: [PERM STRING]... /* Not every role has to be defined.
[If it isnt defined here or if the defined value goes above
the invoking users permissions,
the roles base-channel-permission is used.
If additionally the channel has a parent,
the role permission defined in the parent is used. */
}
Response:
editchannel
edits a channel.
Event:
Parameters:
cId: [ID OF CHANNEL],
[PARAM]: [NEW VALUE],
[PARAM]: [NEW VALUE]...
Response:
channel object of edited channel
deletechannel
deletes a channel.
Event:
Parameters:
cID: [CHANNEL ID]
Response:
id of deleted channel
addrole
adds a role.
Event:
Parameters:
name: [NAME OF CHANNEL], // 1-64 chars
color: [#RRGGBB],
priority: [INT], // cannot be above or equal to the invoking users highest roles priority!
displayType: [BOOL], // determines if users should be grouped by this role in the sidebar or not
globalPerms: [BASE32 PERMISSION STRING], // cant be greater than the invoking users perms.
baseChannelPerms: [BASE32 PERMISSION STRING] // cant be greater than the invoking users perms.
Response:
role object of created role
editrole
edits a role.
Event:
Parameters:
rId: [ID OF ROLE],
[PARAM]: [NEW VALUE],
[PARAM]: [NEW VALUE]...
Response:
role object of edited role
deleterole
addusertorole
adds a role to a user.
Event:
Parameters:
rId: [ID OF ROLE]
uId: [ID OF USER]
Response:
removeuserfromrole
removes a user from a role.
Event:
Parameters:
rId: [ID OF ROLE],
uId: [ID OF USER]
Response:
uploadfile
Uploads a file to the server. Only multi-step task besides auth.
Has to be done, in order to attach the file to messages.
Any error during any part of the process immediatly stops the uploading process.
Upload process step by step:
Prerequisites:
The client must calculate a SHA256 hash of a file.
The client must split the file into parts,
which have a size that will be variable in the future
but is currently hard-coded to 2048 bytes.
Important notice: This task requires the continuous use of a single sequence id until the process is done!
Step 1: Metadata
At this stage the client sends the metadata of the file:
{
type: "uploadfile",
s: [SEQUENCE ID],
data: {
hash: [SHA256 FILE-HASH:bin],
name: [FULL FILENAME + EXTENSION:string],
contentType: [MIME TYPE OF FILE:string],
size: [SIZE IN BYTES:int(for now)]
}
}
The server can now respond in two ways:
File with this hash already exists on the server!
The server will send the already existing file object so the client can use it in attachments:
{
type: "uploadfile",
s: [SEQUENCE ID],
data: {
[FILE OBJECT]
}
}
File doesnt exist yet!
The server will send an empty response to signify successful receival:
{
type: "uploadfile",
s: [SEQUENCE ID],
data: {}
}
Step 2: Uploading
The client will now send the numbered file-parts to the server.
After each file part the client waits for the servers response.
This step is repeated until all fileparts are sent.
{
type: "uploadfile",
s: [SEQUENCE ID],
data: {
part: [FILEPART NUMBER:int],
bin: [BINARY DATA:Buffer/UInt8Array]
}
}
Upon successful receival, the server responds as follows:
{
type: "uploadfile",
s: [SEQUENCE ID],
data: {
part: [FILEPART NUMBER:int],
}
}
Final response after all fileparts have been uploaded:
file object of the uploaded file
setavi
sets an uploaded file as avi of invoking user. File has to be a square image of size n (n set to 2MB on test server).
Event:
Parameters:
hash: [SHA256 FILEHASH:bin]
Response:
user object of edited user
Events
MessagePack formatted data sent by the server with no request being needed.
Structure:
{
type: "event",
name: [EVENT NAME],
data: [EVENT DATA]
}
newmsg
Triggered when a new message gets sent in any channel the client has reading permissions to.
Event Data:
msgedited
Triggered when a message gets edited in any channel the client has reading permissions to.
Event Data:
msgdeleted
Triggered when a message gets deleted in any channel the client has reading permissions to.
Event Data:
msgpinned
Triggered when a message gets pinned in any channel the client has reading permissions to.
Event Data:
msgunpinned
Triggered when a message gets unpinned in any channel the client has reading permissions to.
Event Data:
channeladded
channeledited
channeldeleted
Triggered when a channel gets deleted and the user has read permissions.
Event Data:
id of deleted channel.
roleadded
roleedited
roledeleted
roleremovedfromuser
useredited
Types
user
{
id: [user id:int],
name: [username:string],
nick: [nickname:string],
joinedTime: [account creation unix timestamp/1000:int],
state: [online state (0: offline, 1: online, 2: afk, 3: busy):int], // semi functional
status: [custom 'doing right now' message:string], // semi functional
aviHash: [hash to avi:bin],
messageCount: [amount of messages posted in total:int], // not functional
roles: [array of role ids that are assigned to the user:array(int)]
}
channel
{
id: [channel id:int],
name: [channel name:string],
description: [channel description:string],
type: [channel type (0: text, 1: category):int],
pId: [id of parent channel (category):int],
oId: [order id of channel, specifies position in its parent:int],
perms: [object mapping role ids to permission strings:object(roleid=>permission)]
}
role
{
id: [role id:int],
name: [role name:string],
color: [role color:string],
priority: [role priority (defines role order):int],
group: [group role in sidebar?:boolean],
globalPerms: [role global perms:string],
baseChannelPerms: [initial channel role perms (can be overriden by channel settings):string]
}
message
Info about markup:
The markup gets handeled by the client.
*message*: italic
**message**: bold
__message__: underlined
~~message~~: crossed out
(come up with something cool yourself if youre bored)
Oh btw if the user has HTML markup perms the client handles that too.
{
id: [message id:int],
content: [message text:string],
attachments: {
files: [ARRAY OF FILE OBJECTS],
embeds: [] // not functional
},
uId: [id of creator:int],
cId: [channel id:int],
time: [unix timestamp at the moment of posting:int],
pinned: [is the message pinned?:boolean],
edited: [is the message edited?:boolean],
editorid: [if edited this is the id of the editor, else null]
}
file
{
hash: [SHA256 FILE-HASH:bin],
name: [FULL FILENAME + EXTENSION:string],
contentType: [MIME TYPE OF FILE:string],
size: [SIZE IN BYTES:int(for now)],
width: [IMAGE WIDTH. ONLY FOR IMAGE FILES]
height: [IMAGE HEIGHT. ONLY FOR IMAGE FILES]
}
Channel Permstring
New: The first segment of permission strings now always has a leading 1
after being decoded to make it easier to handle.
Will prolly be removed when i stop being lazy
Permission string for permissions that can be overriden by channel specific permissions.
sent as a base32 string, more readable as base3. After a conversion to base3,
"0" equals undefined, "1" equals disallowed and "2" equals allowed.
All following segments are encoded as base32 aswell but meant to be decoded to base10.
They represent cooldowns, maximum upload sizes, etc.
Example of a decoded permission string:
[base3 number]/[base10 number]/[base10 number]
As you can see, segments are divided using slashes.
toggles: { // all toggles are in the same segment
// [PERM NAME]: [POSITION IN FIRST SEGMENT STRING]
READMSG: 0,
WRITEMSG: 1,
EDITOWN: 2,
EDITOTHER: 3,
DELETEOWN: 4,
DELETEOTHER: 5,
TOGGLEPIN: 6,
USEMARKUP: 7,
USEADVANCEDMARKUP: 8,
USEHTML: 9,
UPLOADFILE: 10,
ADDATTACHMENT: 11,
READHISTORY: 12,
MENTIONEVERYONE: 13,
MENTIONONLINE: 14,
USEEMOTE: 15,
ADDREACTION: 16,
ADDNEWREACTION: 17,
EDITCHANNELNAME: 18,
EDITCHANNELINFO: 19,
EDITCHANNELPERMS: 20,
EDITCHANNELPARENTID: 21,
EDITCHANNELORDERID: 22,
ADDCHANNEL: 23,
DELETECHANNEL: 24,
CLEARCHANNEL: 25
},
numbers: {
// [PERM NAME]: [POSITION IN SEGMENT ARRAY]
ATTACHMENTLIMIT: 1,
POSTTIMEOUT: 2
}
Global Permstring
Permission string for global permissions. Get defined once in the role and cant be overruled by anything other than higher priority roles. The base structure is the same as the channel perms just a little smaller cause less global perms exist atm.
toggles: {
EDITROLEPERMS: 0,
EDITROLENAME: 1,
EDITROLECOLOR: 2,
EDITROLEDISPLAYTYPE: 3,
EDITROLEPRIORITY: 4,
CREATEROLE: 5,
DELETEROLE: 6,
CHANGENICKS: 7,
CHANGEOWNNICK: 8,
CHANGEDISPLAYNAMES: 9,
CHANGEOWNDISPLAYNAME: 10,
CHANGEONLINESTATE: 11,
CHANGECURRENTLYDOING: 12,
SETAVI: 13,
ADDROLETOUSER: 14,
REMOVEROLEFROMUSER: 15,
ADDEMOTE: 16,
EDITEMOTE: 17,
DELETEEMOTE: 18,
VIEWLOGS: 19,
BAN: 20
},
numbers: {
UPLOADSIZELIMIT: 21,
BANCOOLDOWN: 22,
MAXBANTIME: 23
}
HTTP Content Delivery
serving files
https://files.dmc.chat/[HEX-FILEHASH]/[FILENAME]
Avis are publicly served via the following link structure:
https://files.dmc.chat/avi/[USERID]