Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

16 changed files with 77 additions and 77 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
node_modules node_modules
vybe.db celi.db

View File

@ -1,7 +1,7 @@
# NOTICE: this document is outdated # NOTICE: this document is outdated
# todo: update this document # todo: update this document
# vybe websocket protocol - sent by client # celi websocket protocol - sent by client
socket.io actions + expected msg format and other info socket.io actions + expected msg format and other info
@ -18,7 +18,7 @@ if a message fails, you get a response with the following format:
## authenticate ## authenticate
you must generate a random salt, sign a message `vybe_auth [salt]`, and then send you must generate a random salt, sign a message `celi_auth [salt]`, and then send
```json ```json
{ {

View File

@ -1,9 +1,9 @@
## vybe ## celi
vybe is a work-in-progress decentralized **communication network**. celi is a work-in-progress decentralized **communication network**.
a vybe instance (server) features user accounts and **threads**. a celi instance (server) features user accounts and **threads**.
user accounts are owned by PGP keys stored in users' clients. user accounts are owned by PGP keys stored in users' clients.
@ -30,7 +30,7 @@ after first run, the port is configured in instance.json.
**currently, you'll need to proxy it through a webserver like nginx or Caddy, which needs to have https enabled** for things to work correctly. if you want the easy option, i recommend Caddy. example Caddy config: **currently, you'll need to proxy it through a webserver like nginx or Caddy, which needs to have https enabled** for things to work correctly. if you want the easy option, i recommend Caddy. example Caddy config:
> ``` > ```
> vybe.example.domain { > celi.example.domain {
> reverse_proxy localhost:1312 > reverse_proxy localhost:1312
> header Access-Control-Allow-Origin "*" > header Access-Control-Allow-Origin "*"
> } > }
@ -38,7 +38,7 @@ after first run, the port is configured in instance.json.
(the allow-origin header is necessary for federation to work) (the allow-origin header is necessary for federation to work)
then go to `https://vybe.example.domain` to start using vybe! then go to `https://celi.example.domain` to start using celi!
## todo ## todo

View File

@ -219,7 +219,7 @@ document.body.append(html.node`
</div> </div>
<hr class='separator' color='#505050'> <hr class='separator' color='#505050'>
<div id='home' class='column'> <div id='home' class='column'>
<h3>vybe</h3> <h3>celi</h3>
<p id='instances' class='header'>instances:<button onclick=${e => { <p id='instances' class='header'>instances:<button onclick=${e => {
let div = html.node` let div = html.node`
<div> <div>

View File

@ -103,10 +103,10 @@ async function submit(event) {
render(document.body, html` render(document.body, html`
<div id='register' class='hidden'> <div id='register' class='hidden'>
<h1>welcome to vybe</h1> <h1>welcome to celi</h1>
<h3>a communication network</h3> <h3>a communication network</h3>
<p>choose a username for your account.</p> <p>choose a username for your account.</p>
<pre>vybe uses public key cryptography rather than passwords. <pre>celi uses public key cryptography rather than passwords.
your keys are stored in your browser's local storage only, so do this on a browser you can access again.</pre> your keys are stored in your browser's local storage only, so do this on a browser you can access again.</pre>
<p> <p>
if you already have an account, enter your username here if you already have an account, enter your username here

View File

@ -8,7 +8,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vybe</title> <title>celi</title>
<style> <style>
* { * {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",

View File

@ -1,5 +1,5 @@
{ {
"name": "vybe", "name": "celi",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "server.js", "main": "server.js",

View File

@ -46,7 +46,7 @@ if (!instance.url)
it will be auto-set by the first user authentication ! it will be auto-set by the first user authentication !
it can be manually set in instance.json .`); it can be manually set in instance.json .`);
global.vybe = { global.celi = {
instance, instance,
saveSettings, saveSettings,
instances: {}, instances: {},
@ -55,7 +55,7 @@ global.vybe = {
calls: {}, // rtc peer connections go in here calls: {}, // rtc peer connections go in here
streams: {}, streams: {},
connectInstance: async url => { connectInstance: async url => {
let instance = vybe.instances[url]; let instance = celi.instances[url];
function connecting(resolve, reject) { function connecting(resolve, reject) {
instance.socket.on('connect', resolve); instance.socket.on('connect', resolve);
instance.socket.on('connect_error', error => { instance.socket.on('connect_error', error => {
@ -70,7 +70,7 @@ global.vybe = {
} }
} }
else { else {
instance = vybe.instances[url] = { instance = celi.instances[url] = {
socket: ioclient('https://' + url) socket: ioclient('https://' + url)
}; };
await new Promise(connecting); await new Promise(connecting);
@ -88,7 +88,7 @@ app.use(express.static('client'));
// todo: secure this // todo: secure this
app.get('/stream/:id', (req, res) => { app.get('/stream/:id', (req, res) => {
let stream = vybe.streams[req.params.id]; let stream = celi.streams[req.params.id];
if (!stream) { if (!stream) {
res.sendStatus(404); res.sendStatus(404);
return; return;
@ -117,17 +117,17 @@ io.on('connection', (socket) => {
}); });
} }
socket.on('disconnect', async reason => { socket.on('disconnect', async reason => {
let user = vybe.users[socket.__userid]; let user = celi.users[socket.__userid];
if (user) if (user)
user.sockets.splice(user.sockets.indexOf(socket), 1); user.sockets.splice(user.sockets.indexOf(socket), 1);
for (let id in vybe.streams) { for (let id in celi.streams) {
const stream = vybe.streams[id]; const stream = celi.streams[id];
delete stream.listeners[socket.id]; delete stream.listeners[socket.id];
if (stream.socket === socket) if (stream.socket === socket)
stream.stop(); stream.stop();
} }
for (let id in vybe.calls) { for (let id in celi.calls) {
let call = vybe.calls[id]; let call = celi.calls[id];
let connection = call[socket.__userid]; let connection = call[socket.__userid];
if (!connection) if (!connection)
continue; continue;
@ -137,8 +137,8 @@ io.on('connection', (socket) => {
} }
connection.close(); connection.close();
delete call[id]; delete call[id];
delete vybe.threads[id].call[socket.__userid]; delete celi.threads[id].call[socket.__userid];
await vybe.threads[id].emitcall(); await celi.threads[id].emitcall();
} }
}); });
}); });

View File

@ -11,7 +11,7 @@ const authwrap = (fn) => async (msg, respond, socket) => {
} }
return await fn({ return await fn({
...msg, ...msg,
auth_user: vybe.users[socket.__userid] auth_user: celi.users[socket.__userid]
}, respond, socket); }, respond, socket);
}; };

View File

@ -1,7 +1,7 @@
const sqlite3 = require('sqlite3'); const sqlite3 = require('sqlite3');
const fs = require('fs'); const fs = require('fs');
const path = 'vybe.db'; const path = 'celi.db';
const existed = fs.existsSync(path); const existed = fs.existsSync(path);
const db = new sqlite3.Database(path); const db = new sqlite3.Database(path);

View File

@ -15,8 +15,8 @@ async function join(msg, respond, socket) {
success: false, success: false,
message: "user doesn't have permission" message: "user doesn't have permission"
}); });
const thread = vybe.threads[msg.thread]; const thread = celi.threads[msg.thread];
const call = vybe.calls[msg.thread]; const call = celi.calls[msg.thread];
let connection = call[msg.auth_user.id]; let connection = call[msg.auth_user.id];
if (connection) { if (connection) {
if (connection.track) { if (connection.track) {
@ -66,8 +66,8 @@ async function join(msg, respond, socket) {
connection.connected = true; connection.connected = true;
thread.call[msg.auth_user.id] = { thread.call[msg.auth_user.id] = {
id: msg.auth_user.id, id: msg.auth_user.id,
name: vybe.users[msg.auth_user.id].name, name: celi.users[msg.auth_user.id].name,
displayname: vybe.users[msg.auth_user.id].displayname, displayname: celi.users[msg.auth_user.id].displayname,
permissions: Object.fromEntries((await db.query( permissions: Object.fromEntries((await db.query(
`select permission, value from permission `select permission, value from permission
where type = 'user' and thread = ? and user = ?`, where type = 'user' and thread = ? and user = ?`,
@ -104,7 +104,7 @@ async function offer(msg, respond) {
success: false, success: false,
message: 'missing argument' message: 'missing argument'
}); });
let connection = vybe.calls[msg.thread]?.[msg.auth_user.id]; let connection = celi.calls[msg.thread]?.[msg.auth_user.id];
if (!connection) if (!connection)
return respond({ return respond({
success: false, success: false,
@ -124,7 +124,7 @@ async function ice(msg, respond) {
success: false, success: false,
message: 'missing argument' message: 'missing argument'
}); });
let connection = vybe.calls[msg.thread]?.[msg.auth_user.id]; let connection = celi.calls[msg.thread]?.[msg.auth_user.id];
if (!connection) if (!connection)
return respond({ return respond({
success: false, success: false,
@ -144,8 +144,8 @@ async function ice(msg, respond) {
} }
async function leave(msg, respond) { async function leave(msg, respond) {
let thread = vybe.threads[msg.thread]; let thread = celi.threads[msg.thread];
let call = vybe.calls[msg.thread]; let call = celi.calls[msg.thread];
let connection = call?.[msg.auth_user.id]; let connection = call?.[msg.auth_user.id];
if (!connection) if (!connection)
return respond({ return respond({

View File

@ -33,10 +33,10 @@ async function send_message(msg, respond) {
if (p.type === 'user' && p.user == msg.auth_user.id) if (p.type === 'user' && p.user == msg.auth_user.id)
userperms[p.permission] = ['admin', 'post', 'view'].includes(p.permission) userperms[p.permission] = ['admin', 'post', 'view'].includes(p.permission)
? p.value === 'true' : p.value; ? p.value === 'true' : p.value;
for (let id in vybe.users) { for (let id in celi.users) {
if (perms.find(p => p.type === 'everyone' && p.permission === 'view' && p.value === 'true') if (perms.find(p => p.type === 'everyone' && p.permission === 'view' && p.value === 'true')
|| members.includes(id)) { || members.includes(id)) {
for (let s of vybe.users[id].sockets) { for (let s of celi.users[id].sockets) {
s.emit('new_message', { s.emit('new_message', {
id, id,
user: { user: {

View File

@ -65,9 +65,9 @@ async function save_span(msg, respond, socket) {
and type = 'everyone' and value = 'true' and permission = 'view'`, and type = 'everyone' and value = 'true' and permission = 'view'`,
msg.thread msg.thread
); );
for (let userid in vybe.users) { for (let userid in celi.users) {
if (permissions.rows.length > 0 || members.includes(userid)) { if (permissions.rows.length > 0 || members.includes(userid)) {
for (let s of vybe.users[userid].sockets) { for (let s of celi.users[userid].sockets) {
if (s !== socket) if (s !== socket)
s.emit('span', { s.emit('span', {
id, id,

View File

@ -17,14 +17,14 @@ async function stream(msg, respond, socket) {
`select * from permission `select * from permission
where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'`, where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'`,
msg.thread)).rows.length) { msg.thread)).rows.length) {
for (let id in vybe.users) for (let id in celi.users)
for (let socket of vybe.users[id].sockets) for (let socket of celi.users[id].sockets)
socket.emit('stream', stream); socket.emit('stream', stream);
} else { } else {
for (let member of ( for (let member of (
await db.query('select user from member where member.thread = ?', msg.thread) await db.query('select user from member where member.thread = ?', msg.thread)
).rows) { ).rows) {
member = vybe.users[member.user]; member = celi.users[member.user];
if (member) if (member)
for (let socket of member.sockets) for (let socket of member.sockets)
socket.emit('stream', stream); socket.emit('stream', stream);
@ -32,7 +32,7 @@ async function stream(msg, respond, socket) {
} }
} }
if (typeof msg.id === 'number') { if (typeof msg.id === 'number') {
let vstream = vybe.streams[msg.id]; let vstream = celi.streams[msg.id];
if (!vstream) if (!vstream)
return respond({ return respond({
success: false, success: false,
@ -68,9 +68,9 @@ async function stream(msg, respond, socket) {
}, },
name: msg.name name: msg.name
}; };
let thread = vybe.threads[msg.thread]; let thread = celi.threads[msg.thread];
thread.streams.push(stream); thread.streams.push(stream);
vybe.streams[stream.id] = { celi.streams[stream.id] = {
stream, stream,
userid: msg.auth_user.id, userid: msg.auth_user.id,
listeners: {}, listeners: {},
@ -78,7 +78,7 @@ async function stream(msg, respond, socket) {
stop: async () => { stop: async () => {
stream.stopped = true; stream.stopped = true;
thread.streams.splice(thread.streams.findIndex(s => s.id === stream.id), 1); thread.streams.splice(thread.streams.findIndex(s => s.id === stream.id), 1);
delete vybe.streams[stream.id]; delete celi.streams[stream.id];
await send(); await send();
}, },
streams: new Set() streams: new Set()
@ -92,7 +92,7 @@ async function stream(msg, respond, socket) {
} }
async function streamdata(msg, respond) { async function streamdata(msg, respond) {
let stream = vybe.streams[msg.id]; let stream = celi.streams[msg.id];
if (!stream) { if (!stream) {
return respond({ return respond({
success: false, success: false,
@ -119,7 +119,7 @@ async function play_stream(msg, respond, socket) {
message: "user doesn't have permission" message: "user doesn't have permission"
}); });
} }
let stream = vybe.streams[msg.id]; let stream = celi.streams[msg.id];
if (!stream) if (!stream)
return respond({ return respond({
success: false, success: false,

View File

@ -6,7 +6,7 @@ db.ready.then(async () => {
for (let thread of (await db.query( for (let thread of (await db.query(
`select name, id from thread` `select name, id from thread`
)).rows) { )).rows) {
vybe.threads[thread.id] = { celi.threads[thread.id] = {
...thread, ...thread,
streams: [], streams: [],
call: {}, // list of members in call call: {}, // list of members in call
@ -19,14 +19,14 @@ db.ready.then(async () => {
`select * from permission `select * from permission
where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'`, where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'`,
this.id)).rows.length) { this.id)).rows.length) {
for (let id in vybe.users) for (let id in celi.users)
for (let socket of vybe.users[id].sockets) for (let socket of celi.users[id].sockets)
socket.emit('call', msg); socket.emit('call', msg);
} else { } else {
for (let member of ( for (let member of (
await db.query('select user from member where member.thread = ?', this.id) await db.query('select user from member where member.thread = ?', this.id)
).rows) { ).rows) {
member = vybe.users[member.user]; member = celi.users[member.user];
if (member) if (member)
for (let socket of member.sockets) for (let socket of member.sockets)
socket.emit('call', msg); socket.emit('call', msg);
@ -34,7 +34,7 @@ db.ready.then(async () => {
} }
} }
}; };
vybe.calls[thread.id] = {}; celi.calls[thread.id] = {};
} }
}); });
@ -101,7 +101,7 @@ async function create_thread(msg, respond) {
let members = {}; let members = {};
for (let member of msg.members) { for (let member of msg.members) {
let id = member.id.split('@'); let id = member.id.split('@');
id = id[1] === vybe.instance.url ? id[0] : member.id; id = id[1] === celi.instance.url ? id[0] : member.id;
if (members[id]) if (members[id])
continue; continue;
await db.query( await db.query(
@ -116,16 +116,16 @@ async function create_thread(msg, respond) {
values (?, ?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?, ?)`,
[thread_id, 'user', id, true, permission, String(member.permissions[permission])]); [thread_id, 'user', id, true, permission, String(member.permissions[permission])]);
} }
let thread = vybe.threads[thread_id] = { let thread = celi.threads[thread_id] = {
id: thread_id, id: thread_id,
name: msg.name, name: msg.name,
streams: [], streams: [],
call: {} call: {}
}; };
vybe.calls[thread_id] = {}; celi.calls[thread_id] = {};
if (!msg.permissions?.view_limited) { if (!msg.permissions?.view_limited) {
for (let id in vybe.users) { for (let id in celi.users) {
for (let socket of vybe.users[id].sockets) { for (let socket of celi.users[id].sockets) {
socket.emit('thread', { socket.emit('thread', {
...thread, ...thread,
permissions: { permissions: {
@ -140,7 +140,7 @@ async function create_thread(msg, respond) {
} }
else { else {
for (let member in members) { for (let member in members) {
member = vybe.users[member]; member = celi.users[member];
if (!member) if (!member)
continue; continue;
for (let socket of member.sockets) { for (let socket of member.sockets) {
@ -181,7 +181,7 @@ async function list_threads(msg, respond) {
success: true, success: true,
threads: await Promise.all(threads.rows.map(async row => { threads: await Promise.all(threads.rows.map(async row => {
let thread = {}; let thread = {};
Object.assign(thread, vybe.threads[row.id]); Object.assign(thread, celi.threads[row.id]);
thread.permissions = await check_permission(msg.auth_user.id, row.id); thread.permissions = await check_permission(msg.auth_user.id, row.id);
thread.permissions.public = row.value === 'true'; thread.permissions.public = row.value === 'true';
return thread; return thread;
@ -249,7 +249,7 @@ async function get_thread(msg, respond) {
return respond({ return respond({
success: true, success: true,
thread: { thread: {
...vybe.threads[msg.thread], ...celi.threads[msg.thread],
permissions: perms, permissions: perms,
members: Object.values(members) members: Object.values(members)
} }
@ -277,7 +277,7 @@ async function edit_thread(msg, respond) {
message: "user doesn't have permission" message: "user doesn't have permission"
}); });
} }
let thread = vybe.threads[msg.id]; let thread = celi.threads[msg.id];
// update name // update name
await db.query( await db.query(
'update thread set name = ? where id = ?', 'update thread set name = ? where id = ?',
@ -367,8 +367,8 @@ async function edit_thread(msg, respond) {
msg.id msg.id
)).rows.map(row => [row.user, true])); )).rows.map(row => [row.user, true]));
if (!msg.permissions?.view_limited) { if (!msg.permissions?.view_limited) {
for (let id in vybe.users) for (let id in celi.users)
for (let socket of vybe.users[id].sockets) for (let socket of celi.users[id].sockets)
socket.emit('thread', { socket.emit('thread', {
...thread, ...thread,
permissions: { permissions: {
@ -382,7 +382,7 @@ async function edit_thread(msg, respond) {
} }
else { else {
for (let member in members) { for (let member in members) {
member = vybe.users[member]; member = celi.users[member];
if (!member) if (!member)
continue; continue;
for (let socket of member.sockets) for (let socket of member.sockets)

View File

@ -88,7 +88,7 @@ async function authenticate(msg, respond, socket) {
expectSigned: true, expectSigned: true,
date: new Date(Date.now() + 60000 * 4) // slightly in the future to compensate for some system clocks date: new Date(Date.now() + 60000 * 4) // slightly in the future to compensate for some system clocks
}); });
if (vybe.instance.url === id[1] || (!vybe.instance.url && !id[0])) { if (celi.instance.url === id[1] || (!celi.instance.url && !id[0])) {
// this should be user's home instance // this should be user's home instance
user = await getUser(id[0], msg.name); user = await getUser(id[0], msg.name);
if (!user) { if (!user) {
@ -105,13 +105,13 @@ async function authenticate(msg, respond, socket) {
); );
if (result.rows.length === 0) { if (result.rows.length === 0) {
// request auth from logged in sessions // request auth from logged in sessions
if (!vybe.users[user.id]) if (!celi.users[user.id])
vybe.users[user.id] = { celi.users[user.id] = {
...user, ...user,
sockets: [], sockets: [],
authrequests: {} authrequests: {}
}; };
user = vybe.users[user.id]; user = celi.users[user.id];
let id = key.getFingerprint().slice(0, 8); let id = key.getFingerprint().slice(0, 8);
let time = Date.now(); let time = Date.now();
if (!await new Promise(resolve => { if (!await new Promise(resolve => {
@ -132,15 +132,15 @@ async function authenticate(msg, respond, socket) {
[user.id, pubkey]); [user.id, pubkey]);
} }
// default instance url to first authenticated user's location.host // default instance url to first authenticated user's location.host
if (!vybe.instance.url) { if (!celi.instance.url) {
vybe.instance.url = id[1]; celi.instance.url = id[1];
vybe.saveSettings(); celi.saveSettings();
} }
} }
else { else {
// connect to user's home instance and ask for their key // connect to user's home instance and ask for their key
try { try {
let instance = await vybe.connectInstance(id[1]); let instance = await celi.connectInstance(id[1]);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
instance.socket.emit('get_keys', { ids: [id[0]] }, msg => { instance.socket.emit('get_keys', { ids: [id[0]] }, msg => {
if (!msg.success) { if (!msg.success) {
@ -167,7 +167,7 @@ async function authenticate(msg, respond, socket) {
// this socket is now authenticated // this socket is now authenticated
if (!user.displayname) if (!user.displayname)
user.displayname = user.name; user.displayname = user.name;
user = vybe.users[user.id] || (vybe.users[user.id] = { user = celi.users[user.id] || (celi.users[user.id] = {
...user, ...user,
sockets: [], sockets: [],
authrequests: {} authrequests: {}
@ -178,7 +178,7 @@ async function authenticate(msg, respond, socket) {
respond({ respond({
success: true, success: true,
instance: { instance: {
id: vybe.instance.id id: celi.instance.id
}, },
id: user.id, id: user.id,
name: user.name, name: user.name,
@ -201,7 +201,7 @@ async function authenticate(msg, respond, socket) {
} }
async function authorize_key(msg, respond) { async function authorize_key(msg, respond) {
let authrequest = vybe.users[msg.auth_user.id].authrequests[msg.id]; let authrequest = celi.users[msg.auth_user.id].authrequests[msg.id];
if (!authrequest) { if (!authrequest) {
return respond({ return respond({
success: false, success: false,
@ -222,8 +222,8 @@ async function update_user(msg, respond) {
await db.query( await db.query(
`update user set displayname = ?, bio = ?, public = ? where id = ?`, `update user set displayname = ?, bio = ?, public = ? where id = ?`,
[msg.displayname, msg.bio, !!msg.public, msg.auth_user.id]); [msg.displayname, msg.bio, !!msg.public, msg.auth_user.id]);
vybe.users[msg.auth_user.id].displayname = msg.displayname; celi.users[msg.auth_user.id].displayname = msg.displayname;
vybe.users[msg.auth_user.id].bio = msg.bio; celi.users[msg.auth_user.id].bio = msg.bio;
respond({ success: true }); respond({ success: true });
} }