diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..386853da7b29caf48a75dfa6dfc58115b139fdf3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+net/build
diff --git a/net/include/ftl/net.hpp b/net/include/ftl/net.hpp
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3252bf881ac5a50e6e4607712e37c0412d67a2c9 100644
--- a/net/include/ftl/net.hpp
+++ b/net/include/ftl/net.hpp
@@ -0,0 +1,14 @@
+#ifndef _FTL_NET_HPP_
+#define _FTL_NET_HPP_
+
+#include "ftl/net/raw.hpp"
+
+namespace ftl {
+namespace net {
+
+raw::Socket *connect(const char *uri) { return raw::connect(uri); }
+
+}
+}
+
+#endif // _FTL_NET_HPP_
diff --git a/net/include/ftl/net/raw.hpp b/net/include/ftl/net/raw.hpp
index 8c431744804abb421be01e858f4ac4deea8d0754..0dcd8e05b2d011ffd701827455769909d50c37a5 100644
--- a/net/include/ftl/net/raw.hpp
+++ b/net/include/ftl/net/raw.hpp
@@ -1,5 +1,5 @@
-#ifndef _FTL_NET_HPP_
-#define _FTL_NET_HPP_
+#ifndef _FTL_NET_RAW_HPP_
+#define _FTL_NET_RAW_HPP_
 
 #include <functional>
 #include <sstream>
@@ -107,4 +107,4 @@ const int MAX_CONNECTIONS = 100; // TODO Is this a good number?
 } // net
 } // ftl
 
