diff --git a/.gitignore b/.gitignore
index 8d000f2..95e92be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,28 +1,28 @@
 # Editor artifacts
 .*.swp
 
 # App artifacts
 /_build
 /db
 /deps
 /*.ez
 Mnesia*
 
 # Generated on crash by the VM
 erl_crash.dump
 
 # Static artifacts
-/node_modules
+/assets/node_modules
 
 # Since we are building assets from web/static,
 # we ignore priv/static. You may want to comment
 # this depending on your deployment strategy.
 /priv/static/
 
 # The config/prod.secret.exs file by default contains sensitive
 # data and you should not commit it into version control.
 #
 # Alternatively, you may comment the line below and commit the
 # secrets file as long as you replace its contents by environment
 # variables.
 /config/prod.secret.exs
diff --git a/brunch-config.js b/assets/brunch-config.js
similarity index 84%
rename from brunch-config.js
rename to assets/brunch-config.js
index f4ed4ad..53f537c 100644
--- a/brunch-config.js
+++ b/assets/brunch-config.js
@@ -1,77 +1,74 @@
 exports.config = {
   // See http://brunch.io/#documentation for docs.
   files: {
     javascripts: {
       joinTo: "js/app.js"
 
       // To use a separate vendor.js bundle, specify two files path
       // http://brunch.io/docs/config#-files-
       // joinTo: {
       //  "js/app.js": /^(web\/static\/js)/,
       //  "js/vendor.js": /^(web\/static\/vendor)|(deps)/
       // }
       //
       // To change the order of concatenation of files, explicitly mention here
       // order: {
       //   before: [
       //     "web/static/vendor/js/jquery-2.1.1.js",
       //     "web/static/vendor/js/bootstrap.min.js"
       //   ]
       // }
     },
     stylesheets: {
       joinTo: "css/app.css",
       order: {
         after: ["web/static/css/app.css"] // concat app.css last
       }
     },
     templates: {
       joinTo: "js/app.js"
     }
   },
 
   conventions: {
     // This option sets where we should place non-css and non-js assets in.
-    // By default, we set this to "/web/static/assets". Files in this directory
+    // By default, we set this to "/assets/static". Files in this directory
     // will be copied to `paths.public`, which is "priv/static" by default.
-    assets: /^(web\/static\/assets)/
+    assets: /^(static)/
   },
 
   // Phoenix paths configuration
   paths: {
     // Dependencies and current project directories to watch
-    watched: [
-      "web/static",
-      "test/static"
-    ],
+    watched: ["static", "css", "js", "js/widgets", "vendor"],
 
     // Where to compile files to
-    public: "priv/static"
+    public: "../priv/static"
   },
 
   // Configure your plugins
   plugins: {
     babel: {
       // Do not use ES6 compiler in vendor code
-      ignore: [/web\/static\/vendor/]
+      ignore: [/vendor/]
     },
     copycat: {
       fonts: ["node_modules/bootstrap/fonts"]
     }
   },
 
   modules: {
     autoRequire: {
-      "js/app.js": ["web/static/js/app"]
+      "js/app.js": ["js/app"]
     }
   },
 
   npm: {
     enabled: true,
     globals: {
       $: "jquery",
       jQuery: "jquery",
       bootstrap: "bootstrap"
     }
   }
 };
diff --git a/web/static/css/app.css b/assets/css/app.css
similarity index 100%
rename from web/static/css/app.css
rename to assets/css/app.css
diff --git a/web/static/css/phoenix.css b/assets/css/phoenix.css
similarity index 100%
rename from web/static/css/phoenix.css
rename to assets/css/phoenix.css
diff --git a/web/static/css/widgets.css b/assets/css/widgets.css
similarity index 100%
rename from web/static/css/widgets.css
rename to assets/css/widgets.css
diff --git a/web/static/js/api.js b/assets/js/api.js
similarity index 100%
rename from web/static/js/api.js
rename to assets/js/api.js
diff --git a/web/static/js/app.js b/assets/js/app.js
similarity index 100%
rename from web/static/js/app.js
rename to assets/js/app.js
diff --git a/web/static/js/widgets/chatinput.js b/assets/js/widgets/chatinput.js
similarity index 100%
rename from web/static/js/widgets/chatinput.js
rename to assets/js/widgets/chatinput.js
diff --git a/web/static/js/widgets/chatroom.js b/assets/js/widgets/chatroom.js
similarity index 100%
rename from web/static/js/widgets/chatroom.js
rename to assets/js/widgets/chatroom.js
diff --git a/web/static/js/widgets/userlist.js b/assets/js/widgets/userlist.js
similarity index 100%
rename from web/static/js/widgets/userlist.js
rename to assets/js/widgets/userlist.js
diff --git a/web/static/js/widgets/userstatus.js b/assets/js/widgets/userstatus.js
similarity index 100%
rename from web/static/js/widgets/userstatus.js
rename to assets/js/widgets/userstatus.js
diff --git a/package.json b/assets/package.json
similarity index 79%
rename from package.json
rename to assets/package.json
index 0af9f90..a5bc2ea 100644
--- a/package.json
+++ b/assets/package.json
@@ -1,23 +1,23 @@
 {
   "repository": {},
   "license": "MIT",
   "scripts": {
     "deploy": "brunch build --production",
     "watch": "brunch watch --stdin"
   },
   "dependencies": {
-    "phoenix": "file:deps/phoenix",
-    "phoenix_html": "file:deps/phoenix_html",
+    "phoenix": "file:../deps/phoenix",
+    "phoenix_html": "file:../deps/phoenix_html",
     "jquery": ">=2.1",
     "bootstrap": "~3.3.7"
   },
   "devDependencies": {
     "babel-brunch": "~6.0.0",
-    "brunch": "2.7.4",
+    "brunch": "2.10.9",
     "clean-css-brunch": "~2.0.0",
     "css-brunch": "~2.0.0",
     "javascript-brunch": "~2.0.0",
     "uglify-js-brunch": "~2.0.1",
     "copycat-brunch": "~1.1.0"
   }
 }
diff --git a/web/static/assets/favicon.ico b/assets/static/favicon.ico
similarity index 100%
rename from web/static/assets/favicon.ico
rename to assets/static/favicon.ico
diff --git a/web/static/assets/images/kolab-logo.png b/assets/static/images/kolab-logo.png
similarity index 100%
rename from web/static/assets/images/kolab-logo.png
rename to assets/static/images/kolab-logo.png
diff --git a/web/static/assets/robots.txt b/assets/static/robots.txt
similarity index 100%
rename from web/static/assets/robots.txt
rename to assets/static/robots.txt
diff --git a/config/config.exs b/config/config.exs
index 53e09d0..1380390 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,28 +1,28 @@
 # This file is responsible for configuring your application
 # and its dependencies with the aid of the Mix.Config module.
 #
 # This configuration file is loaded before any dependency and
 # is restricted to this project.
 use Mix.Config
 
 # General application configuration
 config :kolab_chat,
   salts: [session_signing: "M7HpCp6W",
           session_encryption: nil]
 
 # Configures the endpoint
-config :kolab_chat, KolabChat.Endpoint,
+config :kolab_chat, KolabChat.Web.Endpoint,
   url: [host: "localhost"],
   secret_key_base: "XCVqlNuOTjBK3GB4lPKKdoTk9149ftPIJmytpQnYxI4qpGwjJbR47bYdzOAggBii",
-  render_errors: [view: KolabChat.ErrorView, accepts: ~w(html json)],
+  render_errors: [view: KolabChat.Web.ErrorView, accepts: ~w(html json)],
   pubsub: [name: KolabChat.PubSub,
            adapter: Phoenix.PubSub.PG2]
 
 # Configures Elixir's Logger
 config :logger, :console,
   format: "$time $metadata[$level] $message\n",
   metadata: [:request_id]
 
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
 import_config "#{Mix.env}.exs"
diff --git a/config/dev.exs b/config/dev.exs
index b4886aa..79718cd 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -1,34 +1,34 @@
 use Mix.Config
 
 # For development, we disable any cache and enable
 # debugging and code reloading.
 #
 # The watchers configuration can be used to run external
 # watchers to your application. For example, we use it
 # with brunch.io to recompile .js and .css sources.
-config :kolab_chat, KolabChat.Endpoint,
+config :kolab_chat, KolabChat.Web.Endpoint,
   http: [port: 4000],
   debug_errors: true,
   code_reloader: true,
   check_origin: false,
   watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
-                    cd: Path.expand("../", __DIR__)]]
+                    cd: Path.expand("../assets", __DIR__)]]
 
 
 # Watch static and templates for browser reloading.
-config :kolab_chat, KolabChat.Endpoint,
+config :kolab_chat, KolabChat.Web.Endpoint,
   live_reload: [
     patterns: [
       ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
       ~r{priv/gettext/.*(po)$},
-      ~r{web/views/.*(ex)$},
-      ~r{web/templates/.*(eex)$}
+      ~r{lib/kolab_chat/web/views/.*(ex)$},
+      ~r{lib/kolab_chat/web/templates/.*(eex)$}
     ]
   ]
 
 # Do not include metadata nor timestamps in development logs
 config :logger, :console, format: "[$level] $message\n"
 
 # Set a higher stacktrace during development. Avoid configuring such
 # in production as building large stacktraces may be expensive.
 config :phoenix, :stacktrace_depth, 20
diff --git a/config/prod.exs b/config/prod.exs
index 746a707..13da2ca 100644
--- a/config/prod.exs
+++ b/config/prod.exs
@@ -1,63 +1,63 @@
 use Mix.Config
 
 # For production, we configure the host to read the PORT
 # from the system environment. Therefore, you will need
 # to set PORT=80 before running your server.
 #
 # You should also configure the url host to something
 # meaningful, we use this information when generating URLs.
 #
 # Finally, we also include the path to a manifest
 # containing the digested version of static files. This
 # manifest is generated by the mix phoenix.digest task
 # which you typically run after static files are built.
-config :kolab_chat, KolabChat.Endpoint,
+config :kolab_chat, KolabChat.Web.Endpoint,
   http: [port: {:system, "PORT"}],
   url: [host: "example.com", port: 80],
-  cache_static_manifest: "priv/static/manifest.json"
+  cache_static_manifest: "priv/static/cache_manifest.json"
 
 # Do not print debug messages in production
 config :logger,
     level: :info,
     compile_time_purge_level: :warn
 
 # ## SSL Support
 #
 # To get SSL working, you will need to add the `https` key
 # to the previous section and set your `:url` port to 443:
 #
-#     config :kolab_chat, KolabChat.Endpoint,
+#     config :kolab_chat, KolabChat.Web.Endpoint,
 #       ...
 #       url: [host: "example.com", port: 443],
 #       https: [port: 443,
 #               keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
 #               certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
 #
 # Where those two env variables return an absolute path to
 # the key and cert in disk or a relative path inside priv,
 # for example "priv/ssl/server.key".
 #
 # We also recommend setting `force_ssl`, ensuring no data is
 # ever sent via http, always redirecting to https:
 #
-#     config :kolab_chat, KolabChat.Endpoint,
+#     config :kolab_chat, KolabChat.Web.Endpoint,
 #       force_ssl: [hsts: true]
 #
 # Check `Plug.SSL` for all available options in `force_ssl`.
 
 # ## Using releases
 #
 # If you are doing OTP releases, you need to instruct Phoenix
 # to start the server for all endpoints:
 #
 #     config :phoenix, :serve_endpoints, true
 #
 # Alternatively, you can configure exactly which server to
 # start per endpoint:
 #
-#     config :kolab_chat, KolabChat.Endpoint, server: true
+#     config :kolab_chat, KolabChat.Web.Endpoint, server: true
 #
 
 # Finally import the config/prod.secret.exs
 # which should be versioned separately.
 import_config "prod.secret.exs"
diff --git a/config/test.exs b/config/test.exs
index 4172c66..230ed95 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -1,10 +1,10 @@
 use Mix.Config
 
 # We don't run a server during test. If one is required,
 # you can enable the server option below.
-config :kolab_chat, KolabChat.Endpoint,
+config :kolab_chat, KolabChat.Web.Endpoint,
   http: [port: 4001],
   server: false
 
 # Print only warnings and errors during test
 config :logger, level: :warn
diff --git a/lib/kolab_chat.ex b/lib/kolab_chat.ex
index 69aff4d..26d6447 100644
--- a/lib/kolab_chat.ex
+++ b/lib/kolab_chat.ex
@@ -1,31 +1,24 @@
 defmodule KolabChat do
   use Application
 
   # See http://elixir-lang.org/docs/stable/elixir/Application.html
   # for more information on OTP Applications
   def start(_type, _args) do
     import Supervisor.Spec
 
     # Define workers and child supervisors to be supervised
     children = [
       # Start the endpoint when the application starts
-      supervisor(KolabChat.Endpoint, []),
+      supervisor(KolabChat.Web.Endpoint, []),
       # Start phoenix presence module
-      supervisor(KolabChat.Presence, []),
+      supervisor(KolabChat.Web.Presence, []),
       # Start your own worker by calling: KolabChat.Worker.start_link(arg1, arg2, arg3)
       # worker(KolabChat.Worker, [arg1, arg2, arg3]),
     ]
 
     # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
     # for other strategies and supported options
     opts = [strategy: :one_for_one, name: KolabChat.Supervisor]
     Supervisor.start_link(children, opts)
   end
-
-  # Tell Phoenix to update the endpoint configuration
-  # whenever the application is updated.
-  def config_change(changed, _new, removed) do
-    KolabChat.Endpoint.config_change(changed, removed)
-    :ok
-  end
 end
diff --git a/web/channels/presence.ex b/lib/kolab_chat/web/channels/presence.ex
similarity index 97%
rename from web/channels/presence.ex
rename to lib/kolab_chat/web/channels/presence.ex
index 3492c97..d51fceb 100644
--- a/web/channels/presence.ex
+++ b/lib/kolab_chat/web/channels/presence.ex
@@ -1,77 +1,77 @@
-defmodule KolabChat.Presence do
+defmodule KolabChat.Web.Presence do
   @moduledoc """
   Provides presence tracking to channels and processes.
 
   See the [`Phoenix.Presence`](http://hexdocs.pm/phoenix/Phoenix.Presence.html)
   docs for more details.
 
   ## Usage
 
   Presences can be tracked in your channel after joining:
 
       defmodule KolabChat.MyChannel do
         use KolabChat.Web, :channel
-        alias KolabChat.Presence
+        alias KolabChat.Web.Presence
 
         def join("some:topic", _params, socket) do
           send(self, :after_join)
           {:ok, assign(socket, :user_id, ...)}
         end
 
         def handle_info(:after_join, socket) do
           {:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
             online_at: inspect(System.system_time(:seconds))
           })
           push socket, "presence_state", Presence.list(socket)
           {:noreply, socket}
         end
       end
 
   In the example above, `Presence.track` is used to register this
   channel's process as a presence for the socket's user ID, with
   a map of metadata. Next, the current presence list for
   the socket's topic is pushed to the client as a `"presence_state"` event.
 
   Finally, a diff of presence join and leave events will be sent to the
   client as they happen in real-time with the "presence_diff" event.
   See `Phoenix.Presence.list/2` for details on the presence datastructure.
 
   ## Fetching Presence Information
 
   The `fetch/2` callback is triggered when using `list/1`
   and serves as a mechanism to fetch presence information a single time,
   before broadcasting the information to all channel subscribers.
   This prevents N query problems and gives you a single place to group
   isolated data fetching to extend presence metadata.
 
   The function receives a topic and map of presences and must return a
   map of data matching the Presence datastructure:
 
       %{"123" => %{metas: [%{status: "away", phx_ref: ...}],
         "456" => %{metas: [%{status: "online", phx_ref: ...}]}
 
   The `:metas` key must be kept, but you can extend the map of information
   to include any additional information. For example:
 
       def fetch(_topic, entries) do
         query =
           from u in User,
             where: u.id in ^Map.keys(entries),
             select: {u.id, u}
 
         users = query |> Repo.all |> Enum.into(%{})
 
         for {key, %{metas: metas}} <- entries, into: %{} do
           {key, %{metas: metas, user: users[key]}}
         end
       end
 
   The function above fetches all users from the database who
   have registered presences for the given topic. The fetched
   information is then extended with a `:user` key of the user's
   information, while maintaining the required `:metas` field from the
   original presence data.
   """
   use Phoenix.Presence, otp_app: :kolab_chat,
                         pubsub_server: KolabChat.PubSub
 end
diff --git a/web/channels/room_channel.ex b/lib/kolab_chat/web/channels/room_channel.ex
similarity index 92%
rename from web/channels/room_channel.ex
rename to lib/kolab_chat/web/channels/room_channel.ex
index f590455..0779f94 100644
--- a/web/channels/room_channel.ex
+++ b/lib/kolab_chat/web/channels/room_channel.ex
@@ -1,14 +1,14 @@
-defmodule KolabChat.RoomChannel do
+defmodule KolabChat.Web.RoomChannel do
   use KolabChat.Web, :channel
 
   @spec join(topic :: binary(), args :: map(), socket :: pid()) :: {:ok, socket :: pid()}
   def join("room:lobby", _, socket) do
     {:ok, socket}
   end
 
   @spec handle_in(topic :: binary, args :: map(), socket :: pid()) :: {:noreply, socket :: pid()}
   def handle_in("new:message", message, socket) do
     broadcast! socket, "new:message", %{user: message["user"], body: message["body"]}
     {:noreply, socket}
   end
 end
diff --git a/web/channels/system_channel.ex b/lib/kolab_chat/web/channels/system_channel.ex
similarity index 98%
rename from web/channels/system_channel.ex
rename to lib/kolab_chat/web/channels/system_channel.ex
index 6011fd7..965a022 100644
--- a/web/channels/system_channel.ex
+++ b/lib/kolab_chat/web/channels/system_channel.ex
@@ -1,108 +1,108 @@
-defmodule KolabChat.SystemChannel do
+defmodule KolabChat.Web.SystemChannel do
   use KolabChat.Web, :channel
 
   @status [
     # user is available for chat
     :online,
     :away,
     # user is connected and visible, but not available
     :busy,
     :unavailable,
     # user is shown as offline
     :invisible,
     :offline
   ]
 
   @spec join(topic :: binary(), args :: map(), socket :: pid()) :: {:ok, socket :: pid()}
   def join("system", %{"context" => context}, socket) do
     perform_join(context, socket)
   end
 
   def join("system", _args, socket) do
     perform_join("default", socket)
   end
 
   @spec handle_info(:after_join, socket :: pid()) :: {:noreply, socket :: pid()}
   def handle_info(:after_join, socket) do
     push socket, "presence_state", Presence.list(socket)
     push socket, "info", %{user: socket.assigns.user.username}
 
     Presence.track(socket, socket.assigns.user.username, %{
       status: get_user_status(socket),
       context: socket.assigns.context
     })
 
     {:noreply, socket}
   end
 
   @spec handle_in(topic :: binary, args :: map(), socket :: pid()) :: {:noreply, socket :: pid()}
   def handle_in("set-status", %{"status" => status}, socket) do
     status = check_status(status)
     socket
       |> update_presence_status(status)
       |> set_user_status(status)
 
     {:noreply, socket}
   end
 
   defp perform_join(context, socket) do
     socket = assign(socket, :context, context)
     send self(), :after_join
 
     {:ok, socket}
   end
 
   defp update_presence_status(socket, :invalid), do: socket
   defp update_presence_status(socket, status) do
     Presence.update(socket, socket.assigns.user.username, %{
       status: status,
       context: socket.assigns.context
     })
 
     socket
   end
 
   # Makes sure the provided status name is supported
   # Returns status name as an atom
   defp check_status(status) do
     status = String.to_atom(status)
 
     if Enum.member?(@status, status) do
       status
     else
       :invalid
     end
   end
 
   # Get the last user/context status from the database
   defp get_user_status(socket) do
     require Amnesia
     require Amnesia.Helper
 
     key = socket.assigns.user.username <> ":" <> socket.assigns.context
 
     Amnesia.transaction do
       case Database.Status.read(key) do
         # use last status
         %Database.Status{status: status} -> status
         # otherwise set status to online
         _ -> :online
       end
     end
   end
 
   # Save the current user/context status to the database
   defp set_user_status(socket, :invalid), do: socket
   defp set_user_status(socket, status) do
     require Amnesia
     require Amnesia.Helper
 
     key = socket.assigns.user.username <> ":" <> socket.assigns.context
 
     Amnesia.transaction do
       Database.Status.write(%Database.Status{key: key, status: status})
     end
 
     socket
   end
 end
diff --git a/web/channels/user_socket.ex b/lib/kolab_chat/web/channels/user_socket.ex
similarity index 90%
rename from web/channels/user_socket.ex
rename to lib/kolab_chat/web/channels/user_socket.ex
index a731204..d1f03db 100644
--- a/web/channels/user_socket.ex
+++ b/lib/kolab_chat/web/channels/user_socket.ex
@@ -1,41 +1,41 @@
-defmodule KolabChat.UserSocket do
+defmodule KolabChat.Web.UserSocket do
   use Phoenix.Socket
 
   alias KolabChat.Database
 
   ## Channels
-  channel "room:*", KolabChat.RoomChannel
-  channel "system", KolabChat.SystemChannel
+  channel "room:*", KolabChat.Web.RoomChannel
+  channel "system", KolabChat.Web.SystemChannel
 
   ## Transports
   transport :websocket, Phoenix.Transports.WebSocket
   transport :longpoll, Phoenix.Transports.LongPoll
 
   # Socket params are passed from the client and can
   # be used to verify and authenticate a user. After
   # verification, you can put default assigns into
   # the socket that will be set for all channels, ie
   #     {:ok, assign(socket, :user_id, verified_user_id)}
   # To deny connection, return `:error`.
   def connect(%{"token" => token}, socket) do
     case Phoenix.Token.verify(socket, "user", token, max_age: 86_400) do
       {:ok, user_id} ->
         socket = assign(socket, :user, Database.User.read!(user_id))
         {:ok, socket}
       {:error, _} ->
         :error
     end
   end
 
   # Socket id's are topics that allow you to identify all sockets for a given user:
   #
   #     def id(socket), do: "users_socket:#{socket.assigns.user_id}"
   #
   # Would allow you to broadcast a "disconnect" event and terminate
   # all active sockets and channels for a given user:
   #
   #     KolabChat.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})
   #
   # Returning `nil` makes this socket anonymous.
   def id(_socket), do: nil
 end
diff --git a/web/controllers/auth_controller.ex b/lib/kolab_chat/web/controllers/auth_controller.ex
similarity index 96%
rename from web/controllers/auth_controller.ex
rename to lib/kolab_chat/web/controllers/auth_controller.ex
index 5327c7c..fcf556a 100644
--- a/web/controllers/auth_controller.ex
+++ b/lib/kolab_chat/web/controllers/auth_controller.ex
@@ -1,55 +1,55 @@
-defmodule KolabChat.AuthController do
+defmodule KolabChat.Web.AuthController do
   use KolabChat.Web, :controller
 
   @doc """
   Handler for the default logon form
   """
   def default_callback(conn, params) do
     %{"logon" => %{"password" => _pass, "username" => username}} = params
     signin(conn, username)
   end
 
   defp signin(conn, username) when is_nil(username) or username == "" do
     conn
       |> put_flash(:error, gettext("Invalid username!"))
       |> redirect(to: "/")
   end
 
   defp signin(conn, username) do
     case insert_or_update_user(username) do
       {:ok, user} ->
         conn
         |> put_flash(:info, gettext("Signed in!"))
         |> put_session(:user_id, user.id)
         |> redirect(to: "/")
       {:error, _reason} ->
         conn
         |> put_flash(:error, gettext("Error signing in"))
         |> redirect(to: "/")
     end
   end
 
   defp insert_or_update_user(username) do
     require Amnesia
     require Amnesia.Helper
 
     case Database.User.find(username) do
       nil ->
         user = Amnesia.transaction do
           Database.User.write!(%Database.User{username: username})
         end
         {:ok, user}
       user ->
         {:ok, user}
     end
   end
 
   @doc """
   Handler for logout action
   """
   def logout(conn, _params) do
     conn
     |> configure_session(drop: true)
     |> redirect(to: "/")
   end
 end
diff --git a/web/controllers/chat_controller.ex b/lib/kolab_chat/web/controllers/chat_controller.ex
similarity index 81%
rename from web/controllers/chat_controller.ex
rename to lib/kolab_chat/web/controllers/chat_controller.ex
index 9fb4fad..47958af 100644
--- a/web/controllers/chat_controller.ex
+++ b/lib/kolab_chat/web/controllers/chat_controller.ex
@@ -1,11 +1,11 @@
-defmodule KolabChat.ChatController do
+defmodule KolabChat.Web.ChatController do
   use KolabChat.Web, :controller
 
   plug :put_layout, "chat.html"
 
   def index(conn, %{"room" => room} = _params) do
     conn
     |> assign(:room, room)
     |> render("index.html")
   end
 end
diff --git a/web/controllers/page_controller.ex b/lib/kolab_chat/web/controllers/page_controller.ex
similarity index 71%
rename from web/controllers/page_controller.ex
rename to lib/kolab_chat/web/controllers/page_controller.ex
index ca5625b..66dfe02 100644
--- a/web/controllers/page_controller.ex
+++ b/lib/kolab_chat/web/controllers/page_controller.ex
@@ -1,7 +1,7 @@
-defmodule KolabChat.PageController do
+defmodule KolabChat.Web.PageController do
   use KolabChat.Web, :controller
 
   def index(conn, _params) do
     render conn, "index.html"
   end
 end
diff --git a/web/controllers/plugs/locale.ex b/lib/kolab_chat/web/controllers/plugs/locale.ex
similarity index 87%
rename from web/controllers/plugs/locale.ex
rename to lib/kolab_chat/web/controllers/plugs/locale.ex
index 2bc795c..fa5eb84 100644
--- a/web/controllers/plugs/locale.ex
+++ b/lib/kolab_chat/web/controllers/plugs/locale.ex
@@ -1,38 +1,38 @@
 defmodule KolabChat.Plugs.Locale do
   import Plug.Conn
 
   def init(opts), do: opts
 
   def call(conn, _opts) do
     case get_session(conn, :locale) || client_locale(conn) do
       nil ->
         conn
       locale ->
-        Gettext.put_locale(KolabChat.Gettext, locale)
+        Gettext.put_locale(KolabChat.Web.Gettext, locale)
         put_session(conn, :locale, locale)
     end
   end
 
   # Gets supported locale code from the client
   # Uses 'locale' parameter or Accept-Language header
   defp client_locale(conn) do
-    supported = Gettext.known_locales(KolabChat.Gettext)
+    supported = Gettext.known_locales(KolabChat.Web.Gettext)
     locale = conn.params["locale"] || get_req_header(conn, "accept-language")
 
     locale
     |> to_string()
     |> String.split(",")
     |> Enum.map(&parse_locale/1)
     |> Enum.filter(fn(x) -> Enum.member?(supported, x) end)
     |> Enum.at(0)
   end
 
   # Extracts locale code from an element of Accept-Language header
   defp parse_locale(locale) do
     locale
     |> String.split(";")
     |> Enum.at(0)
     |> String.trim()
     |> String.replace("-", "_")
   end
 end
diff --git a/web/controllers/plugs/set_user.ex b/lib/kolab_chat/web/controllers/plugs/set_user.ex
similarity index 100%
rename from web/controllers/plugs/set_user.ex
rename to lib/kolab_chat/web/controllers/plugs/set_user.ex
diff --git a/lib/kolab_chat/endpoint.ex b/lib/kolab_chat/web/endpoint.ex
similarity index 92%
rename from lib/kolab_chat/endpoint.ex
rename to lib/kolab_chat/web/endpoint.ex
index aca8631..8e113f5 100644
--- a/lib/kolab_chat/endpoint.ex
+++ b/lib/kolab_chat/web/endpoint.ex
@@ -1,43 +1,43 @@
-defmodule KolabChat.Endpoint do
+defmodule KolabChat.Web.Endpoint do
   use Phoenix.Endpoint, otp_app: :kolab_chat
 
-  socket "/socket", KolabChat.UserSocket
+  socket "/socket", KolabChat.Web.UserSocket
 
   # Serve at "/" the static files from "priv/static" directory.
   #
   # You should set gzip to true if you are running phoenix.digest
   # when deploying your static files in production.
   plug Plug.Static,
     at: "/", from: :kolab_chat, gzip: false,
     only: ~w(css fonts images js favicon.ico robots.txt)
 
   # Code reloading can be explicitly enabled under the
   # :code_reloader configuration of your endpoint.
   if code_reloading? do
     socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
     plug Phoenix.LiveReloader
     plug Phoenix.CodeReloader
   end
 
   plug Plug.RequestId
   plug Plug.Logger
 
   plug Plug.Parsers,
     parsers: [:urlencoded, :multipart, :json],
     pass: ["*/*"],
     json_decoder: Poison
 
   plug Plug.MethodOverride
   plug Plug.Head
 
   # The session will be stored in the cookie and signed,
   # this means its contents can be read but not tampered with.
   # Set :encryption_salt if you would also like to encrypt it.
   plug Plug.Session,
     store: :cookie,
     key: "_kolab_chat_key",
     signing_salt: Keyword.get(Application.get_env(:kolab_chat, :salts), :session_signing),
     encryption_salt: Keyword.get(Application.get_env(:kolab_chat, :salts), :session_encryption)
 
