diff --git a/Cargo.lock b/Cargo.lock
index b24b97fe978539cea50308db7257d17b1ed18976..335baedd55ce33a9740304f019361c6e89a16302 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2273,7 +2273,7 @@ dependencies = [
 
 [[package]]
 name = "revolt"
-version = "0.3.0-alpha"
+version = "0.3.1"
 dependencies = [
  "async-std",
  "async-tungstenite",
diff --git a/Cargo.toml b/Cargo.toml
index d5813bde1705f6a0b8cb8ff82ee70f7e1e90f301..e8d14fd69435a3ff5546a965c12a4b66622c5b49 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "revolt"
-version = "0.3.0-alpha"
+version = "0.3.1"
 authors = ["Paul Makles <paulmakles@gmail.com>"]
 edition = "2018"
 
diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index ad10efc73ab1033b0387ef949fc5b23ed4fdcef7..42bc1fcd6f24d2593b3bbf91e5e732883522bc92 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -1,39 +1,7 @@
-use crate::{
-    database::get_collection,
-    util::result::{Error, Result},
-};
+use crate::database::*;
 use mongodb::bson::to_document;
 use serde::{Deserialize, Serialize};
-
-/*#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct LastMessage {
-    id: String,
-    user_id: String,
-    short_content: String,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct Channel {
-    #[serde(rename = "_id")]
-    pub id: String,
-    #[serde(rename = "type")]
-    pub channel_type: u8,
-
-    // DM: whether the DM is active
-    pub active: Option<bool>,
-    // DM + GDM: last message in channel
-    pub last_message: Option<LastMessage>,
-    // DM + GDM: recipients for channel
-    pub recipients: Option<Vec<String>>,
-    // GDM: owner of group
-    pub owner: Option<String>,
-    // GUILD: channel parent
-    pub guild: Option<String>,
-    // GUILD + GDM: channel name
-    pub name: Option<String>,
-    // GUILD + GDM: channel description
-    pub description: Option<String>,
-}*/
+use crate::util::result::{Error, Result};
 
 #[derive(Serialize, Deserialize, Debug)]
 #[serde(tag = "type")]
diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs
index bdad5ce5bdae7fda3b5f1b030691728808af99db..a71a90e936999514ada35135e9d7b8807ae8efa2 100644
--- a/src/database/guards/reference.rs
+++ b/src/database/guards/reference.rs
@@ -1,11 +1,11 @@
 use crate::database::*;
 use crate::util::result::{Error, Result};
 
-use mongodb::bson::{doc, from_bson, Bson};
+use validator::Validate;
 use rocket::http::RawStr;
 use rocket::request::FromParam;
-use serde::{Deserialize, Serialize};
-use validator::Validate;
+use mongodb::bson::{doc, from_bson, Bson};
+use serde::{Deserialize, Serialize, de::DeserializeOwned};
 
 #[derive(Validate, Serialize, Deserialize)]
 pub struct Ref {
@@ -18,8 +18,8 @@ impl Ref {
         Ok(Ref { id })
     }
 
-    pub async fn fetch_user(&self) -> Result<User> {
-        let doc = get_collection("users")
+    pub async fn fetch<T: DeserializeOwned>(&self, collection: &'static str) -> Result<T> {
+        let doc = get_collection(&collection)
             .find_one(
                 doc! {
                     "_id": &self.id
@@ -29,17 +29,25 @@ impl Ref {
             .await
             .map_err(|_| Error::DatabaseError {
                 operation: "find_one",
-                with: "user",
+                with: &collection,
             })?
             .ok_or_else(|| Error::UnknownUser)?;
 
         Ok(
-            from_bson(Bson::Document(doc)).map_err(|_| Error::DatabaseError {
+            from_bson::<T>(Bson::Document(doc)).map_err(|_| Error::DatabaseError {
                 operation: "from_bson",
-                with: "user",
+                with: &collection,
             })?,
         )
     }
+
+    pub async fn fetch_user(&self) -> Result<User> {
+        self.fetch("users").await
+    }
+
+    pub async fn fetch_channel(&self) -> Result<Channel> {
+        self.fetch("channels").await
+    }
 }
 
 impl User {
diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cfb4ebde79996097e7f3598d1533d9fe7c90d77e
--- /dev/null
+++ b/src/database/permissions/channel.rs
@@ -0,0 +1,33 @@
+use crate::database::*;
+use num_enum::TryFromPrimitive;
+use std::ops;
+
+#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
+#[repr(u32)]
+pub enum ChannelPermission {
+    View = 1,
+    SendMessage = 2,
+}
+
+bitfield! {
+    pub struct ChannelPermissions(MSB0 [u32]);
+    u32;
+    pub get_view, _: 31;
+    pub get_send_message, _: 30;
+}
+
+impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
+impl_op_ex_commutative!(+ |a: &u32, b: &ChannelPermission| -> u32 { *a | *b as u32 });
+
+pub async fn calculate(user: &User, target: &Channel) -> ChannelPermissions<[u32; 1]> {
+    match target {
+        Channel::SavedMessages { user: owner, .. } => {
+            if &user.id == owner {
+                ChannelPermissions([ ChannelPermission::View + ChannelPermission::SendMessage ])
+            } else {
+                ChannelPermissions([ 0 ])
+            }
+        }
+        _ => unreachable!()
+    }
+}
diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs
index 4565ef950e64dfe231229bf6bab0367e169f44be..0240b8bf45f9d63c5fd6adbd78f04b182ea7eeb5 100644
--- a/src/database/permissions/mod.rs
+++ b/src/database/permissions/mod.rs
@@ -1,3 +1,4 @@
 pub mod user;
+pub mod channel;
 
 pub use user::get_relationship;
diff --git a/src/routes/channels/fetch_channel.rs b/src/routes/channels/fetch_channel.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4d9596018998d9dd2b0f9f2d772f6b382f6bd1cb
--- /dev/null
+++ b/src/routes/channels/fetch_channel.rs
@@ -0,0 +1,16 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use rocket_contrib::json::JsonValue;
+
+#[get("/<target>")]
+pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
+    let target = target.fetch_channel().await?;
+
+    let perm = permissions::channel::calculate(&user, &target).await;
+    if !perm.get_view() {
+        Err(Error::LabelMe)?
+    }
+
+    Ok(json!(target))
+}
diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs
index 018a956f4c2105512d8ab6e07b2605c0d950d64e..2137b3ca9c28ca722e44dcdfa281f07ab3eddd37 100644
--- a/src/routes/channels/mod.rs
+++ b/src/routes/channels/mod.rs
@@ -1,5 +1,9 @@
 use rocket::Route;
 
+mod fetch_channel;
+
 pub fn routes() -> Vec<Route> {
-    routes![]
+    routes![
+        fetch_channel::req
+    ]
 }