diff --git a/cmd/HocusPocus.hs b/cmd/HocusPocus.hs
--- a/cmd/HocusPocus.hs
+++ b/cmd/HocusPocus.hs
@@ -96,11 +96,11 @@
 bounce endpoint appID = do
   side1 <- MagicWormhole.generateSide
   side2 <- MagicWormhole.generateSide
-  MagicWormhole.runClient endpoint appID side1 $ \session1 -> do
+  MagicWormhole.runClient endpoint appID side1 Nothing $ \session1 -> do
     nameplate <- MagicWormhole.allocate session1
     mailbox1 <- MagicWormhole.claim session1 nameplate
     peer1 <- MagicWormhole.open session1 mailbox1
-    MagicWormhole.runClient endpoint appID side2 $ \session2 -> do
+    MagicWormhole.runClient endpoint appID side2 Nothing $ \session2 -> do
       mailbox2 <- MagicWormhole.claim session2 nameplate
       peer2 <- MagicWormhole.open session2 mailbox2
       let message = "aoeu"
@@ -128,9 +128,9 @@
   side <- MagicWormhole.generateSide
   let endpoint = rendezvousEndpoint options
   case cmd options of
-    Send -> MagicWormhole.runClient endpoint appID side $ \session ->
+    Send -> MagicWormhole.runClient endpoint appID side Nothing $ \session ->
       sendText session "potato" "Brave new world that has such offers in it"
-    Receive -> MagicWormhole.runClient endpoint appID side $ \session -> do
+    Receive -> MagicWormhole.runClient endpoint appID side Nothing $ \session -> do
       message <- receiveText session
       putStr message
     Bounce -> bounce endpoint appID
diff --git a/magic-wormhole.cabal b/magic-wormhole.cabal
--- a/magic-wormhole.cabal
+++ b/magic-wormhole.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: c3ace4c817265fed08798ca9d75f3f34218ace8530d9ee3a27378b3ca1a1d844
+-- hash: cf81a4ee4f4e09c4a0bd7523601d063291f43ceb3b0204078f4969fece93f825
 
 name:           magic-wormhole
-version:        0.2.1
+version:        0.3.0
 synopsis:       Interact with Magic Wormhole
 description:    Magic Wormhole is a scheme to get things from one computer to another,
                 safely.
@@ -39,7 +39,7 @@
   ghc-options: -Wall -Werror=incomplete-patterns
   build-depends:
       aeson
-    , base >=4.6 && <=4.12
+    , base >=4.6 && <5
     , bytestring
     , containers
     , cryptonite
@@ -79,7 +79,7 @@
   ghc-options: -Wall -Werror=incomplete-patterns
   build-depends:
       aeson
-    , base >=4.6 && <=4.12
+    , base >=4.6 && <5
     , magic-wormhole
     , optparse-applicative
     , protolude >=0.2
@@ -96,7 +96,7 @@
   ghc-options: -Wall -Werror=incomplete-patterns
   build-depends:
       aeson
-    , base >=4.6 && <=4.12
+    , base >=4.6 && <5
     , bytestring
     , hedgehog
     , magic-wormhole
diff --git a/src/MagicWormhole/Internal/FileTransfer.hs b/src/MagicWormhole/Internal/FileTransfer.hs
--- a/src/MagicWormhole/Internal/FileTransfer.hs
+++ b/src/MagicWormhole/Internal/FileTransfer.hs
@@ -70,6 +70,7 @@
            <*> (toEnum <$> ((offer .: "directory") >>= (.: "numfiles")))
          ]
 
+-- | Textual representation of the format in which the directory tree is encoded
 data DirectoryMode = ZipFileDeflated
   deriving (Eq, Show)
 
diff --git a/src/MagicWormhole/Internal/Rendezvous.hs b/src/MagicWormhole/Internal/Rendezvous.hs
--- a/src/MagicWormhole/Internal/Rendezvous.hs
+++ b/src/MagicWormhole/Internal/Rendezvous.hs
@@ -105,17 +105,22 @@
   => WebSocketEndpoint -- ^ The websocket to connect to
   -> Messages.AppID -- ^ ID for your application (e.g. example.com/your-application)
   -> Messages.Side -- ^ Identifier for your side
+  -> Maybe Socket.Socket -- ^ Just an existing socket to use or Nothing to create and use a new one
   -> (Session -> IO a) -- ^ Action to perform inside the Magic Wormhole session
   -> IO a -- ^ The result of the action
-runClient (WebSocketEndpoint host port path) appID side app =
-  Socket.withSocketsDo . WS.runClient host port path $ \ws -> do
-    session <- atomically $ new ws appID side
-    (_, result) <- concurrently (readMessages session) (action ws session)
-    pure result
+runClient (WebSocketEndpoint host port path) appID side maybeSock app =
+  case maybeSock of
+    Nothing -> Socket.withSocketsDo . WS.runClient host port path $ runAction
+    Just sock -> Socket.withSocketsDo . WS.runClientWithSocket sock host path WS.defaultConnectionOptions [] $ runAction
   where
     action ws session = do
       bind session appID side
       app session `finally` WS.sendClose ws ("Connection closed connection" :: Text)
+
+    runAction ws = do
+      session <- atomically $ new ws appID side
+      (_, result) <- concurrently (readMessages session) (action ws session)
+      pure result
 
     -- | Read messages from the websocket forever, or until we fail to handle one.
     readMessages session = do
