diff --git a/.travis.yml b/.travis.yml
--- a/.travis.yml
+++ b/.travis.yml
@@ -83,7 +83,7 @@
  - cabal --version
  - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
  - travis_retry cabal update
- - cabal install --only-dependencies --enable-tests -f-test-hlint
+ - cabal install --only-dependencies --enable-tests -f-test-hlint -f-test-doctests
 
 # Here starts the actual work to be performed for the package under
 # test; any command which exits with a non-zero exit code causes the
@@ -91,7 +91,7 @@
 script:
  - if [ -f configure.ac ]; then autoreconf -i; fi
  # -v2 provides useful information for debugging
- - cabal configure --enable-tests -f-test-hlint -v2
+ - cabal configure --enable-tests -f-test-hlint -f-test-doctests -v2
 
  # this builds all libraries and executables
  # (including tests/benchmarks)
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+0.0.5.0
+-------
+
+  * Added doctests and crash course introduction.
+  * Fixed some bugs in the test harnesses.
+  * Fixed some `hlint` gripes.
+  * Minor touchups elsewhere.
+
 0.0.4.0
 -------
 
diff --git a/benchmarks/bench.hs b/benchmarks/bench.hs
new file mode 100644
--- /dev/null
+++ b/benchmarks/bench.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE CPP #-}
+module Main
+       ( main -- :: IO ()
+       ) where
+
+import Criterion.Main
+import Crypto.Sign.Ed25519
+
+import Data.Maybe (fromJust)
+import Control.DeepSeq
+import qualified Data.ByteString as B
+
+--------------------------------------------------------------------------------
+
+#if !MIN_VERSION_bytestring(0,10,0)
+instance NFData B.ByteString
+#endif
+
+instance NFData SecretKey
+instance NFData PublicKey
+
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  -- Don't use `createKeypair`, since that will incur a ton of calls
+  -- to the OS to generate randomness. Simply generate a bogus Ed25519
+  -- seed instead.
+  let seed         = B.pack [0..31]
+      keys@(pk,sk) = fromJust (createKeypairFromSeed_ seed)
+
+      -- Generate a dummy message to sign, and a signature to verify
+      -- against.
+      dummy = B.pack [0..255]
+      msg = sign sk dummy
+  defaultMain
+    [ bench "deterministic key generation"   $ nf createKeypairFromSeed_ seed
+    , bench "signing a 256 byte message"     $ nf (sign sk)              dummy
+    , bench "verifying a signature"          $ nf (verify pk)            msg
+    , bench "roundtrip 256-byte sign/verify" $ nf (signBench keys)       dummy
+    ]
+
+signBench :: (PublicKey, SecretKey) -> B.ByteString -> Bool
+signBench (pk, sk) xs = verify pk (sign sk xs)
diff --git a/benchmarks/bench1.hs b/benchmarks/bench1.hs
deleted file mode 100644
--- a/benchmarks/bench1.hs
+++ /dev/null
@@ -1,44 +0,0 @@
-{-# LANGUAGE CPP #-}
-module Main
-       ( main -- :: IO ()
-       ) where
-
-import Criterion.Main
-import Crypto.Sign.Ed25519
-
-import Data.Maybe (fromJust)
-import Control.DeepSeq
-import qualified Data.ByteString as B
-
---------------------------------------------------------------------------------
-
-#if !MIN_VERSION_bytestring(0,10,0)
-instance NFData B.ByteString
-#endif
-
-instance NFData SecretKey
-instance NFData PublicKey
-
---------------------------------------------------------------------------------
-
-main :: IO ()
-main = do
-  -- Don't use `createKeypair`, since that will incur a ton of calls
-  -- to the OS to generate randomness. Simply generate a bogus Ed25519
-  -- seed instead.
-  let seed         = B.pack [0..31]
-      keys@(pk,sk) = fromJust (createKeypairFromSeed_ seed)
-
-      -- Generate a dummy message to sign, and a signature to verify
-      -- against.
-      dummy = B.pack [0..255]
-      msg = sign sk dummy
-  defaultMain
-    [ bench "deterministic key generation"   $ nf createKeypairFromSeed_ seed
-    , bench "signing a 256 byte message"     $ nf (sign sk)              dummy
-    , bench "verifying a signature"          $ nf (verify pk)            msg
-    , bench "roundtrip 256-byte sign/verify" $ nf (signBench keys)       dummy
-    ]
-
-signBench :: (PublicKey, SecretKey) -> B.ByteString -> Bool
-signBench (pk, sk) xs = verify pk (sign sk xs)
diff --git a/ed25519.cabal b/ed25519.cabal
--- a/ed25519.cabal
+++ b/ed25519.cabal
@@ -1,5 +1,5 @@
 name:                ed25519
-version:             0.0.4.0
+version:             0.0.5.0
 category:            Cryptography
 license:             MIT
 synopsis:            Ed25519 cryptographic signatures
@@ -47,6 +47,10 @@
   default: True
   manual: True
 
+flag test-doctests
+  default: True
+  manual: True
+
 flag no-donna
   default: True
   manual: True
@@ -101,11 +105,10 @@
 --
 
 test-suite hlint
-  type: exitcode-stdio-1.0
-  main-is: hlint.hs
-  ghc-options: -w
-  hs-source-dirs: tests
-  default-language:   Haskell2010
+  type:             exitcode-stdio-1.0
+  main-is:          hlint.hs
+  hs-source-dirs:   tests
+  default-language: Haskell2010
 
   if !flag(test-hlint)
     buildable: False
@@ -114,10 +117,25 @@
       base  >= 4   && < 5,
       hlint >= 1.7 && < 1.10
 
+test-suite doctests
+  type:             exitcode-stdio-1.0
+  main-is:          doctests.hs
+  hs-source-dirs:   tests
+  default-language: Haskell2010
+
+  if !flag(test-doctests)
+    buildable: False
+  else
+    build-depends:
+      base      >= 4    && < 5,
+      filepath  >= 1.0  && < 1.5,
+      directory >= 1.0  && < 1.3,
+      doctest   >= 0.10 && < 0.11
+
 -------------------------------------------------------------------------------
 -- Build pt 3: benchmarks
 
-benchmark bench1
+benchmark bench
   type:               exitcode-stdio-1.0
   build-depends:
       base        >= 4   && < 5,
@@ -128,4 +146,4 @@
 
   default-language:   Haskell2010
   hs-source-dirs:     benchmarks
-  main-is:            bench1.hs
+  main-is:            bench.hs
diff --git a/src/Crypto/Sign/Ed25519.hs b/src/Crypto/Sign/Ed25519.hs
--- a/src/Crypto/Sign/Ed25519.hs
+++ b/src/Crypto/Sign/Ed25519.hs
@@ -28,7 +28,7 @@
 -- educational and should help adjust your expectations properly.)
 --
 -- For more reading on the underlying implementation and theory