-  plug KolabChat.Router
+  plug KolabChat.Web.Router
 end
diff --git a/web/gettext.ex b/lib/kolab_chat/web/gettext.ex
similarity index 95%
rename from web/gettext.ex
rename to lib/kolab_chat/web/gettext.ex
index 24deb2b..1fa9da4 100644
--- a/web/gettext.ex
+++ b/lib/kolab_chat/web/gettext.ex
@@ -1,24 +1,24 @@
-defmodule KolabChat.Gettext do
+defmodule KolabChat.Web.Gettext do
   @moduledoc """
   A module providing Internationalization with a gettext-based API.
 
   By using [Gettext](https://hexdocs.pm/gettext),
   your module gains a set of macros for translations, for example:
 
       import KolabChat.Gettext
 
       # Simple translation
       gettext "Here is the string to translate"
 
       # Plural translation
       ngettext "Here is the string to translate",
                "Here are the strings to translate",
                3
 
       # Domain-based translation
       dgettext "errors", "Here is the error message to translate"
 
   See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
   """
   use Gettext, otp_app: :kolab_chat
 end
diff --git a/web/router.ex b/lib/kolab_chat/web/router.ex
similarity index 85%
rename from web/router.ex
rename to lib/kolab_chat/web/router.ex
index e67850f..e65a8fb 100644
--- a/web/router.ex
+++ b/lib/kolab_chat/web/router.ex
@@ -1,42 +1,42 @@
-defmodule KolabChat.Router do
+defmodule KolabChat.Web.Router do
   use KolabChat.Web, :router
 
   pipeline :browser do
     plug :accepts, ["html"]
     plug :fetch_session
     plug :fetch_flash
     plug :protect_from_forgery
     plug :put_secure_browser_headers
     plug KolabChat.Plugs.Locale
     plug KolabChat.Plugs.SetUser
   end
 
   pipeline :api do
     plug :accepts, ["json"]
   end
 
