diff --git a/Cargo.lock b/Cargo.lock
index d10c57f0d75f0f700fd6c81fa06721b21ddb55a4..dca41144e8ef5f990c93b8c0096d513a77fdfd91 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -835,6 +835,19 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
 
+[[package]]
+name = "hive_pubsub"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2b08d5028b638db3f5eb475dc7c5e5aad1b0a5b4f737b2c394883034330853e"
+dependencies = [
+ "many-to-many",
+ "mongodb",
+ "serde",
+ "serde_json",
+ "ulid",
+]
+
 [[package]]
 name = "hmac"
 version = "0.7.1"
@@ -1218,6 +1231,12 @@ dependencies = [
  "linked-hash-map",
 ]
 
+[[package]]
+name = "many-to-many"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28806b11671f3ae32bfcacf1948d77af5bddf1aa4ac518207615f11abe31df19"
+
 [[package]]
 name = "match_cfg"
 version = "0.1.0"
@@ -1326,9 +1345,9 @@ dependencies = [
 
 [[package]]
 name = "mongodb"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cb54f85e6380c8cfe130a55a85cfc2584e8317f4bb8bda464d367fd4f24957"
+checksum = "a726495d7418c4579ecc9465b53ddade8187c0299e5db0e7250672b67d196913"
 dependencies = [
  "async-std",
  "async-trait",
@@ -2031,7 +2050,7 @@ dependencies = [
 
 [[package]]
 name = "revolt"
-version = "0.2.10"
+version = "0.2.11"
 dependencies = [
  "bcrypt",
  "bitfield",
@@ -2039,10 +2058,12 @@ dependencies = [
  "dotenv",
  "env_logger",
  "hashbrown",
+ "hive_pubsub",
  "lazy_static",
  "lettre",
  "log 0.4.11",
  "lru",
+ "many-to-many",
  "mongodb",
  "num_enum",
  "once_cell",
diff --git a/Cargo.toml b/Cargo.toml
index c08634cbcc2df5685e01c3e115edcfb5afdb01f8..5016d6a6de26b22b2216cfaa775e55d11c1e78fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,13 @@
 [package]
 name = "revolt"
-version = "0.2.10"
+version = "0.2.11"
 authors = ["Paul Makles <paulmakles@gmail.com>"]
 edition = "2018"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-mongodb = { version = "1.1.0", default-features = false, features = ["sync"] } # FIXME: rewrite database with async API
+mongodb = { version = "1.1.1", default-features = false, features = ["sync"] } # FIXME: rewrite database with async API
 rocket = { version = "0.4.5", default-features = false }
 once_cell = "1.4.1"
 dotenv = "0.15.0"
@@ -31,3 +31,5 @@ lazy_static = "1.4.0"
 log = "0.4.11"
 env_logger = "0.7.1"
 lettre = "0.10.0-alpha.1"
+hive_pubsub = { version = "0.3.1", features = ["mongo"] }
+many-to-many = "0.1.2"
diff --git a/src/database/guild.rs b/src/database/guild.rs
index a7a90dbed228e5d6c1700c7cf5c353e9f2b97f9e..3edde9c42adf9e459756f79a9b2d8b4e5bd943ea 100644
--- a/src/database/guild.rs
+++ b/src/database/guild.rs
@@ -107,6 +107,7 @@ pub fn fetch_guild(id: &str) -> Result<Option<Guild>, String> {
     let col = get_collection("guilds");
     if let Ok(result) = col.find_one(doc! { "_id": id }, None) {
         if let Some(doc) = result {
+            dbg!(doc.to_string());
             if let Ok(guild) = from_bson(Bson::Document(doc)) as Result<Guild, _> {
                 let mut cache = CACHE.lock().unwrap();
                 cache.put(id.to_string(), guild.clone());
@@ -152,6 +153,7 @@ pub fn fetch_guilds(ids: &Vec<String>) -> Result<Vec<Guild>, String> {
         for item in result {
             let mut cache = CACHE.lock().unwrap();
             if let Ok(doc) = item {
+                dbg!(doc.to_string());
                 if let Ok(guild) = from_bson(Bson::Document(doc)) as Result<Guild, _> {
                     cache.put(guild.id.clone(), guild.clone());
                     guilds.push(guild);
diff --git a/src/database/message.rs b/src/database/message.rs
index f20d061a9ee6ed8469b9158d853f049cd1c1c918..688365ff41267e787b72a580f1673a8f10137797 100644
--- a/src/database/message.rs
+++ b/src/database/message.rs
@@ -3,6 +3,7 @@ use crate::database::channel::Channel;
 use crate::notifications;
 use crate::notifications::events::message::Create;
 use crate::notifications::events::Notification;
+use crate::pubsub::hive;
 use crate::routes::channel::ChannelType;
 
 use mongodb::bson::from_bson;
@@ -11,6 +12,8 @@ use rocket::http::RawStr;
 use rocket::request::FromParam;
 use serde::{Deserialize, Serialize};
 
+use log::warn;
+
 #[derive(Serialize, Deserialize, Debug)]
 pub struct PreviousEntry {
     pub content: String,
@@ -51,6 +54,23 @@ impl Message {
                 &target,
             );
 
+            if hive::publish(
+                &target.id,
+                crate::pubsub::events::Notification::message_create(
+                    crate::pubsub::events::message::Create {
+                        id: self.id.clone(),
+                        nonce: self.nonce.clone(),
+                        channel: self.channel.clone(),
+                        author: self.author.clone(),
+                        content: self.content.clone(),
+                    },
+                ),
+            )
+            .is_err()
+            {
+                warn!("Saved message but couldn't send notification.");
+            }
+
             let short_content: String = self.content.chars().take(24).collect();
 
             // !! this stuff can be async
diff --git a/src/main.rs b/src/main.rs
index eb2307ae8987d5106fc35e0379d60437e6fd1978..d47bb69aa8b453dee8eab0b185d1aada400e222b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,33 +11,38 @@ extern crate lazy_static;
 
 pub mod database;
 pub mod notifications;
+pub mod pubsub;
 pub mod routes;
 pub mod util;
 
+use log::info;
 use rocket_cors::AllowedOrigins;
 use std::thread;
-use log::info;
 
 fn main() {
     dotenv::dotenv().ok();
-    env_logger::init_from_env(
-        env_logger::Env::default()
-            .filter_or("RUST_LOG", "info")
-    );
+    env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info"));
 
     info!("Starting REVOLT server.");
 
     util::variables::preflight_checks();
     database::connect();
+
+    // ! START OLD NOTIF CODE
     notifications::start_worker();
 
     thread::spawn(|| {
         notifications::pubsub::launch_subscriber();
     });
 
-    thread::spawn(|| {
+    notifications::state::init();
+    /*thread::spawn(|| {
         notifications::ws::launch_server();
-    });
+    });*/
+    // ! END OLD NOTIF CODE
+
+    pubsub::hive::init_hive();
+    pubsub::websocket::launch_server();
 
     let cors = rocket_cors::CorsOptions {
         allowed_origins: AllowedOrigins::All,
diff --git a/src/notifications/state.rs b/src/notifications/state.rs
index 9b063ec4178408470b760caa9f8d304bfec891ca..cfa1a3f2552c5af91e20c3d34e98b77a052b2b4e 100644
--- a/src/notifications/state.rs
+++ b/src/notifications/state.rs
@@ -14,17 +14,15 @@ pub enum StateResult {
     Success(String),
 }
 
-static mut CONNECTIONS: OnceCell<RwLock<HashMap<String, Sender>>> = OnceCell::new();
+static CONNECTIONS: OnceCell<RwLock<HashMap<String, Sender>>> = OnceCell::new();
 
 pub fn add_connection(id: String, sender: Sender) {
-    unsafe {
-        CONNECTIONS
-            .get()
-            .unwrap()
-            .write()
-            .unwrap()
-            .insert(id, sender);
-    }
+    CONNECTIONS
+        .get()
+        .unwrap()
+        .write()
+        .unwrap()
+        .insert(id, sender);
 }
 
 pub struct User {
@@ -148,33 +146,29 @@ impl GlobalState {
             }
         }
 
-        unsafe {
-            CONNECTIONS
-                .get()
-                .unwrap()
-                .write()
-                .unwrap()
-                .remove(&connection);
-        }
+        CONNECTIONS
+            .get()
+            .unwrap()
+            .write()
+            .unwrap()
+            .remove(&connection);
     }
 }
 
-pub static mut DATA: OnceCell<RwLock<GlobalState>> = OnceCell::new();
+pub static DATA: OnceCell<RwLock<GlobalState>> = OnceCell::new();
 
 pub fn init() {
-    unsafe {
-        if CONNECTIONS.set(RwLock::new(HashMap::new())).is_err() {
-            panic!("Failed to set global connections map.");
-        }
+    if CONNECTIONS.set(RwLock::new(HashMap::new())).is_err() {
+        panic!("Failed to set global connections map.");
+    }
 
-        if DATA.set(RwLock::new(GlobalState::new())).is_err() {
-            panic!("Failed to set global state.");
-        }
+    if DATA.set(RwLock::new(GlobalState::new())).is_err() {
+        panic!("Failed to set global state.");
     }
 }
 
 pub fn send_message(users: Option<Vec<String>>, guild: Option<String>, data: String) {
-    let state = unsafe { DATA.get().unwrap().read().unwrap() };
+    let state = DATA.get().unwrap().read().unwrap();
     let mut connections = HashSet::new();
 
     let mut users = vec_to_set(&users.unwrap_or(vec![]));
@@ -194,7 +188,7 @@ pub fn send_message(users: Option<Vec<String>>, guild: Option<String>, data: Str
         }
     }
 
-    let targets = unsafe { CONNECTIONS.get().unwrap().read().unwrap() };
+    let targets = CONNECTIONS.get().unwrap().read().unwrap();
     for conn in connections {
         if let Some(sender) = targets.get(&conn) {
             if sender.send(data.clone()).is_err() {
diff --git a/src/pubsub/events/groups.rs b/src/pubsub/events/groups.rs
new file mode 100644
index 0000000000000000000000000000000000000000..876f13d163faa0a0b8545a6196301038926fb064
--- /dev/null
+++ b/src/pubsub/events/groups.rs
@@ -0,0 +1,13 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct UserJoin {
+    pub id: String,
+    pub user: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct UserLeave {
+    pub id: String,
+    pub user: String,
+}
diff --git a/src/pubsub/events/guilds.rs b/src/pubsub/events/guilds.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3b1b95d2052ca750b17aa1f89d6b7151e8c31acb
--- /dev/null
+++ b/src/pubsub/events/guilds.rs
@@ -0,0 +1,33 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct UserJoin {
+    pub id: String,
+    pub user: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct UserLeave {
+    pub id: String,
+    pub user: String,
+    pub banned: bool,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct ChannelCreate {
+    pub id: String,
+    pub channel: String,
+    pub name: String,
+    pub description: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct ChannelDelete {
+    pub id: String,
+    pub channel: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Delete {
+    pub id: String,
+}
diff --git a/src/pubsub/events/message.rs b/src/pubsub/events/message.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ffec878984282a769efff93bee04a98c3ddea214
--- /dev/null
+++ b/src/pubsub/events/message.rs
@@ -0,0 +1,23 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Create {
+    pub id: String,
+    pub nonce: Option<String>,
+    pub channel: String,
+    pub author: String,
+    pub content: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Edit {
+    pub id: String,
+    pub channel: String,
+    pub author: String,
+    pub content: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Delete {
+    pub id: String,
+}
diff --git a/src/pubsub/events/mod.rs b/src/pubsub/events/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..597502b3f8102f8af090f335f5bfe0336f494c64
--- /dev/null
+++ b/src/pubsub/events/mod.rs
@@ -0,0 +1,31 @@
+use serde::{Deserialize, Serialize};
+
+pub mod groups;
+pub mod guilds;
+pub mod message;
+pub mod users;
+
+#[allow(non_camel_case_types)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(tag = "type", content = "data")]
+pub enum Notification {
+    message_create(message::Create),
+    message_edit(message::Edit),
+    message_delete(message::Delete),
+    group_user_join(groups::UserJoin),
+    group_user_leave(groups::UserLeave),
+    guild_user_join(guilds::UserJoin),
+    guild_user_leave(guilds::UserLeave),
+    guild_channel_create(guilds::ChannelCreate),
+    guild_channel_delete(guilds::ChannelDelete),
+    guild_delete(guilds::Delete),
+    user_friend_status(users::FriendStatus),
+}
+
+impl Notification {    
+    pub fn push_to_cache(&self) {
+        //crate::database::channel::process_event(&self);
+        //crate::database::guild::process_event(&self);
+        //crate::database::user::process_event(&self);
+    }
+}
diff --git a/src/pubsub/events/users.rs b/src/pubsub/events/users.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7a23446e295c3e76534c9a69a1a1d31b9436ae67
--- /dev/null
+++ b/src/pubsub/events/users.rs
@@ -0,0 +1,8 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct FriendStatus {
+    pub id: String,
+    pub user: String,
+    pub status: i32,
+}
diff --git a/src/pubsub/hive.rs b/src/pubsub/hive.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c157baa4569c9d7fdd1f831ba0e4e3841cee38d5
--- /dev/null
+++ b/src/pubsub/hive.rs
@@ -0,0 +1,61 @@
+use super::events::Notification;
+use super::websocket;
+use crate::database::get_collection;
+
+use hive_pubsub::backend::mongo::{listen_thread, MongodbPubSub};
+use hive_pubsub::PubSub;
+use once_cell::sync::OnceCell;
+use serde_json::to_string;
+use log::{error, debug};
+
+static HIVE: OnceCell<MongodbPubSub<String, String, Notification>> = OnceCell::new();
+
+pub fn init_hive() {
+    let hive = MongodbPubSub::new(
+        |ids, notification| {
+            if let Ok(data) = to_string(&notification) {
+                debug!("Pushing out notification. {}", data);
+                if let Err(err) = websocket::publish(ids, data) {
+                    error!("Failed to publish notification through WebSocket! {}", err);
+                }
+            } else {
+                error!("Failed to serialise notification.");
+            }
+        },
+        get_collection("hive"),
+    );
+
+    listen_thread(hive.clone());
+
+    if HIVE.set(hive).is_err() {
+        panic!("Failed to set global pubsub instance.");
+    }
+}
+
+pub fn publish(topic: &String, data: Notification) -> Result<(), String> {
+    let hive = HIVE.get().expect("Global pubsub instance not available.");
+    hive.publish(topic, data)
+}
+
+pub fn subscribe(user: String, topics: Vec<String>) -> Result<(), String> {
+    let hive = HIVE.get().expect("Global pubsub instance not available.");
+    for topic in topics {
+        hive.subscribe(user.clone(), topic)?;
+    }
+
+    Ok(())
+}
+
+pub fn drop_user(user: &String) -> Result<(), String> {
+    let hive = HIVE.get().expect("Global pubsub instance not available.");
+    hive.drop_client(user)?;
+
+    Ok(())
+}
+
+pub fn drop_topic(topic: &String) -> Result<(), String> {
+    let hive = HIVE.get().expect("Global pubsub instance not available.");
+    hive.drop_topic(topic)?;
+
+    Ok(())
+}
diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..23abbfd52dd5c810e66c3b4c87e2c6ec3b29d821
--- /dev/null
+++ b/src/pubsub/mod.rs
@@ -0,0 +1,3 @@
+pub mod events;
+pub mod hive;
+pub mod websocket;
diff --git a/src/pubsub/websocket/client.rs b/src/pubsub/websocket/client.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2a491704bf13c9ca3c7879ba24fe9e24711ff1a3
--- /dev/null
+++ b/src/pubsub/websocket/client.rs
@@ -0,0 +1,199 @@
+use super::state;
+use crate::database::get_collection;
+use crate::pubsub::hive;
+
+use log::{error, info};
+use mongodb::bson::doc;
+use mongodb::options::FindOneOptions;
+use serde_json::{from_str, json, Value};
+use ulid::Ulid;
+use ws::{CloseCode, Error, Handler, Handshake, Message, Result, Sender};
+
+pub struct Client {
+    id: String,
+    sender: Sender,
+    user_id: Option<String>,
+}
+
+impl Client {
+    pub fn new(sender: Sender) -> Client {
+        Client {
+            id: Ulid::new().to_string(),
+            user_id: None,
+            sender,
+        }
+    }
+}
+
+impl Handler for Client {
+    fn on_open(&mut self, handshake: Handshake) -> Result<()> {
+        info!("Client connected. [{}] {:?}", self.id, handshake.peer_addr);
+
+        Ok(())
+    }
+
+    // Client sends { "type": "authenticate", "token": token }.
+    // Receives { "type": "authorised" } and waits.
+    // Client then receives { "type": "ready", "data": payload }.
+    // If at any point we hit an error, send { "type": "error", "error": error }.
+    fn on_message(&mut self, msg: Message) -> Result<()> {
+        if let Message::Text(text) = msg {
+            if let Ok(data) = from_str(&text) as std::result::Result<Value, _> {
+                if let Value::String(packet_type) = &data["type"] {
+                    if packet_type == "authenticate" {
+                        if self.user_id.is_some() {
+                            return self.sender.send(
+                                json!({
+                                    "type": "error",
+                                    "error": "Already authenticated!"
+                                })
+                                .to_string(),
+                            );
+                        } else if let Value::String(token) = &data["token"] {
+                            let user = get_collection("users").find_one(
+                                doc! {
+                                    "access_token": token
+                                },
+                                FindOneOptions::builder()
+                                    .projection(doc! { "_id": 1 })
+                                    .build(),
+                            );
+
+                            if let Ok(result) = user {
+                                if let Some(doc) = result {
+                                    self.sender.send(
+                                        json!({
+                                            "type": "authorised"
+                                        })
+                                        .to_string(),
+                                    )?;
+
+                                    // FIXME: fetch above when we switch to new token system
+                                    // or auth cache system, something like that
+                                    let user = crate::database::user::fetch_user(
+                                        doc.get_str("_id").unwrap(),
+                                    )
+                                    .unwrap()
+                                    .unwrap(); // this should be guranteed, I think, maybe? I'm getting rid of it later. FIXME
+
+                                    self.user_id = Some(user.id.clone());
+
+                                    match user.create_payload() {
+                                        Ok(payload) => {
+                                            // ! Grab the ids from the payload,
+                                            // ! there's probably a better way to
+                                            // ! do this. I'll rewrite it at some point.
+                                            let mut ids = vec![
+                                                self.user_id.as_ref().unwrap().clone()
+                                            ];
+
+                                            {
+                                                // This is bad code. But to be fair
+                                                // it should work just fine.
+                                                for user in payload.get("users").unwrap().as_array().unwrap() {
+                                                    ids.push(user.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string());
+                                                }
+
+                                                for channel in payload.get("channels").unwrap().as_array().unwrap() {
+                                                    ids.push(channel.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string());
+                                                }
+
+                                                for guild in payload.get("guilds").unwrap().as_array().unwrap() {
+                                                    ids.push(guild.as_object().unwrap().get("id").unwrap().as_str().unwrap().to_string());
+                                                }
+                                            }
+
+                                            if let Err(err) = hive::subscribe(self.user_id.as_ref().unwrap().clone(), ids) {
+                                                self.sender.send(
+                                                    json!({
+                                                        "type": "warn",
+                                                        "error": "Failed to subscribe you to the Hive. You may not receive all notifications."
+                                                    })
+                                                    .to_string(),
+                                                )?;
+                                            }
+
+                                            self.sender.send(
+                                                json!({
+                                                    "type": "ready",
+                                                    "data": payload
+                                                })
+                                                .to_string(),
+                                            )?;
+
+                                            if state::accept(
+                                                self.id.clone(),
+                                                self.user_id.as_ref().unwrap().clone(),
+                                                self.sender.clone(),
+                                            )
+                                            .is_err()
+                                            {
+                                                self.sender.send(
+                                                    json!({
+                                                        "type": "warn",
+                                                        "error": "Failed to accept your connection. You will not receive any notifications."
+                                                    })
+                                                    .to_string(),
+                                                )?;
+                                            }
+                                        }
+                                        Err(error) => {
+                                            error!("Failed to create payload! {}", error);
+                                            self.sender.send(
+                                                json!({
+                                                    "type": "error",
+                                                    "error": "Failed to create payload."
+                                                })
+                                                .to_string(),
+                                            )?;
+                                        }
+                                    }
+                                } else {
+                                    self.sender.send(
+                                        json!({
+                                            "type": "error",
+                                            "error": "Invalid token."
+                                        })
+                                        .to_string(),
+                                    )?;
+                                }
+                            } else {
+                                self.sender.send(
+                                    json!({
+                                        "type": "error",
+                                        "error": "Failed to fetch from database."
+                                    })
+                                    .to_string(),
+                                )?;
+                            }
+                        } else {
+                            self.sender.send(
+                                json!({
+                                    "type": "error",
+                                    "error": "Missing token."
+                                })
+                                .to_string(),
+                            )?;
+                        }
+                    }
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    fn on_close(&mut self, _code: CloseCode, reason: &str) {
+        info!("Client disconnected. [{}] {}", self.id, reason);
+        if let Err(error) = state::drop(&self.id) {
+            error!("Also failed to drop client from state! {}", error);
+        }
+    }
+
+    fn on_error(&mut self, err: Error) {
+        error!(
+            "A client disconnected due to an error. [{}] {}",
+            self.id, err
+        );
+    }
+}
diff --git a/src/pubsub/websocket/mod.rs b/src/pubsub/websocket/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e161253ea783e1404995fb139ddaae82e94e58ec
--- /dev/null
+++ b/src/pubsub/websocket/mod.rs
@@ -0,0 +1,23 @@
+use crate::util::variables::WS_HOST;
+
+use log::{error, info};
+use std::thread;
+use ws::listen;
+
+mod client;
+mod state;
+
+pub use state::publish;
+
+pub fn launch_server() {
+    thread::spawn(|| {
+        if listen(WS_HOST.to_string(), |sender| client::Client::new(sender)).is_err() {
+            error!(
+                "Failed to listen for WebSocket connections on {:?}!",
+                *WS_HOST
+            );
+        } else {
+            info!("Listening for WebSocket connections on {:?}", *WS_HOST);
+        }
+    });
+}
diff --git a/src/pubsub/websocket/state.rs b/src/pubsub/websocket/state.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ec02d9fedf29b5e4541faad90516377f8711f74c
--- /dev/null
+++ b/src/pubsub/websocket/state.rs
@@ -0,0 +1,89 @@
+use crate::pubsub::hive;
+
+use many_to_many::ManyToMany;
+use std::collections::HashMap;
+use std::sync::{Arc, RwLock};
+use log::{error, info};
+use ws::Sender;
+
+lazy_static! {
+    static ref CONNECTIONS: Arc<RwLock<HashMap<String, Sender>>> =
+        Arc::new(RwLock::new(HashMap::new()));
+    static ref CLIENTS: Arc<RwLock<ManyToMany<String, String>>> =
+        Arc::new(RwLock::new(ManyToMany::new()));
+}
+
+pub fn accept(id: String, user_id: String, sender: Sender) -> Result<(), String> {
+    let mut conns = CONNECTIONS
+        .write()
+        .map_err(|_| "Failed to lock connections for writing.")?;
+    
+    conns.insert(id.clone(), sender);
+
+    let mut clients = CLIENTS
+        .write()
+        .map_err(|_| "Failed to lock clients for writing.")?;
+    
+    clients.insert(user_id.clone(), id.clone());
+
+    info!("Accepted user [{}] for connection {}.", user_id, id);
+    Ok(())
+}
+
+pub fn drop(id: &String) -> Result<(), String> {
+    let mut conns = CONNECTIONS
+        .write()
+        .map_err(|_| "Failed to lock connections for writing.")?;
+    
+    conns.remove(id);
+
+    let mut clients = CLIENTS
+        .write()
+        .map_err(|_| "Failed to lock clients for writing.")?;
+    
+    let uid = if let Some(ids) = clients.get_right(id) {
+        let user_id: String = ids.into_iter().next().unwrap();
+        info!("Dropped user [{}] for connection {}.", user_id, id);
+        Some(user_id)
+    } else {
+        None
+    };
+
+    clients.remove_right(id);
+
+    if let Some(user_id) = &uid {
+        if let None = clients.get_left(user_id) {
+            if let Err(error) = hive::drop_user(user_id) {
+                error!("Failed to drop user from hive! {}", error);
+            } else {
+                info!("User [{}] has completed disconnected from node.", user_id);
+            }
+        }
+    }
+
+    Ok(())
+}
+
+pub fn publish(clients: Vec<String>, data: String) -> Result<(), String> {
+    let conns = CONNECTIONS
+        .read()
+        .map_err(|_| "Failed to lock connections for reading.")?;
+
+    let client_map = CLIENTS
+        .read()
+        .map_err(|_| "Failed to lock clients for reading.")?;
+    
+    for client in clients {
+        if let Some(targets) = client_map.get_left(&client) {
+            for target in &targets {
+                if let Some(connection) = conns.get(target) {
+                    if let Err(err) = connection.send(data.clone()) {
+                        error!("Failed to publish notification to client [{}]! {}", target, err);
+                    }
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
diff --git a/src/routes/account.rs b/src/routes/account.rs
index 01c565e18210ee9036eedfe3a7b404b880a045d9..ed19b3702ae9905483e722ccb9e0a69cb0d06b15 100644
--- a/src/routes/account.rs
+++ b/src/routes/account.rs
@@ -1,7 +1,7 @@
 use super::Response;
 use crate::database;
-use crate::util::{captcha, email, gen_token};
 use crate::util::variables::{DISABLE_REGISTRATION, USE_EMAIL};
+use crate::util::{captcha, email, gen_token};
 
 use bcrypt::{hash, verify};
 use chrono::prelude::*;
@@ -84,7 +84,7 @@ pub fn create(info: Json<Create>) -> Response {
             },
             false => doc! {
                 "verified": true
-            }
+            },
         };
 
         let id = Ulid::new().to_string();
diff --git a/src/routes/root.rs b/src/routes/root.rs
index 3026701be41e4fa0fae29f286441a947e78d0d84..af33c5ee516da6c342e866c299288a0279f96085 100644
--- a/src/routes/root.rs
+++ b/src/routes/root.rs
@@ -1,5 +1,5 @@
 use super::Response;
-use crate::util::variables::{USE_EMAIL, DISABLE_REGISTRATION, USE_HCAPTCHA};
+use crate::util::variables::{DISABLE_REGISTRATION, HCAPTCHA_SITEKEY, USE_EMAIL, USE_HCAPTCHA};
 
 use mongodb::bson::doc;
 
@@ -7,15 +7,13 @@ use mongodb::bson::doc;
 #[get("/")]
 pub fn root() -> Response {
     Response::Success(json!({
-        "revolt": "0.2.10",
-        "version": {
-            "major": 0,
-            "minor": 2,
-            "patch": 10
-        },
+        "revolt": "0.2.11",
         "features": {
             "registration": !*DISABLE_REGISTRATION,
-            "captcha": *USE_HCAPTCHA,
+            "captcha": {
+                "enabled": *USE_HCAPTCHA,
+                "key": HCAPTCHA_SITEKEY.to_string()
+            },
             "email": *USE_EMAIL,
         }
     }))
diff --git a/src/util/variables.rs b/src/util/variables.rs
index 794658af0b8f5e5e7b6aa2966bf693f4ece25431..682e0326c4a27834ef991875ac9da4086bbdee04 100644
--- a/src/util/variables.rs
+++ b/src/util/variables.rs
@@ -10,10 +10,12 @@ lazy_static! {
     pub static ref PUBLIC_URL: String =
         env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable.");
     pub static ref HCAPTCHA_KEY: String =
-        env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "".to_string());
+        env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string());
+    pub static ref HCAPTCHA_SITEKEY: String =
+        env::var("REVOLT_HCAPTCHA_SITEKEY").unwrap_or_else(|_| "10000000-ffff-ffff-ffff-000000000001".to_string());
     pub static ref WS_HOST: String =
-        env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9999".to_string());
-    
+        env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string());
+
     // Application Flags
     pub static ref DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "*1");
     pub static ref USE_EMAIL: bool = env::var("REVOLT_USE_EMAIL_VERIFICATION").map_or(
@@ -39,8 +41,7 @@ pub fn preflight_checks() {
     if *USE_EMAIL == false {
         #[cfg(not(debug_assertions))]
         {
-            if !env::var("REVOLT_UNSAFE_NO_EMAIL")
-                .map_or(false, |v| v == *"1") {
+            if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") {
                 panic!(
                     "Not letting you run this in production, set REVOLT_UNSAFE_NO_EMAIL=1 to run."
                 );
@@ -54,8 +55,7 @@ pub fn preflight_checks() {
     if *USE_HCAPTCHA == false {
         #[cfg(not(debug_assertions))]
         {
-            if !env::var("REVOLT_UNSAFE_NO_CAPTCHA")
-                .map_or(false, |v| v == *"1") {
+            if !env::var("REVOLT_UNSAFE_NO_CAPTCHA").map_or(false, |v| v == *"1") {
                 panic!("Not letting you run this in production, set REVOLT_UNSAFE_NO_CAPTCHA=1 to run.");
             }
         }