--- (including how to get a copy of the software Ed25519 software),
+-- (including how to get a copy of the Ed25519 software),
 -- visit <http://ed25519.cr.yp.to>. There are two papers that discuss
 -- the design of EdDSA/Ed25519 in detail:
 --
@@ -41,12 +41,14 @@
 --   be used with more curves (such as Ed41417, or Ed488), as well as
 --   defining the support for __message prehashing__. The original
 --   EdDSA is easily derived from the extended version through a few
---   parameter defaults. (This package will not consider non-Ed25519
+--   parameter defaults. (This package won't consider non-Ed25519
 --   EdDSA systems any further.)
 --
---
 module Crypto.Sign.Ed25519
-       ( -- * Keypair creation
+       ( -- * A crash course introduction
+         -- $intro
+
+         -- * Keypair creation
          -- $creatingkeys
          PublicKey(..)          -- :: *
        , SecretKey(..)          -- :: *
@@ -67,7 +69,8 @@
        , dverify              -- :: PublicKey -> ByteString -> Signature -> Bool
          -- ** Deprecated interface
          -- | The below interface is deprecated but functionally
-         -- equivalent to the above; it simply has \"worse\" naming.
+         -- equivalent to the above; it simply has \"worse\" naming and will
+         -- eventually be removed.
        , sign'                -- :: SecretKey -> ByteString -> Signature
        , verify'              -- :: PublicKey -> ByteString -> Signature -> Bool
 
@@ -108,6 +111,14 @@
 import           GHC.Generics             (Generic)
 #endif
 
+-- Doctest setup with some examples
+
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Data.ByteString.Char8
+-- >>> let hash        x = x
+-- >>> let readBigFile x = return x
+
 --------------------------------------------------------------------------------
 -- Key creation
 
@@ -229,9 +240,7 @@
                       -> (PublicKey, SecretKey) -- ^ Resulting keypair
 createKeypairFromSeed seed
   = fromMaybe (error "seed has incorrect length") (createKeypairFromSeed_ seed)
-{-# DEPRECATED createKeypairFromSeed "This function is unsafe as it can \
-@'fail'@ with an invalid input. This function will be replaced with \
-@'createKeypairWithSeed_'@ in a future release." #-}
+{-# DEPRECATED createKeypairFromSeed "This function is unsafe as it can @'fail'@ with an invalid input. Use @'createKeypairWithSeed_'@ instead." #-}
 
 -- | Derive the @'PublicKey'@ for a given @'SecretKey'@. This is a
 -- convenience which allows (for example) using @'createKeypair'@ and
@@ -380,9 +389,7 @@
       -> Signature
       -- ^ Message @'Signature'@, without the message
 sign' sk xs = dsign sk xs
-{-# DEPRECATED sign' "@'sign''@ will be removed in a future release; \
-use @'dsign'@ instead." #-}
-
+{-# DEPRECATED sign' "@'sign''@ will be removed in a future release; use @'dsign'@ instead." #-}
 
 -- | Verify a message with a detached @'Signature'@ against a given
 -- @'PublicKey'@. Simply an alias for @'dverify'@.
@@ -397,8 +404,7 @@
         -> Bool
         -- ^ Verification result
 verify' pk xs sig = dverify pk xs sig
-{-# DEPRECATED verify' "@'verify''@ will be removed in a future release; \
-use @'dverify'@ instead." #-}
+{-# DEPRECATED verify' "@'verify''@ will be removed in a future release; use @'dverify'@ instead." #-}
 
 --------------------------------------------------------------------------------
 -- FFI binding
@@ -433,6 +439,53 @@
 --------------------------------------------------------------------------------
 -- Documentation and notes
 
+-- $intro
+--
+-- The simplest use of this library is one where you probably need to
+-- sign short messages, so they can be verified independently. That's
+-- easily done by first creating a keypair with @'createKeypair'@, and
+-- using @'sign'@ to create a signed message. Then, you can distribute
+-- your public key and the signed message, and any recipient can
+-- verify that message:
+--
+-- >>> (pk, sk) <- createKeypair
+-- >>> let msg = sign sk "Hello world"
+-- >>> verify pk msg
+-- True
+--
+-- This interface is fine if your messages are small and simple binary
+-- blobs you want to verify in an opaque manner, but internally it
+-- creates a copy of the input message. Often, you'll want the
+-- signature independently of the message, and that can be done with
+-- @'dsign'@ and @'dverify'@. Naturally, verification fails if the
+-- message is incorrect:
+--
+-- >>> (pk, sk) <- createKeypair
+-- >>> let msg = "Hello world" :: ByteString
+-- >>> let sig = dsign sk msg
+-- >>> dverify pk msg sig
+-- True
+-- >>> dverify pk "Hello world" sig
+-- True
+-- >>> dverify pk "Goodbye world" sig
+-- False
+--
+-- Finally, it's worth keeping in mind this package doesn't expose any
+-- kind of incremental interface, and signing/verification can be
+-- expensive. So, if you're dealing with __large inputs__, you can
+-- hash the input with a robust, fast cryptographic hash, and then
+-- sign that (for example, the @hash@ function below could be
+-- __SHA-512__ or __BLAKE2b__):
+--
+-- >>> (pk, sk) <- createKeypair
+-- >>> msg <- readBigFile "blob.tar.gz" :: IO ByteString
+-- >>> let sig = dsign sk (hash msg)
+-- >>> dverify pk (hash msg) sig
+-- True
+--
+-- See the notes at the bottom of this module for more on message
+-- prehashing (as it acts slightly differently in an EdDSA system).
+
 -- $security
 --
 -- Included below are some notes on the security aspects of the
@@ -473,7 +526,7 @@
 -- level of Ed25519 is thus @2^((32*8)/2) = 2^128@, far beyond any
 -- attacker capability (modulo major breakthroughs for the ECDLP,
 -- which would likely catastrophically be applicable to other systems
--- too.)
+-- too).
 --
 -- Ed25519 designed to meet the standard notion of unforgeability for
 -- a public-key signature scheme under chosen-message attacks. This
@@ -729,7 +782,7 @@
 -- loading the entire file up front to either sign, or verify it. This
 -- is especially unoptimal for possibly smaller, low-memory systems
 -- (where decompression, hashing or verification are all best done in
--- constant space if possible.)
+-- constant space if possible).
 --
 -- Beware however, that if you do this sort of incremental hashing for
 -- large blobs, you are __taking untrusted data__ and hashing it
diff --git a/tests/doctests.hs b/tests/doctests.hs
new file mode 100644
--- /dev/null
+++ b/tests/doctests.hs
@@ -0,0 +1,29 @@
+module Main
+       ( main -- :: IO ()
+       ) where
+import           Control.Applicative
+import           Control.Monad
+import           Data.List
+import           System.Directory
+import           System.FilePath
+
+import           Test.DocTest
+
+main :: IO ()
+main = allSources >>= \sources -> doctest ("-isrc":sources)
+
+allSources :: IO [FilePath]
+allSources = liftM2 (++) (getFiles ".hs" "src")
+                         (getFiles ".o" "dist/build/src/cbits")
+
+getFiles :: String -> FilePath -> IO [FilePath]
+getFiles ext root = filter (isSuffixOf ext) <$> go root
+  where
+    go dir = do
+      (dirs, files) <- getFilesAndDirectories dir
+      (files ++) . concat <$> mapM go dirs
+
+getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])
+getFilesAndDirectories dir = do
+  c <- fmap (dir </>) . filter (`notElem` ["..", "."]) <$> getDirectoryContents dir
+  (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c
diff --git a/tests/hlint.hs b/tests/hlint.hs
--- a/tests/hlint.hs
+++ b/tests/hlint.hs
@@ -1,10 +1,11 @@
-module Main where
-
-import Data.Monoid
-import Control.Monad
-import Language.Haskell.HLint
-import System.Environment
-import System.Exit
+module Main
+       ( main -- :: IO ()
+       ) where
+import           Control.Monad
+import           Data.Monoid
+import           Language.Haskell.HLint
+import           System.Environment
+import           System.Exit
 
 main :: IO ()
 main = do
diff --git a/tests/properties.hs b/tests/properties.hs
--- a/tests/properties.hs
+++ b/tests/properties.hs
@@ -1,7 +1,7 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 {-# LANGUAGE CPP #-}
 module Main
-       ( main  -- :: IO ()
+       ( main -- :: IO ()
        ) where
 import           Control.Monad
 import           Data.ByteString          (ByteString)
@@ -62,7 +62,7 @@
 
 main :: IO ()
 main = do
-  args <- fmap (drop 1) getArgs
+  args <- getArgs
   let n = if null args then 100 else read (head args) :: Int
   (results, passed) <- runTests n
   printf "Passed %d tests!\n" (sum passed)
@@ -82,7 +82,7 @@
   where
     wrap :: Testable prop => prop -> IO (Bool, Int)
     wrap prop = do
-      r <- quickCheckWithResult stdArgs{maxSize=ntests} prop
+      r <- quickCheckWithResult stdArgs{maxSuccess=ntests} prop
       case r of
         Success n _ _           -> return (True, n)
         GaveUp  n _ _           -> return (True, n)