-  scope "/", KolabChat do
+  scope "/", KolabChat.Web do
     pipe_through :browser
 
     get "/", PageController, :index
   end
 
-  scope "/chat", KolabChat do
+  scope "/chat", KolabChat.Web do
     pipe_through :browser
 
     get "/", ChatController, :index
     get "/:room", ChatController, :index
   end
 
-  scope "/auth", KolabChat do
+  scope "/auth", KolabChat.Web do
     pipe_through :browser
 
     post "/default/callback", AuthController, :default_callback
     get "/logout", AuthController, :logout
   end
 
   # Other scopes may use custom stacks.
   # scope "/api", KolabChat do
   #   pipe_through :api
   # end
 end
diff --git a/web/templates/chat/index.html.eex b/lib/kolab_chat/web/templates/chat/index.html.eex
similarity index 100%
rename from web/templates/chat/index.html.eex
rename to lib/kolab_chat/web/templates/chat/index.html.eex
diff --git a/web/templates/layout/app.html.eex b/lib/kolab_chat/web/templates/layout/app.html.eex
similarity index 100%
rename from web/templates/layout/app.html.eex
rename to lib/kolab_chat/web/templates/layout/app.html.eex
diff --git a/web/templates/layout/chat.html.eex b/lib/kolab_chat/web/templates/layout/chat.html.eex
similarity index 100%
rename from web/templates/layout/chat.html.eex
rename to lib/kolab_chat/web/templates/layout/chat.html.eex
diff --git a/web/templates/page/index.html.eex b/lib/kolab_chat/web/templates/page/index.html.eex
similarity index 100%
rename from web/templates/page/index.html.eex
rename to lib/kolab_chat/web/templates/page/index.html.eex
diff --git a/lib/kolab_chat/web/views/chat_view.ex b/lib/kolab_chat/web/views/chat_view.ex
new file mode 100644
index 0000000..d0939ef
--- /dev/null
+++ b/lib/kolab_chat/web/views/chat_view.ex
@@ -0,0 +1,3 @@
+defmodule KolabChat.Web.ChatView do
+  use KolabChat.Web, :view
+end
diff --git a/web/views/error_helpers.ex b/lib/kolab_chat/web/views/error_helpers.ex
similarity index 96%
rename from web/views/error_helpers.ex
rename to lib/kolab_chat/web/views/error_helpers.ex
index 40b4c0c..610d43c 100644
--- a/web/views/error_helpers.ex
+++ b/lib/kolab_chat/web/views/error_helpers.ex
@@ -1,40 +1,40 @@
-defmodule KolabChat.ErrorHelpers do
+defmodule KolabChat.Web.ErrorHelpers do
   @moduledoc """
   Conveniences for translating and building error messages.
   """
 
   use Phoenix.HTML
 
   @doc """
   Generates tag for inlined form input errors.
   """
   def error_tag(form, field) do
     if error = form.errors[field] do
       content_tag :span, translate_error(error), class: "help-block"
     end
   end
 
   @doc """
   Translates an error message using gettext.
   """
   def translate_error({msg, opts}) do
     # Because error messages were defined within Ecto, we must
     # call the Gettext module passing our Gettext backend. We
     # also use the "errors" domain as translations are placed
     # in the errors.po file.
     # Ecto will pass the :count keyword if the error message is
     # meant to be pluralized.
     # On your own code and templates, depending on whether you
     # need the message to be pluralized or not, this could be
     # written simply as:
     #
     #     dngettext "errors", "1 file", "%{count} files", count
     #     dgettext "errors", "is invalid"
     #
     if count = opts[:count] do
       Gettext.dngettext(KolabChat.Gettext, "errors", msg, msg, count, opts)
     else
       Gettext.dgettext(KolabChat.Gettext, "errors", msg, opts)
     end
   end
 end