-#endif // _FTL_NET_HPP_
+#endif // _FTL_NET_RAW_HPP_
diff --git a/net/include/ftl/uri.hpp b/net/include/ftl/uri.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..142e8afd82d5c1a29f716031ba077c70771009c4
--- /dev/null
+++ b/net/include/ftl/uri.hpp
@@ -0,0 +1,102 @@
+#ifndef _FTL_URI_HPP_
+#define _FTL_URI_HPP_
+
+#include <uriparser/Uri.h>
+#include <string>
+#include <vector>
+
+namespace ftl {
+
+	typedef const char * uri_t;
+
+	class URI {
+		public:
+		URI(uri_t puri) {
+			UriUriA uri;
+
+			#ifdef HAVE_URIPARSESINGLE
+			const char *errpos;
+			if (uriParseSingleUriA(&uri, puri, &errpos) != URI_SUCCESS) {
+			#else
+			UriParserStateA uris;
+			uris.uri = &uri;
+			if (uriParseUriA(&uris, puri) != URI_SUCCESS) {
+			#endif
+				m_valid = false;
+				m_host = "none";
+				m_port = -1;
+				m_proto = SCHEME_NONE;
+				m_path = "";
+			} else {
+				m_host = std::string(uri.hostText.first, uri.hostText.afterLast - uri.hostText.first);
+				
+				std::string prototext = std::string(uri.scheme.first, uri.scheme.afterLast - uri.scheme.first);
+				if (prototext == "tcp") m_proto = SCHEME_TCP;
+				else if (prototext == "udp") m_proto = SCHEME_UDP;
+				else if (prototext == "ftl") m_proto = SCHEME_FTL;
+				else if (prototext == "http") m_proto = SCHEME_HTTP;
+				else if (prototext == "ws") m_proto = SCHEME_WS;
+				else if (prototext == "ipc") m_proto = SCHEME_IPC;
+				else m_proto = SCHEME_OTHER;
+
+				std::string porttext = std::string(uri.portText.first, uri.portText.afterLast - uri.portText.first);
+				m_port = atoi(porttext.c_str());
+
+				for (auto h=uri.pathHead; h!=NULL; h=h->next) {
+					auto pstr = std::string(
+							h->text.first, h->text.afterLast - h->text.first);
+
+					m_path += "/";
+					m_path += pstr;
+					m_pathseg.push_back(pstr);
+				}
+
+				m_query = std::string(uri.query.first, uri.query.afterLast - uri.query.first);
+
+				uriFreeUriMembersA(&uri);
+
+				m_valid = m_proto != SCHEME_NONE && m_host.size() > 0;
+
+				if (m_valid) {
+					if (m_query.size() > 0) m_base = std::string(uri.scheme.first, uri.query.first - uri.scheme.first - 1);
+					else m_base = std::string(uri.scheme.first);
+				}
+			}
+		}
+
+		~URI() {};
+
+		enum scheme_t : int {
+			SCHEME_NONE,
+			SCHEME_TCP,
+			SCHEME_UDP,
+			SCHEME_FTL,		// Future Tech Lab
+			SCHEME_HTTP,
+			SCHEME_WS,
+			SCHEME_IPC,
+			SCHEME_FILE,
+			SCHEME_OTHER
+		};
+
+		bool isValid() { return m_valid; };
+		std::string &getHost() { return m_host; };
+		int getPort() { return m_port; };
+		scheme_t getProtocol() { return m_proto; };
+		scheme_t getScheme() { return m_proto; };
+		std::string &getPath() { return m_path; };
+		std::string &getQuery() { return m_query; };
+		std::string &getBaseURI() { return m_base; };
+
+		private:
+		bool m_valid;
+		std::string m_host;
+		std::string m_path;
+		std::string m_base;
+		std::vector<std::string> m_pathseg;
+		int m_port;
+		scheme_t m_proto;
+		std::string m_query;
+	};
+}
+
+#endif // _FTL_URI_HPP_
diff --git a/net/test/net_raw.cpp b/net/test/net_raw.cpp
index aefaa890fffde9d978fd4c3a72e66655c490f131..1aafbc4b39803e0d94e6efb0fcd5a59d5541e7e6 100644
--- a/net/test/net_raw.cpp
+++ b/net/test/net_raw.cpp
@@ -1,7 +1,6 @@
 #include "catch.hpp"
 #include <string.h>
 #include <ftl/net/raw.hpp>
-#include <ftl/net/services.hpp>
 #include <iostream>
 
 
@@ -241,12 +240,12 @@ TEST_CASE("Socket.onMessage()", "[net]") {
 	accept_connection();
 
 	SECTION("small valid message") {
-		send_json(ftl::net::SERVICE_CONFIGURE, "{message: \"Hello\"}");
+		send_json(1, "{message: \"Hello\"}");
 
 		bool msg = false;
 
 		sock->onMessage([&](int service, std::string &data) {
-			REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
+			REQUIRE(service == 1);
 			REQUIRE(data == "{message: \"Hello\"}");
 			msg = true;	
 		});
@@ -256,12 +255,12 @@ TEST_CASE("Socket.onMessage()", "[net]") {
 	}
 
 	SECTION("empty message") {
-		send_json(ftl::net::SERVICE_CONFIGURE, "");
+		send_json(1, "");
 
 		bool msg = false;
 
 		sock->onMessage([&](int service, std::string &data) {
-			REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
+			REQUIRE(service == 1);
 			REQUIRE(data == "");
 			msg = true;	
 		});
@@ -271,13 +270,13 @@ TEST_CASE("Socket.onMessage()", "[net]") {
 	}
 
 	SECTION("multiple valid messages") {
-		send_json(ftl::net::SERVICE_CONFIGURE, "{message: \"Hello\"}");
-		send_json(ftl::net::SERVICE_CONFIGURE, "{test: \"world\"}");
+		send_json(1, "{message: \"Hello\"}");
+		send_json(1, "{test: \"world\"}");
 
 		int msg = 0;
 
 		sock->onMessage([&](int service, std::string &data) {
-			REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
+			REQUIRE(service == 1);
 			if (msg == 0) REQUIRE(data == "{message: \"Hello\"}");
 			else REQUIRE(data == "{test: \"world\"}");
 			msg++;	
@@ -288,7 +287,7 @@ TEST_CASE("Socket.onMessage()", "[net]") {
 	}
 
 	SECTION("disconnected does not get message") {
-		send_json(ftl::net::SERVICE_CONFIGURE, "world");
+		send_json(1, "world");
 
 		bool msg = false;
 
diff --git a/p2p-remote-array/README.md b/p2p-remote-array/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7f250a23b963cfc345cd89be21fd81ff0d1297b9
--- /dev/null
+++ b/p2p-remote-array/README.md
@@ -0,0 +1,5 @@
+# Peer-2-Peer Remote Array Library
+Provides a remote memory access abstraction to allocate, read and write to arrays stored
+in different peers on the network.
+
+Requires `libftl-net`