diff --git a/gbs-downloader.cabal b/gbs-downloader.cabal
index 1bfed99021e858773ed0c84632dbf20ae3abbd2c..3f4350fa8bfb7decda8d7c5139168c7aaa0f1fbf 100644
--- a/gbs-downloader.cabal
+++ b/gbs-downloader.cabal
@@ -148,6 +148,7 @@ test-suite gbs-downloader-test
   build-depends:
     , base            ^>=4.14.3.0
     , bytestring
+    , containers
     , crypto-api
     , gbs-downloader
     , hedgehog
diff --git a/src/Tahoe/Download.hs b/src/Tahoe/Download.hs
index 25d438f60c65bae5f424b91e85c4735bd32504a7..11e1105a86b2275857314dcba39260f1d9f39c40 100644
--- a/src/Tahoe/Download.hs
+++ b/src/Tahoe/Download.hs
@@ -1,9 +1,21 @@
 module Tahoe.Download (DownloadError (..), download) where
 
 import Data.ByteString.Lazy (ByteString)
-import Data.Map.Strict (Map)
-import Tahoe.CHK.Capability (Reader)
-import Tahoe.CHK.Server (StorageServerAnnouncement, StorageServerID)
+import Data.Either (rights)
+import qualified Data.Map.Strict as Map
+
+-- import qualified Data.Set as Set
+import Data.Word (Word8)
+import Tahoe.CHK.Capability (Reader (..), Verifier (..))
+import Tahoe.CHK.Server (StorageServerAnnouncement (..), StorageServerID)
+import Tahoe.CHK.Types (StorageIndex)
+
+newtype ShareNum = ShareNum Word8 deriving (Eq, Ord, Show)
+
+-- {- | A map from share numbers to servers where the corresponding shares have
+--  recently been observed.
+-- -}
+-- type ShareMap = Map.Map ShareNum (Set.Set StorageServerID)
 
 {- | An unrecoverable problem arose while attempting to download and/or read
  some application data.
@@ -11,11 +23,57 @@ import Tahoe.CHK.Server (StorageServerAnnouncement, StorageServerID)
 data DownloadError
     = -- | The configuration included no candidate servers from which to download.
       NoConfiguredServers
+    | -- | Across all of the configured servers, none were actually connectable.
+      NoReachableServers
     deriving (Eq, Ord, Show)
 
+{- | Recover the application data associated with a given capability from the
+ given servers, if possible.
+-}
 download ::
-    Map StorageServerID StorageServerAnnouncement ->
+    -- | Information about the servers from which to consider downloading shares
+    -- representing the application data.
+    Map.Map StorageServerID StorageServerAnnouncement ->
+    -- | The read capability for the application data.
     Reader ->
+    -- | Either a description of how the recovery failed or the recovered
+    -- application data.
     IO (Either DownloadError ByteString)
-download servers cap
-    | mempty == servers = pure . Left $ NoConfiguredServers
+download servers Reader{verifier = Verifier{..}} =
+    case Map.toList servers of
+        [] -> pure . Left $ NoConfiguredServers
+        serverList -> do
+            -- Ask each server for all shares it has.
+            discovered <- rights <$> mapM discoverOnce serverList
+            case discovered of
+                [] -> pure $ Left NoReachableServers
+                _ -> pure $ Right ""
+  where
+    discoverOnce (sid, sann) = do
+        sharenums <- discoverShares storageIndex sann
+        pure $ case sharenums of
+            Left e -> Left e
+            Right shnums -> Right (sid, shnums)
+
+{- | An problem arose while attempting to discover the shares held on a
+ particular server.
+-}
+data DiscoverError
+    = -- | An announcement did not include a location for a connection attempt.
+      StorageServerLocationUnknown
+    deriving (Eq, Ord, Show)
+
+-- | Identify which servers claim to have some data at some index.
+discoverShares ::
+    -- | The storage index at which to look for data.
+    StorageIndex ->
+    -- | A server which could possibly have the data.  It "could possibly"
+    -- have the data because local configuration suggests the data might have
+    -- been uploaded to them in the past.
+    StorageServerAnnouncement ->
+    -- | The share numbers the server claims to have.
+    IO (Either DiscoverError [ShareNum])
+discoverShares _storageIndex ann
+    | Nothing == storageServerAnnouncementFURL ann =
+        pure $ Left StorageServerLocationUnknown
+    | otherwise = pure $ Right []
diff --git a/test/Spec.hs b/test/Spec.hs
index b3132b7347c79265ca90ca9b840fda67c6a5bfcb..956850d568905c1965be19b8227068e3a568ff90 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -3,8 +3,11 @@ module Main where
 import Control.Monad.IO.Class (liftIO)
 import Crypto.Classes (buildKey)
 import qualified Data.ByteString as B
+import qualified Data.Map.Strict as Map
+import Data.Word (Word16)
 import System.IO (hSetEncoding, stderr, stdout, utf8)
 import Tahoe.CHK.Capability (Reader (..), Verifier (..))
+import Tahoe.CHK.Server (StorageServerAnnouncement (..))
 import Tahoe.Download (DownloadError (..))
 import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit (assertEqual, testCase)
@@ -18,21 +21,42 @@ tests =
         [ testCase "no configured servers" $ do
             -- If there are no servers then we can't possibly get enough
             -- shares to recover the application data.
-            let Just readKey = buildKey $ B.replicate 32 0x00
-                storageIndex = B.replicate 32 0x00
-                fingerprint = B.replicate 32 0x00
-                required = 1
-                total = 1
-                size = 1234
-                verifier = Verifier{..}
-                cap = Reader{..}
-            result <- liftIO $ download mempty cap
+            result <- liftIO $ download mempty (trivialCap 1 1)
             assertEqual
                 "download should fail with no servers"
                 (Left NoConfiguredServers)
                 result
+        , testCase "no reachable servers" $ do
+            -- If we can't contact any configured server then we can't
+            -- possibly get enough shares to recover the application data.
+            let servers =
+                    Map.fromList
+                        [
+                            ( "v0-abc123"
+                            , StorageServerAnnouncement
+                                { storageServerAnnouncementFURL = Nothing
+                                , storageServerAnnouncementNick = Just "unreachable"
+                                , storageServerAnnouncementPermutationSeed = Nothing
+                                }
+                            )
+                        ]
+
+            result <- liftIO $ download servers (trivialCap 1 1)
+            assertEqual
+                "download should fail with no reachable servers"
+                (Left NoReachableServers)
+                result
         ]
 
+trivialCap :: Word16 -> Word16 -> Reader
+trivialCap required total = Reader{..}
+  where
+    Just readKey = buildKey $ B.replicate 32 0x00
+    storageIndex = B.replicate 32 0x00
+    fingerprint = B.replicate 32 0x00
+    size = 1234
+    verifier = Verifier{..}
+
 main :: IO ()
 main = do
     -- Hedgehog writes some non-ASCII and the whole test process will die if