diff --git a/web/views/error_view.ex b/lib/kolab_chat/web/views/error_view.ex
similarity index 90%
rename from web/views/error_view.ex
rename to lib/kolab_chat/web/views/error_view.ex
index d0c0f09..f4dfffa 100644
--- a/web/views/error_view.ex
+++ b/lib/kolab_chat/web/views/error_view.ex
@@ -1,17 +1,17 @@
-defmodule KolabChat.ErrorView do
+defmodule KolabChat.Web.ErrorView do
   use KolabChat.Web, :view
 
   def render("404.html", _assigns) do
     "Page not found"
   end
 
   def render("500.html", _assigns) do
     "Internal server error"
   end
 
   # In case no render clause matches or no
   # template is found, let's render it as 500
   def template_not_found(_template, assigns) do
     render "500.html", assigns
   end
 end
diff --git a/lib/kolab_chat/web/views/layout_view.ex b/lib/kolab_chat/web/views/layout_view.ex
new file mode 100644
index 0000000..563a97c
--- /dev/null
+++ b/lib/kolab_chat/web/views/layout_view.ex
@@ -0,0 +1,3 @@
+defmodule KolabChat.Web.LayoutView do
+  use KolabChat.Web, :view
+end
diff --git a/lib/kolab_chat/web/views/page_view.ex b/lib/kolab_chat/web/views/page_view.ex
new file mode 100644
index 0000000..dc52629
--- /dev/null
+++ b/lib/kolab_chat/web/views/page_view.ex
@@ -0,0 +1,3 @@
+defmodule KolabChat.Web.PageView do
+  use KolabChat.Web, :view
+end
diff --git a/web/web.ex b/lib/kolab_chat/web/web.ex
similarity index 72%
rename from web/web.ex
rename to lib/kolab_chat/web/web.ex
index 74ce923..5fc0a31 100644
--- a/web/web.ex
+++ b/lib/kolab_chat/web/web.ex
@@ -1,69 +1,70 @@
 defmodule KolabChat.Web do
   @moduledoc """
   A module that keeps using definitions for controllers,
   views and so on.
 
   This can be used in your application as:
 
       use KolabChat.Web, :controller
       use KolabChat.Web, :view
 
   The definitions below will be executed for every view,
   controller, etc, so keep them short and clean, focused
   on imports, uses and aliases.
 
   Do NOT define functions inside the quoted expressions
   below.
   """
 
   def controller do
     quote do
