diff --git a/src/Tahoe/SDMF.hs b/src/Tahoe/SDMF.hs
index 194439d3f318499df9108c914a171a65029a6eb2..fc31ca9dd98230f2a447c2d85a45898f994e180e 100644
--- a/src/Tahoe/SDMF.hs
+++ b/src/Tahoe/SDMF.hs
@@ -8,6 +8,8 @@ module Tahoe.SDMF (
 
 import Tahoe.SDMF.Internal.Capability (
     Reader (..),
+    SDMF (..),
+    Verifier (..),
     Writer (..),
     pCapability,
     pReader,
diff --git a/src/Tahoe/SDMF/Internal/Capability.hs b/src/Tahoe/SDMF/Internal/Capability.hs
index e259a5b844562b29f6a14a394c9e0fc27ae84ab5..8d42e1c951d8dc8af75bd7fb173252215bc3fe50 100644
--- a/src/Tahoe/SDMF/Internal/Capability.hs
+++ b/src/Tahoe/SDMF/Internal/Capability.hs
@@ -10,7 +10,6 @@ import Data.Binary (decode)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Base32 as B
 import qualified Data.ByteString.Lazy as LB
-import Data.Maybe (fromMaybe)
 import qualified Data.Set as Set
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
@@ -24,18 +23,21 @@ data Verifier = Verifier
     { verifierStorageIndex :: StorageIndex
     , verifierVerificationKeyHash :: Digest SHA256
     }
+    deriving (Eq, Show)
 
 -- | A read capability for an SDMF object.
 data Reader = Reader
     { readerReadKey :: Read
     , readerVerifier :: Verifier
     }
+    deriving (Eq, Show)
 
 -- | A write capability for an SDMF object.
 data Writer = Writer
     { writerWriteKey :: Write
     , writerReader :: Reader
     }
+    deriving (Eq, Show)
 
 -- | Diminish a write key to a read key and wrap it in a reader capability.
 deriveReader :: Write -> Digest SHA256 -> Maybe Reader
diff --git a/src/Tahoe/SDMF/Internal/Keys.hs b/src/Tahoe/SDMF/Internal/Keys.hs
index 47484f0909e5edb9faa3bbfdb68164406bdc58ba..5711b4def2516af8994284e0710e90565bebdb5a 100644
--- a/src/Tahoe/SDMF/Internal/Keys.hs
+++ b/src/Tahoe/SDMF/Internal/Keys.hs
@@ -46,6 +46,9 @@ newtype Signature = Signature {unSignature :: RSA.PrivateKey}
 
 data Write = Write {unWrite :: AES128, writeKeyBytes :: ByteArray.ScrubbedBytes}
 
+instance Eq Write where
+    (Write _ left) == (Write _ right) = left == right
+
 instance Binary Write where
     put = putByteString . ByteArray.convert . writeKeyBytes
     get = do
@@ -54,10 +57,30 @@ instance Binary Write where
         pure Write{..}
 
 instance Show Write where
-    show (Write _ bs) = T.unpack $ T.concat ["<WriteKey ", encodeBase32Unpadded (ByteArray.convert bs), ">"]
+    show (Write _ bs) =
+        T.unpack $
+            T.concat
+                [ "<WriteKey "
+                , T.take 4 . encodeBase32Unpadded . ByteArray.convert $ bs
+                , "..."
+                , ">"
+                ]
 
 data Read = Read {unRead :: AES128, readKeyBytes :: ByteArray.ScrubbedBytes}
 
+instance Eq Read where
+    (Read _ left) == (Read _ right) = left == right
+
+instance Show Read where
+    show (Read _ bs) =
+        T.unpack $
+            T.concat
+                [ "<ReadKey "
+                , T.take 4 . encodeBase32Unpadded . ByteArray.convert $ bs
+                , "..."
+                , ">"
+                ]
+
 instance Binary Read where
     put = putByteString . ByteArray.convert . readKeyBytes
     get = do
@@ -65,12 +88,17 @@ instance Binary Read where
         let (CryptoPassed unRead) = cipherInit readKeyBytes
         pure Read{..}
 
-instance Show Read where
-    show (Read _ bs) = T.unpack $ T.concat ["<ReadKey ", encodeBase32Unpadded (ByteArray.convert bs), ">"]
-instance Eq Read where
-    (Read _ left) == (Read _ right) = left == right
+newtype StorageIndex = StorageIndex {unStorageIndex :: B.ByteString} deriving newtype (Eq)
 
-newtype StorageIndex = StorageIndex {unStorageIndex :: B.ByteString}
+instance Show StorageIndex where
+    show (StorageIndex si) =
+        T.unpack $
+            T.concat
+                [ "<SI "
+                , T.take 4 . encodeBase32Unpadded . ByteArray.convert $ si
+                , "..."
+                , ">"
+                ]
 
 newtype WriteEnablerMaster = WriteEnablerMaster ByteArray.ScrubbedBytes
 
@@ -256,3 +284,12 @@ signatureKeyFromBytes bs = do
 -- | Encrypt the signature key for inclusion in the SDMF share itself.
 encryptSignatureKey :: Write -> Signature -> B.ByteString
 encryptSignatureKey Write{unWrite} = ctrCombine unWrite nullIV . signatureKeyToBytes
+
+{- | Replace most of the tail of a string with a short placeholder.  If the
+ string is not much longer than `n` then the result might not actually be
+ shorter.
+
+ TODO: Deduplicate this between here and tahoe-chk.
+-}
+shorten :: Int -> T.Text -> T.Text
+shorten n = (<> "...") . T.take n
diff --git a/test/Spec.hs b/test/Spec.hs
index 24ebcb92ebd9ed66bf2dbfe4d41ec43cf7310784..0519fe026e534c2817fa26afad506e15ea542a3e 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -148,9 +148,13 @@ tests =
                 validRead = "URI:SSK-RO:ro7pnpq6duaduuolookwbv5lqy:xlwog3jxbgsuaddh3bsofwmyhncv7fanmo7ujhqiy26usx2v2neq"
                 validVerify = "URI:SSK-Verifier:gz4s2zkkqy2geblvv77atyoppi:xlwog3jxbgsuaddh3bsofwmyhncv7fanmo7ujhqiy26usx2v2neq"
 
-                parsed = rights $ parse Tahoe.SDMF.pCapability "<test>" <$> [validWrite, validVerify, validRead]
+                parsed = rights $ parse Tahoe.SDMF.pCapability "<test>" <$> [validWrite, validRead, validVerify]
 
             assertEqual "parsing failed" 3 (length parsed)
+            let [Tahoe.SDMF.SDMFWriter writeCap, Tahoe.SDMF.SDMFReader readCap, Tahoe.SDMF.SDMFVerifier verifyCap] = parsed
+
+            assertEqual "derived reader /= parsed reader" (Tahoe.SDMF.writerReader writeCap) readCap
+            assertEqual "derived verifier /= parsed verifier" (Tahoe.SDMF.readerVerifier readCap) verifyCap
         , testProperty "Share round-trips through bytes" $
             property $ do
                 share <- forAll shares