diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Renzo Carbonara <renzocarbonaraλgmail.com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Renzo Carbonara nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/PEOPLE b/PEOPLE
new file mode 100644
--- /dev/null
+++ b/PEOPLE
@@ -0,0 +1,5 @@
+The following people have participated in creating this library, either
+by directly contributing code or by providing thoughtful input in
+discussions about the library design.
+
+Renzo Carbonara
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# network-simple
+
+Haskell library abstracring over simple network sockets usage patterns.
+Currently, only TCP sockets are supported. Support for UDP and Unix
+sockets is planned for future versions.
+
+Check the source or rendered Haddocks for extensive documentation.
+
+This code is licensed under the terms of the so called **3-clause BSD
+license**. Read the file named ``LICENSE`` found in this same directory
+for details.
+
+See the ``PEOPLE`` file to learn about the people involved in this
+effort.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/network-simple.cabal b/network-simple.cabal
new file mode 100644
--- /dev/null
+++ b/network-simple.cabal
@@ -0,0 +1,26 @@
+name:                network-simple
+version:             0.1.0.0
+synopsis:            Abstract simple network sockets usage patterns.
+description:         Abstract simple network sockets usage patterns.
+homepage:            https://github.com/k0001/network-simple
+bug-reports:         https://github.com/k0001/network-simple/issues
+license:             BSD3
+license-file:        LICENSE
+author:              Renzo Carbonara
+maintainer:          renzocarbonaraaλgmail.com
+copyright:           Copyright (c) Renzo Carbonara 2013
+category:            Network
+build-type:          Simple
+cabal-version:       >=1.8
+extra-source-files:  README.md PEOPLE
+
+source-repository head
+    type: git
+    location: git://github.com/k0001/network-simple.git
+
+library
+  hs-source-dirs:    src
+  exposed-modules:   Network.Simple.TCP
+  other-modules:     Network.Simple.Internal
+  build-depends:     base (>=4.5 && < 5)
+                   , network (>=2.3 && <2.5)
diff --git a/src/Network/Simple/Internal.hs b/src/Network/Simple/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Simple/Internal.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# OPTIONS_HADDOCK hide #-}
+
+-- Some code in this file was adapted from the @network-conduit@ library by
+-- Michael Snoyman. Copyright (c) 2011. See its licensing terms (BSD3) at:
+--   https://github.com/snoyberg/conduit/blob/master/network-conduit/LICENSE
+
+module Network.Simple.Internal
+  ( HostPreference(..)
+  , hpHostName
+  ) where
+
+import           Data.String                   (IsString (fromString))
+import qualified Network.Socket as             NS
+
+-- | Preferred host to bind.
+data HostPreference
+  = HostAny          -- ^Any available host.
+  | HostIPv4         -- ^Any available IPv4 host.
+  | HostIPv6         -- ^Any available IPv6 host.
+  | Host NS.HostName -- ^An explicit host name.
+  deriving (Eq, Ord, Show, Read)
+
+-- | The following special values are recognized:
+--
+-- * @*@ means 'HostAny'
+--
+-- * @*4@ means 'HostIPv4'
+--
+-- * @*6@ means 'HostIPv6'
+--
+-- * Any other string is 'Host'
+instance IsString HostPreference where
+  fromString "*"  = HostAny
+  fromString "*4" = HostIPv4
+  fromString "*6" = HostIPv6
+  fromString s    = Host s
+
+-- | Extract the 'NS.HostName' from a 'Host' preference, or 'Nothing' otherwise.
+hpHostName:: HostPreference -> Maybe NS.HostName
+hpHostName (Host s) = Just s
+hpHostName _        = Nothing
+
diff --git a/src/Network/Simple/TCP.hs b/src/Network/Simple/TCP.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Simple/TCP.hs
@@ -0,0 +1,290 @@
+-- | This module exports functions that abstract simple TCP 'NS.Socket'
+-- usage patterns.
+
+-- Some code in this file was adapted from the @pipes-network@ library by
+-- Renzo Carbonara. Copyright (c) 2012-2013. See its licensing terms (BSD3) at:
+--   https://github.com/k0001/pipes-network/blob/master/LICENSE
+--
+-- Some code in this file was adapted from the @network-conduit@ library by
+-- Michael Snoyman. Copyright (c) 2011. See its licensing terms (BSD3) at:
+--   https://github.com/snoyberg/conduit/blob/master/network-conduit/LICENSE
+
+
+module Network.Simple.TCP (
+  -- * Introduction to TCP networking
+  -- $tcp-101
+
+  -- * Server side
+  -- $server-side
+  serve,
+  serveFork,
+  -- ** Listening
+  listen,
+  -- ** Accepting
+  accept,
+  acceptFork,
+
+  -- * Client side
+  -- $client-side
+  connect,
+
+  -- * Low level support
+  bindSock,
+  connectSock,
+
+  -- * Exports
+  HostPreference(..),
+  ) where
+
+import           Control.Concurrent             (ThreadId, forkIO)
+import qualified Control.Exception              as E
+import           Control.Monad
+import           Data.List                      (partition)
+import qualified Network.Socket                 as NS
+import           Network.Simple.Internal
+
+--------------------------------------------------------------------------------
+-- $tcp-101
+--
+-- This introduction aims to give you a overly simplified overview of some
+-- concepts you need to know about TCP sockets in order to make effective use of
+-- this module.
+--
+-- There's two ends in a single TCP connection: one is the TCP «server» and the
+-- other is the TCP «client». Each end is uniquely identified by an IP address
+-- and a TCP port pair, and each end knows the IP address and TCP port of the
+-- other end. Each end can send and receive data to and from the other end.
+--
+-- A TCP server, once «bound» to a well-known IP address and TCP port, starts
+-- «listening» for incoming connections from TCP clients to such bound IP
+-- address and TCP port. When a TCP client attempts to connect to the TCP
+-- server, the TCP server must «accept» the incoming connection in order to
+-- start exchanging data with the remote end. A single TCP server can
+-- sequentially accept many incoming connections, possibly handling each one
+-- concurrently.
+--
+-- A TCP client can «connect» to a well-known IP address and TCP port previously
+-- bound by a listening TCP server willing to accept new incoming connections.
+-- Once the connection is established, the TCP client can immediately start
+-- exchanging data with the TCP server. The TCP client is randomly assigned a
+-- TCP port when connecting, and its IP address is selected by the operating
+-- system so that it is reachable from the remote end.
+--
+-- The TCP client a and the TCP server can be running in the same host or in
+-- different hosts.
+
+--------------------------------------------------------------------------------
+
+-- $client-side
+--
+-- The following functions allow you to obtain and use 'NS.Socket's useful to
+-- the client side of a TCP connection.
+--
+-- Here's how you could run a TCP client:
+--
+-- > connect "www.example.org" "80" $ \(connectionSocket, remoteAddr) -> do
+-- >   putStrLn $ "Connection established to " ++ show remoteAddr
+-- >   -- now you may use connectionSocket as you please within this scope.
+
+-- | Connect to a TCP server and use the connection.
+--
+-- The connection socket is closed when done or in case of exceptions.
+--
+-- If you prefer to acquire and close the socket yourself, then use
+-- 'connectSock' and the 'NS.sClose' function from "Network.Socket" instead.
+connect
+  :: NS.HostName      -- ^Server hostname.
+  -> NS.ServiceName   -- ^Server service port.
+  -> ((NS.Socket, NS.SockAddr) -> IO r)
+                      -- ^Computation taking the communication socket
+                      -- and the server address.
+  -> IO r
+connect host port = E.bracket (connectSock host port) (NS.sClose . fst)
+
+--------------------------------------------------------------------------------
+
+-- $server-side
+--
+-- The following functions allow you to obtain and use 'NS.Socket's useful to
+-- the server side of a TCP connection.
+--
+-- Here's how you could run a TCP server that handles in different threads each
+-- incoming connection to port @8000@ at IPv4 address @127.0.0.1@:
+--
+-- > listen (Host "127.0.0.1") "8000" $ \(listeningSocket, listeningAddr) -> do
+-- >   putStrLn $ "Listening for incoming connections at " ++ show listeningAddr
+-- >   forever . acceptFork listeningSocket $ \(connectionSocket, remoteAddr) -> do
+-- >     putStrLn $ "Connection established from " ++ show remoteAddr
+-- >     -- now you may use connectionSocket as you please within this scope.
+--
+-- If you keep reading you'll discover there are different ways to achieve
+-- the same, some ways more general than others. The above one was just an
+-- example using a pretty general approach, you are encouraged to use simpler
+-- approaches such as 'serve' if those suit your needs.
+
+-- | Bind a TCP listening socket and use it.
+--
+-- The listening socket is closed when done or in case of exceptions.
+--
+-- If you prefer to acquire and close the socket yourself, then use
+-- 'bindSock' and the 'NS.listen' and 'NS.sClose' functions from
+-- "Network.Socket" instead.
+--
+-- Note: 'N.maxListenQueue' is tipically 128, which is too small for high
+-- performance servers. So, we use the maximum between 'N.maxListenQueue' and
+-- 2048 as the default size of the listening queue. The 'NS.NoDelay' and
+-- 'NS.ReuseAddr' options are set on the socket.
+listen
+  :: HostPreference   -- ^Preferred host to bind.
+  -> NS.ServiceName   -- ^Service port to bind.
+  -> ((NS.Socket, NS.SockAddr) -> IO r)
+                      -- ^Computation taking the listening socket and
+                      -- the address it's bound to.
+  -> IO r
+listen hp port = E.bracket listen' (NS.sClose . fst)
+  where
+    listen' = do x@(bsock,_) <- bindSock hp port
+                 NS.listen bsock $ max 2048 NS.maxListenQueue
+                 return x
+
+-- | Start a TCP server that sequentially accepts and uses each incoming
+-- connection.
+--
+-- Both the listening and connection sockets are closed when done or in case of
+-- exceptions.
+--
+-- Note: You don't need to use 'listen' nor 'accept' manually if you use this
+-- function.
+serve
+  :: HostPreference   -- ^Preferred host to bind.
+  -> NS.ServiceName   -- ^Service port to bind.
+  -> ((NS.Socket, NS.SockAddr) -> IO r)
+                      -- ^Computation to run once an incoming
+                      -- connection is accepted. Takes the connection socket
+                      -- and remote end address.
+  -> IO r
+serve hp port k = do
+    listen hp port $ \(lsock,_) -> do
+      forever $ accept lsock k
+
+-- | Start a TCP server that accepts incoming connections and uses them
+-- concurrently in different threads.
+--
+-- The listening and connection sockets are closed when done or in case of
+-- exceptions.
+--
+-- Note: You don't need to use 'listen' nor 'acceptFork' manually if you use
+-- this function.
+serveFork
+  :: HostPreference   -- ^Preferred host to bind.
+  -> NS.ServiceName   -- ^Service port to bind.
+  -> ((NS.Socket, NS.SockAddr) -> IO ())
+                      -- ^Computation to run in a different thread
+                      -- once an incoming connection is accepted. Takes the
+                      -- connection socket and remote end address.
+  -> IO ()
+serveFork hp port k = do
+    listen hp port $ \(lsock,_) -> do
+      forever $ acceptFork lsock k
+
+-- | Accept a single incoming connection and use it.
+--
+-- The connection socket is closed when done or in case of exceptions.
+accept
+  :: NS.Socket        -- ^Listening and bound socket.
+  -> ((NS.Socket, NS.SockAddr) -> IO b)
+                      -- ^Computation to run once an incoming
+                      -- connection is accepted. Takes the connection socket
+                      -- and remote end address.
+  -> IO b
+accept lsock k = do
+    conn@(csock,_) <- NS.accept lsock
+    E.finally (k conn) (NS.sClose csock)
+{-# INLINABLE accept #-}
+
+-- | Accept a single incoming connection and use it in a different thread.
+--
+-- The connection socket is closed when done or in case of exceptions.
+acceptFork
+  :: NS.Socket        -- ^Listening and bound socket.
+  -> ((NS.Socket, NS.SockAddr) -> IO ())
+                      -- ^Computation to run in a different thread
+                      -- once an incoming connection is accepted. Takes the
+                      -- connection socket and remote end address.
+  -> IO ThreadId
+acceptFork lsock f = do
+    client@(csock,_) <- NS.accept lsock
+    forkIO $ E.finally (f client) (NS.sClose csock)
+{-# INLINABLE acceptFork #-}
+
+--------------------------------------------------------------------------------
+
+-- | Obtain a 'NS.Socket' connected to the given host and TCP service port.
+--
+-- The obtained 'NS.Socket' should be closed manually using 'NS.sClose' when
+-- it's not needed anymore, otherwise you risk having the socket open for much
+-- longer than needed.
+--
+-- Prefer to use 'connect' if you will be using the socket within a limited
+-- scope and would like it to be closed immediately after its usage or in case
+-- of exceptions.
+connectSock :: NS.HostName -> NS.ServiceName -> IO (NS.Socket, NS.SockAddr)
+connectSock host port = do
+    (addr:_) <- NS.getAddrInfo (Just hints) (Just host) (Just port)
+    E.bracketOnError (newSocket addr) NS.sClose $ \sock -> do
+       let sockAddr = NS.addrAddress addr
+       NS.connect sock sockAddr
+       return (sock, sockAddr)
+  where
+    hints = NS.defaultHints { NS.addrFlags = [NS.AI_ADDRCONFIG]
+                            , NS.addrSocketType = NS.Stream }
+
+-- | Obtain a 'NS.Socket' bound to the given host name and TCP service port.
+--
+-- The obtained 'NS.Socket' should be closed manually using 'NS.sClose' when
+-- it's not needed anymore.
+--
+-- Prefer to use 'listen' if you will be listening on this socket and using it
+-- within a limited scope, and would like it to be closed immediately after its
+-- usage or in case of exceptions.
+bindSock :: HostPreference -> NS.ServiceName -> IO (NS.Socket, NS.SockAddr)
+bindSock hp port = do
+    addrs <- NS.getAddrInfo (Just hints) (hpHostName hp) (Just port)
+    let addrs' = case hp of
+          HostIPv4 -> prioritize isIPv4addr addrs
+          HostIPv6 -> prioritize isIPv6addr addrs
+          _        -> addrs
+    tryAddrs addrs'
+  where
+    hints = NS.defaultHints { NS.addrFlags = [NS.AI_PASSIVE]
+                            , NS.addrSocketType = NS.Stream }
+
+    tryAddrs []     = error "bindSock: no addresses available"
+    tryAddrs [x]    = useAddr x
+    tryAddrs (x:xs) = E.catch (useAddr x)
+                              (\e -> let _ = e :: E.IOException in tryAddrs xs)
+
+    useAddr addr = E.bracketOnError (newSocket addr) NS.sClose $ \sock -> do
+      let sockAddr = NS.addrAddress addr
+      NS.setSocketOption sock NS.NoDelay 1
+      NS.setSocketOption sock NS.ReuseAddr 1
+      NS.bindSocket sock sockAddr
+      return (sock, sockAddr)
+
+--------------------------------------------------------------------------------
+
+-- Misc
+
+newSocket :: NS.AddrInfo -> IO NS.Socket
+newSocket addr = NS.socket (NS.addrFamily addr)
+                           (NS.addrSocketType addr)
+                           (NS.addrProtocol addr)
+
+isIPv4addr, isIPv6addr :: NS.AddrInfo -> Bool
+isIPv4addr x = NS.addrFamily x == NS.AF_INET
+isIPv6addr x = NS.addrFamily x == NS.AF_INET6
+
+-- | Move the elements that match the predicate closer to the head of the list.
+-- Sorting is stable.
+prioritize :: (a -> Bool) -> [a] -> [a]
+prioritize p = uncurry (++) . partition p