-      use Phoenix.Controller
+      use Phoenix.Controller, namespace: KolabChat.Web
 
       alias KolabChat.Database
 
-      import KolabChat.Router.Helpers
-      import KolabChat.Gettext
+      import KolabChat.Web.Router.Helpers
+      import KolabChat.Web.Gettext
     end
   end
 
   def view do
     quote do
-      use Phoenix.View, root: "web/templates"
+      use Phoenix.View, root: "lib/kolab_chat/web/templates",
+                        namespace: KolabChat.Web
 
       # Import convenience functions from controllers
       import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
 
       # Use all HTML functionality (forms, tags, etc)
       use Phoenix.HTML
 
-      import KolabChat.Router.Helpers
-      import KolabChat.ErrorHelpers
-      import KolabChat.Gettext
+      import KolabChat.Web.Router.Helpers
+      import KolabChat.Web.ErrorHelpers
+      import KolabChat.Web.Gettext
     end
   end
 
   def router do
     quote do
       use Phoenix.Router
     end
   end
 
   def channel do
     quote do
       use Phoenix.Channel
 
       alias KolabChat.Database
-      alias KolabChat.Presence
+      alias KolabChat.Web.Presence
 
-      import KolabChat.Gettext
+      import KolabChat.Web.Gettext
     end
   end
 
   @doc """
   When used, dispatch to the appropriate controller/view/etc.
   """
   defmacro __using__(which) when is_atom(which) do
     apply(__MODULE__, which, [])
   end
 end
diff --git a/mix.exs b/mix.exs
index 48bdc3e..ebeb3a5 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,53 +1,53 @@
 defmodule KolabChat.Mixfile do
   use Mix.Project
 
   def project do
     [app: :kolab_chat,
      version: "0.0.1",
      elixir: "~> 1.2",
      elixirc_paths: elixirc_paths(Mix.env),
      compilers: [:phoenix, :gettext] ++ Mix.compilers,
      build_embedded: Mix.env == :prod,
      start_permanent: Mix.env == :prod,
      aliases: aliases(),
      deps: deps()]
   end
 
   # Configuration for the OTP application.
   #
   # Type `mix help compile.app` for more information.
   def application do
     [mod: {KolabChat, []},
      applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,
                     :amnesia]]
   end
 
   # Specifies which paths to compile per environment.
-  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
-  defp elixirc_paths(_),     do: ["lib", "web"]
+  defp elixirc_paths(:test), do: ["lib", "test/support"]
+  defp elixirc_paths(_),     do: ["lib"]
 
   # Specifies your project dependencies.
   #
   # Type `mix help deps` for examples and options.
   defp deps do
-    [{:phoenix, "~> 1.2.1"},
+    [{:phoenix, "~> 1.3.0-rc"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_html, "~> 2.6"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:amnesia, "~> 0.2.0"},
      {:cowboy, "~> 1.0"},
      {:credo, "~> 0.5", only: [:dev, :test]}
      ]
   end
 
   # Aliases are shortcuts or tasks specific to the current project.
   # For example, to create, migrate and run the seeds file at once:
   #
   #     $ mix ecto.setup
   #
   # See the documentation for `Mix` for more info on aliases.
   defp aliases do
     []
   end
 end
diff --git a/mix.lock b/mix.lock
index f3d86b9..8ad867e 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,16 +1,16 @@
-%{"amnesia": {:hex, :amnesia, "0.2.5", "3202e0b01e380671274caea32fbe78bbd1989e1215215f41a2d97583e5c7d163", [], [{:exquisite, "~> 0.1.6", [hex: :exquisite, optional: false]}]},
+%{"amnesia": {:hex, :amnesia, "0.2.5", "3202e0b01e380671274caea32fbe78bbd1989e1215215f41a2d97583e5c7d163", [:mix], [{:exquisite, "~> 0.1.6", [hex: :exquisite, optional: false]}]},
   "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
   "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]},
-  "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], []},
+  "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
   "credo": {:hex, :credo, "0.6.1", "a941e2591bd2bd2055dc92b810c174650b40b8290459c89a835af9d59ac4a5f8", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]},
-  "exquisite": {:hex, :exquisite, "0.1.8", "ee8f56aae477287ce5e7dfcbc163a420cccbb73e680a6d80a09203e9ef514fa4", [], []},
-  "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [], []},
-  "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [], []},
+  "exquisite": {:hex, :exquisite, "0.1.8", "ee8f56aae477287ce5e7dfcbc163a420cccbb73e680a6d80a09203e9ef514fa4", [:mix], []},
+  "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
+  "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], []},
   "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
-  "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
-  "phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
-  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.8", "4333f9c74190f485a74866beff2f9304f069d53f047f5fbb0fb8d1ee4c495f73", [], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
-  "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [], []},
-  "plug": {:hex, :plug, "1.3.3", "d9be189924379b4e9d470caef87380d09549aea1ceafe6a0d41292c8c317c923", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
-  "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [], []},
+  "phoenix": {:hex, :phoenix, "1.3.0-rc.1", "0d04948a4bd24823f101024c07b6a4d35e58f1fd92a465c1bc75dd37acd1041a", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: false]}]},
+  "phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
+  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.8", "4333f9c74190f485a74866beff2f9304f069d53f047f5fbb0fb8d1ee4c495f73", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
+  "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []},
+  "plug": {:hex, :plug, "1.3.4", "b4ef3a383f991bfa594552ded44934f2a9853407899d47ecc0481777fb1906f6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
+  "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
   "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []}}
diff --git a/test/controllers/page_controller_test.exs b/test/controllers/page_controller_test.exs
index dfda05d..3529eec 100644
--- a/test/controllers/page_controller_test.exs
+++ b/test/controllers/page_controller_test.exs
@@ -1,8 +1,8 @@
-defmodule KolabChat.PageControllerTest do
-  use KolabChat.ConnCase
+defmodule KolabChat.Web.PageControllerTest do
+  use KolabChat.Web.ConnCase
 
   test "GET /", %{conn: conn} do
     conn = get conn, "/"
     assert html_response(conn, 200) =~ "Welcome to Phoenix!"
   end
 end
diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex
index bd99ed8..a28ae2a 100644
--- a/test/support/channel_case.ex
+++ b/test/support/channel_case.ex
@@ -1,43 +1,43 @@
-defmodule KolabChat.ChannelCase do
+defmodule KolabChat.Web.ChannelCase do
   @moduledoc """
   This module defines the test case to be used by
   channel tests.
 
   Such tests rely on `Phoenix.ChannelTest` and also
   import other functionality to make it easier
   to build and query models.
 
   Finally, if the test case interacts with the database,
   it cannot be async. For this reason, every test runs
   inside a transaction which is reset at the beginning
   of the test unless the test case is marked as async.
   """
 
   use ExUnit.CaseTemplate
 
   using do
     quote do
       # Import conveniences for testing with channels
       use Phoenix.ChannelTest
 
       alias KolabChat.Repo
       import Ecto
       import Ecto.Changeset
       import Ecto.Query
 
 
       # The default endpoint for testing
-      @endpoint KolabChat.Endpoint
+      @endpoint KolabChat.Web.Endpoint
     end
   end
 
   setup tags do
     :ok = Ecto.Adapters.SQL.Sandbox.checkout(KolabChat.Repo)
 
     unless tags[:async] do
       Ecto.Adapters.SQL.Sandbox.mode(KolabChat.Repo, {:shared, self()})
     end
 
     :ok
   end
 end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index 5fb21fa..4fc8cad 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -1,44 +1,44 @@
-defmodule KolabChat.ConnCase do
+defmodule KolabChat.Web.ConnCase do
   @moduledoc """
   This module defines the test case to be used by
   tests that require setting up a connection.
 
   Such tests rely on `Phoenix.ConnTest` and also
   import other functionality to make it easier
   to build and query models.
 
   Finally, if the test case interacts with the database,
   it cannot be async. For this reason, every test runs
   inside a transaction which is reset at the beginning
   of the test unless the test case is marked as async.
   """
 
   use ExUnit.CaseTemplate
 
   using do
     quote do
       # Import conveniences for testing with connections
       use Phoenix.ConnTest
 
       alias KolabChat.Repo
       import Ecto
       import Ecto.Changeset
       import Ecto.Query
 
-      import KolabChat.Router.Helpers
+      import KolabChat.Web.Router.Helpers
 
       # The default endpoint for testing
-      @endpoint KolabChat.Endpoint
+      @endpoint KolabChat.Web.Endpoint
     end
   end
 
   setup tags do
     :ok = Ecto.Adapters.SQL.Sandbox.checkout(KolabChat.Repo)
 
     unless tags[:async] do
       Ecto.Adapters.SQL.Sandbox.mode(KolabChat.Repo, {:shared, self()})
     end
 
     {:ok, conn: Phoenix.ConnTest.build_conn()}
   end
 end
diff --git a/test/views/error_view_test.exs b/test/views/error_view_test.exs
index bc7aaa8..469633e 100644
--- a/test/views/error_view_test.exs
+++ b/test/views/error_view_test.exs
@@ -1,21 +1,21 @@
-defmodule KolabChat.ErrorViewTest do
-  use KolabChat.ConnCase, async: true
+defmodule KolabChat.Web.ErrorViewTest do
+  use KolabChat.Web.ConnCase, async: true
 
   # Bring render/3 and render_to_string/3 for testing custom views
   import Phoenix.View
 
   test "renders 404.html" do
-    assert render_to_string(KolabChat.ErrorView, "404.html", []) ==
+    assert render_to_string(KolabChat.Web.ErrorView, "404.html", []) ==
            "Page not found"
   end
 
   test "render 500.html" do
-    assert render_to_string(KolabChat.ErrorView, "500.html", []) ==
+    assert render_to_string(KolabChat.Web.ErrorView, "500.html", []) ==
            "Internal server error"
   end
 
   test "render any other" do
-    assert render_to_string(KolabChat.ErrorView, "505.html", []) ==
+    assert render_to_string(KolabChat.Web.ErrorView, "505.html", []) ==
            "Internal server error"
   end
 end
diff --git a/test/views/layout_view_test.exs b/test/views/layout_view_test.exs
index 97a527b..b5f41e2 100644
--- a/test/views/layout_view_test.exs
+++ b/test/views/layout_view_test.exs
@@ -1,3 +1,3 @@
-defmodule KolabChat.LayoutViewTest do
-  use KolabChat.ConnCase, async: true
+defmodule KolabChat.Web.LayoutViewTest do
+  use KolabChat.Web.ConnCase, async: true
 end
diff --git a/test/views/page_view_test.exs b/test/views/page_view_test.exs
index 8f993a8..996b8ce 100644
--- a/test/views/page_view_test.exs
+++ b/test/views/page_view_test.exs
@@ -1,3 +1,3 @@
-defmodule KolabChat.PageViewTest do
-  use KolabChat.ConnCase, async: true
+defmodule KolabChat.Web.PageViewTest do
+  use KolabChat.Web.ConnCase, async: true
 end
diff --git a/web/views/chat_view.ex b/web/views/chat_view.ex
deleted file mode 100644
index b6b3261..0000000
--- a/web/views/chat_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule KolabChat.ChatView do
-  use KolabChat.Web, :view
-end
diff --git a/web/views/layout_view.ex b/web/views/layout_view.ex
deleted file mode 100644
index 131a259..0000000
--- a/web/views/layout_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule KolabChat.LayoutView do
-  use KolabChat.Web, :view
-end
diff --git a/web/views/page_view.ex b/web/views/page_view.ex
deleted file mode 100644
index 7d6c7a4..0000000
--- a/web/views/page_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule KolabChat.PageView do
-  use KolabChat.Web, :view
-end