diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -6,8 +6,12 @@
 
 ## Description
 
-Core data types, signing, (de)serialisation routines, and HTTP plumbing for all
-`amazonka-*` libraries.
+Core functionality, serialisation primitives, and data types for
+Amazonka related Amazon Web Services SDKs.
+
+The external interface of this library is stable with respect to the
+downstream Amazonka libraries, only, and as such is probably not suitable
+for use in non-Amazonka projects.
 
 
 ## Contribute
diff --git a/amazonka-core.cabal b/amazonka-core.cabal
--- a/amazonka-core.cabal
+++ b/amazonka-core.cabal
@@ -1,6 +1,6 @@
 name:                  amazonka-core
-version:               0.0.5
-synopsis:              Core functionality, serialisation primitives, and data types for the Amazonka Amazon Web Services SDKs.
+version:               0.0.6
+synopsis:              Core functionality and data types for Amazonka libraries.
 homepage:              https://github.com/brendanhay/amazonka
 license:               OtherLicense
 license-file:          LICENSE
@@ -16,6 +16,10 @@
     Core functionality, serialisation primitives, and data types for
     Amazonka related Amazon Web Services SDKs.
     .
+    The external interface of this library is stable with respect to the
+    downstream Amazonka libraries, only, and as such is probably not suitable
+    for use in non-Amazonka projects.
+    .
     /Warning:/ This is an experimental preview release which is still under
     heavy development and not intended for public consumption, caveat emptor!
 
@@ -30,23 +34,18 @@
     ghc-options:       -Wall
 
     exposed-modules:
-          Network.AWS.Auth
-        , Network.AWS.Data
+          Network.AWS.Data
         , Network.AWS.EC2.Metadata
         , Network.AWS.Error
         , Network.AWS.Pagination
         , Network.AWS.Prelude
-        , Network.AWS.Request.Internal
         , Network.AWS.Request.JSON
         , Network.AWS.Request.Query
         , Network.AWS.Request.RestJSON
         , Network.AWS.Request.RestXML
         , Network.AWS.Request.S3
         , Network.AWS.Response
-        , Network.AWS.Signing.Internal
-        , Network.AWS.Signing.V2
-        , Network.AWS.Signing.V3
-        , Network.AWS.Signing.V4
+        , Network.AWS.Signing
         , Network.AWS.Types
 
     other-modules:
@@ -65,6 +64,11 @@
         , Network.AWS.Data.Internal.Time
         , Network.AWS.Data.Internal.URI
         , Network.AWS.Data.Internal.XML
+        , Network.AWS.Request.Internal
+        , Network.AWS.Signing.Internal
+        , Network.AWS.Signing.Internal.V2
+        , Network.AWS.Signing.Internal.V3
+        , Network.AWS.Signing.Internal.V4
 
     build-depends:
           aeson                >= 0.8    && < 0.9
diff --git a/src/Network/AWS/Auth.hs b/src/Network/AWS/Auth.hs
deleted file mode 100644
--- a/src/Network/AWS/Auth.hs
+++ /dev/null
@@ -1,203 +0,0 @@
-{-# LANGUAGE BangPatterns      #-}
-{-# LANGUAGE FlexibleContexts  #-}
-{-# LANGUAGE LambdaCase        #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ViewPatterns      #-}
-
--- Module      : Network.AWS.Auth
--- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
--- License     : This Source Code Form is subject to the terms of
---               the Mozilla Public License, v. 2.0.
---               A copy of the MPL can be found in the LICENSE file or
---               you can obtain it at http://mozilla.org/MPL/2.0/.
--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
--- Stability   : experimental
--- Portability : non-portable (GHC extensions)
-
--- | Explicitly specify, or automatically retrieve your Amazon AWS security
--- credentials from the underlying host.
-module Network.AWS.Auth
-    (
-    -- * Defaults
-      accessKey
-    , secretKey
-
-    -- * Specifying credentials
-    , fromKeys
-    , fromSession
-
-    -- * Retrieving credentials
-    , Credentials (..)
-    , getAuth
-    ) where
-
-import           Control.Applicative
-import           Control.Concurrent
-import           Control.Monad.Except
-import qualified Data.Aeson                 as Aeson
-import qualified Data.ByteString.Char8      as BS
-import qualified Data.ByteString.Lazy.Char8 as LBS
-import           Data.IORef
-import           Data.Monoid
-import           Data.Text                  (Text)
-import qualified Data.Text                  as Text
-import qualified Data.Text.Encoding         as Text
-import           Data.Time
-import           Network.AWS.Data
-import           Network.AWS.EC2.Metadata
-import           Network.AWS.Types
-import           Network.HTTP.Client
-import           System.Environment
-import           System.Mem.Weak
-
--- | Default access key environment variable.
-accessKey :: Text -- ^ 'AWS_ACCESS_KEY'
-accessKey = "AWS_ACCESS_KEY"
-
--- | Default secret key environment variable.
-secretKey :: Text -- ^ 'AWS_SECRET_KEY'
-secretKey = "AWS_SECRET_KEY"
-
--- | Explicit access and secret keys.
-fromKeys :: AccessKey -> SecretKey -> Auth
-fromKeys a s = Auth (AuthEnv a s Nothing Nothing)
-
--- | A session containing the access key, secret key, and a security token.
-fromSession :: AccessKey -> SecretKey -> SecurityToken -> Auth
-fromSession a s t = Auth (AuthEnv a s (Just t) Nothing)
-
--- | Determines how authentication information is retrieved.
-data Credentials
-    = FromKeys AccessKey SecretKey
-      -- ^ Explicit access and secret keys.
-      -- Note: you can achieve the same result purely by using 'fromKeys'.
-    | FromSession AccessKey SecretKey SecurityToken
-      -- ^ A session containing the access key, secret key, and a security token.
-      -- Note: you can achieve the same result purely by using 'fromSession'.
-    | FromProfile Text
-      -- ^ An IAM Profile name to lookup from the local EC2 instance-data.
-    | FromEnv Text Text
-      -- ^ Environment variables to lookup for the access and secret keys.
-    | Discover
-      -- ^ Attempt to read the default access and secret keys from the environment,
-      -- falling back to the first available IAM profile if they are not set.
-      --
-      -- Note: This attempts to resolve <http://instance-data> rather than directly
-      -- retrieving <http://169.254.169.254> for IAM profile information to ensure
-      -- the dns lookup terminates promptly if not running on EC2.
-      deriving (Eq)
-
-instance ToText Credentials where
-    toText = \case
-        FromKeys    a _   -> "FromKeys "    <> toText a <> " ****"
-        FromSession a _ _ -> "FromSession " <> toText a <> " **** ****"
-        FromProfile n     -> "FromProfile " <> n
-        FromEnv     a s   -> "FromEnv "     <> a <> " " <> s
-        Discover          -> "Discover"
-
-instance Show Credentials where
-    show = showText
-
--- | Retrieve authentication information from the environment or instance-data.
-getAuth :: (Functor m, MonadIO m)
-        => Manager
-        -> Credentials
-        -> ExceptT String m Auth
-getAuth m = \case
-    FromKeys    a s   -> return (fromKeys a s)
-    FromSession a s t -> return (fromSession a s t)
-    FromProfile n     -> show `withExceptT` fromProfileName m n
-    FromEnv     a s   -> fromEnvVars a s
-    Discover          -> fromEnv `catchError` const (iam `catchError` const err)
-      where
-        iam = show `withExceptT` fromProfile m
-        err = throwError "Unable to read environment variables or IAM profile."
-
--- | Retrieve access and secret keys from the default environment variables.
---
--- /See:/ 'accessKey' and 'secretKey'
-fromEnv :: (Functor m, MonadIO m) => ExceptT String m Auth
-fromEnv = fromEnvVars accessKey secretKey
-
--- | Retrieve access and secret keys from specific environment variables.
-fromEnvVars :: (Functor m, MonadIO m) => Text -> Text -> ExceptT String m Auth
-fromEnvVars a s = fmap Auth $ AuthEnv
-    <$> (AccessKey <$> key a)
-    <*> (SecretKey <$> key s)
-    <*> pure Nothing
-    <*> pure Nothing
-  where
-    key (Text.unpack -> k) = ExceptT $ do
-        m <- liftIO (lookupEnv k)
-        return $
-            maybe (Left $ "Unable to read ENV variable: " ++ k)
-                  (Right . BS.pack)
-                  m
-
--- | Retrieve the default IAM Profile from the local EC2 instance-data.
---
--- This determined by Amazon as the first IAM profile found in the response from:
--- @http://169.254.169.254/latest/meta-data/iam/security-credentials/@
-fromProfile :: MonadIO m => Manager -> ExceptT HttpException m Auth
-fromProfile m = do
-    !ls <- BS.lines `liftM` metadata m (IAM $ SecurityCredentials Nothing)
-    case ls of
-        (x:_) -> fromProfileName m (Text.decodeUtf8 x)
-        _     -> throwError $
-           HttpParserException "Unable to get default IAM Profile from EC2 metadata"
-
--- | Lookup a specific IAM Profile by name from the local EC2 instance-data.
---
--- The resulting IONewRef wrapper + timer is designed so that multiple concurrent
--- accesses of 'AuthEnv' from the 'AWS' environment are not required to calculate
--- expiry and sequentially queue to update it.
---
--- The forked timer ensures a singular owner and pre-emptive refresh of the
--- temporary session credentials.
---
--- A weak reference is used to ensure that the forked thread will eventually
--- terminate when 'Auth' is no longer referenced.
-fromProfileName :: MonadIO m
-                => Manager
-                -> Text
-                -> ExceptT HttpException m Auth
-fromProfileName m name = auth >>= start
-  where
-    auth :: MonadIO m => ExceptT HttpException m AuthEnv
-    auth = do
-        !lbs <- LBS.fromStrict `liftM` metadata m
-            (IAM . SecurityCredentials $ Just name)
-        either (throwError . HttpParserException)
-               return
-               (Aeson.eitherDecode lbs)
-
-    start !a = ExceptT . liftM Right . liftIO $
-        case _authExpiry a of
-            Nothing -> return (Auth a)
-            Just x  -> do
-                r <- newIORef a
-                p <- myThreadId
-                s <- timer r p x
-                return (Ref s r)
-
-    timer r p x = forkIO $ do
-        s <- myThreadId
-        w <- mkWeakIORef r (killThread s)
-        loop w p x
-
-    loop w p x = do
-        diff x <$> getCurrentTime >>= threadDelay
-        ea <- runExceptT auth
-        case ea of
-            Left   e -> throwTo p e
-            Right !a -> do
-                 mr <- deRefWeak w
-                 case mr of
-                     Nothing -> return ()
-                     Just  r -> do
-                         atomicWriteIORef r a
-                         maybe (return ()) (loop w p) (_authExpiry a)
-
-    diff x y = (* 1000000) $
-        let n = truncate (diffUTCTime x y) - 60
-         in if n > 0 then n else 1
diff --git a/src/Network/AWS/Data/Internal/Body.hs b/src/Network/AWS/Data/Internal/Body.hs
--- a/src/Network/AWS/Data/Internal/Body.hs
+++ b/src/Network/AWS/Data/Internal/Body.hs
@@ -23,7 +23,6 @@
 import           Data.Aeson
 import           Data.ByteString                      (ByteString)
 import qualified Data.ByteString                      as BS
-import qualified Data.ByteString.Builder              as Build
 import qualified Data.ByteString.Lazy                 as LBS
 import qualified Data.ByteString.Lazy.Char8           as LBS8
 import           Data.Conduit
@@ -45,7 +44,7 @@
     build = const "RsBody { ResumableSource (ResourceT IO) ByteString }"
 
 instance Show RsBody where
-    show = LBS8.unpack . Build.toLazyByteString . build
+    show = LBS8.unpack . buildBS
 
 connectBody :: MonadResource m => RsBody -> Sink ByteString m a -> m a
 connectBody (RsBody src) sink = hoist liftResourceT src $$+- sink
@@ -69,7 +68,7 @@
         ]
 
 instance Show RqBody where
-    show = LBS8.unpack . Build.toLazyByteString . build . _bdyBody
+    show = LBS8.unpack . buildBS . _bdyBody
 
 instance IsString RqBody where
     fromString = toBody . LBS8.pack
diff --git a/src/Network/AWS/Data/Internal/ByteString.hs b/src/Network/AWS/Data/Internal/ByteString.hs
--- a/src/Network/AWS/Data/Internal/ByteString.hs
+++ b/src/Network/AWS/Data/Internal/ByteString.hs
@@ -18,15 +18,17 @@
     , ToByteString (..)
     , ToBuilder    (..)
     , showBS
+    , buildBS
     , stripBS
     ) where
 
 import           Crypto.Hash
 import           Data.ByteString                (ByteString)
+import qualified Data.ByteString                as BS
 import           Data.ByteString.Builder        (Builder)
 import qualified Data.ByteString.Builder        as Build
-import qualified Data.ByteString.Char8          as BS
-import qualified Data.ByteString.Lazy.Char8     as LBS
+import qualified Data.ByteString.Char8          as BS8
+import qualified Data.ByteString.Lazy           as LBS
 import           Data.CaseInsensitive           (CI)
 import qualified Data.CaseInsensitive           as CI
 import           Data.Char
@@ -44,7 +46,7 @@
 type LazyByteString = LBS.ByteString
 
 showBS :: ToByteString a => a -> String
-showBS = BS.unpack . toBS
+showBS = BS8.unpack . toBS
 {-# INLINE showBS #-}
 
 class ToByteString a where
@@ -54,22 +56,23 @@
     toBS = Text.encodeUtf8 . toText
     {-# INLINE toBS #-}
 
-instance ToByteString Builder    where toBS = buildBS
-instance ToByteString ByteString where toBS = id
-instance ToByteString Text       where toBS = Text.encodeUtf8
-instance ToByteString Int        where toBS = buildBS
-instance ToByteString Integer    where toBS = buildBS
-instance ToByteString Natural    where toBS = buildBS
-instance ToByteString Double     where toBS = buildBS
-instance ToByteString StdMethod  where toBS = renderStdMethod
-instance ToByteString (Digest a) where toBS = digestToHexByteString
-instance ToByteString UTCTime    where toBS = BS.pack . show
+instance ToByteString ByteString     where toBS = id
+instance ToByteString Builder        where toBS = toBS . Build.toLazyByteString
+instance ToByteString LazyByteString where toBS = LBS.toStrict
+instance ToByteString Text           where toBS = Text.encodeUtf8
+instance ToByteString Int            where toBS = toBS . buildBS
+instance ToByteString Integer        where toBS = toBS . buildBS
+instance ToByteString Natural        where toBS = toBS . buildBS
+instance ToByteString Double         where toBS = toBS . buildBS
+instance ToByteString StdMethod      where toBS = renderStdMethod
+instance ToByteString (Digest a)     where toBS = digestToHexByteString
+instance ToByteString UTCTime        where toBS = BS8.pack . show
 
 instance ToByteString a => ToByteString (CI a) where
     toBS = toBS . CI.original
 
-buildBS :: ToBuilder a => a -> ByteString
-buildBS = LBS.toStrict . Build.toLazyByteString . build
+buildBS :: ToBuilder a => a -> LazyByteString
+buildBS = Build.toLazyByteString . build
 {-# INLINE buildBS #-}
 
 class ToBuilder a where
@@ -82,7 +85,7 @@
 instance ToBuilder Builder        where build = id
 instance ToBuilder ByteString     where build = Build.byteString
 instance ToBuilder LazyByteString where build = Build.lazyByteString
-instance ToBuilder Text           where build = Build.byteString . Text.encodeUtf8
+instance ToBuilder Text           where build = Build.byteString . toBS
 instance ToBuilder Char           where build = Build.charUtf8
 instance ToBuilder [Char]         where build = Build.stringUtf8
 instance ToBuilder Int            where build = Build.intDec
@@ -169,5 +172,5 @@
         s = responseStatus x
 
 stripBS :: ByteString -> ByteString
-stripBS = BS.dropWhile isSpace . fst . BS.spanEnd isSpace
+stripBS = BS8.dropWhile isSpace . fst . BS8.spanEnd isSpace
 {-# INLINE stripBS #-}
diff --git a/src/Network/AWS/Data/Internal/JSON.hs b/src/Network/AWS/Data/Internal/JSON.hs
--- a/src/Network/AWS/Data/Internal/JSON.hs
+++ b/src/Network/AWS/Data/Internal/JSON.hs
@@ -15,6 +15,7 @@
     -- * FromJSON
       FromJSON (..)
     , parseJSONText
+    , eitherDecode'
     -- ** Parser a
     , withObject
     , (.:)
@@ -30,6 +31,7 @@
     , (.=)
     ) where
 
+import           Data.Aeson                     (eitherDecode')
 import           Data.Aeson.Types
 import qualified Data.HashMap.Strict            as Map
 import           Data.Text                      (Text)
diff --git a/src/Network/AWS/Signing.hs b/src/Network/AWS/Signing.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/AWS/Signing.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE ConstraintKinds   #-}
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+-- Module      : Network.AWS.Signing.Internal
+-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
+-- License     : This Source Code Form is subject to the terms of
+--               the Mozilla Public License, v. 2.0.
+--               A copy of the MPL can be found in the LICENSE file or
+--               you can obtain it at http://mozilla.org/MPL/2.0/.
+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+
+module Network.AWS.Signing
+    (
+    -- * Version 2
+      V2
+
+    -- * Version 3
+    , V3
+
+    -- * Version 4
+    , V4
+
+    -- * Signing functions
+    , sign
+    , presign
+    ) where
+
+import Network.AWS.Signing.Internal
+import Network.AWS.Signing.Internal.V2
+import Network.AWS.Signing.Internal.V3
+import Network.AWS.Signing.Internal.V4
diff --git a/src/Network/AWS/Signing/Internal.hs b/src/Network/AWS/Signing/Internal.hs
--- a/src/Network/AWS/Signing/Internal.hs
+++ b/src/Network/AWS/Signing/Internal.hs
@@ -44,6 +44,3 @@
 
 hmacSHA256 :: ByteString -> ByteString -> ByteString
 hmacSHA256 = HMAC.hmac SHA256.hash 64
-
-serviceOf :: AWSService (Sv a) => Request a -> Service (Sv a)
-serviceOf = const service
diff --git a/src/Network/AWS/Signing/Internal/V2.hs b/src/Network/AWS/Signing/Internal/V2.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/AWS/Signing/Internal/V2.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TupleSections     #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+-- Module      : Network.AWS.Signaing.Internal.V2
+-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
+-- License     : This Source Code Form is subject to the terms of
+--               the Mozilla Public License, v. 2.0.
+--               A copy of the MPL can be found in the LICENSE file or
+--               you can obtain it at http://mozilla.org/MPL/2.0/.
+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+
+module Network.AWS.Signing.Internal.V2
+    ( V2
+    ) where
+
+import           Control.Applicative
+import           Control.Lens
+import           Data.ByteString              (ByteString)
+import qualified Data.ByteString.Base64       as Base64
+import qualified Data.ByteString.Char8        as BS
+import           Data.List                    (intersperse)
+import           Data.Monoid
+import           Data.Time
+import           Network.AWS.Data
+import           Network.AWS.Request.Internal
+import           Network.AWS.Signing.Internal
+import           Network.AWS.Types
+import           Network.HTTP.Types           hiding (toQuery)
+
+data V2
+
+data instance Meta V2 = Meta
+    { _mSignature :: ByteString
+    , _mTime      :: UTCTime
+    }
+
+instance ToBuilder (Meta V2) where
+    build Meta{..} = mconcat $ intersperse "\n"
+        [ "[Version 2 Metadata] {"
+        , "  signature = " <> build _mSignature
+        , "  time      = " <> build _mTime
+        , "}"
+        ]
+
+instance AWSSigner V2 where
+    signed AuthEnv{..} r x@Request{..} l t = Signed meta rq
+      where
+        meta = Meta
+            { _mSignature = signature
+            , _mTime      = t
+            }
+
+        rq = clientRequest
+            & method         .~ meth
+            & host           .~ _endpointHost
+            & path           .~ _rqPath
+            & queryString    .~ toBS authorised
+            & requestHeaders .~ headers
+            & requestBody    .~ _bdyBody _rqBody
+
+        meth = toBS _rqMethod
+
+        Endpoint{..} = endpoint svc r
+
+        authorised = pair "Signature" (urlEncode True signature) query
+
+        signature = Base64.encode
+            . hmacSHA256 (toBS _authSecret)
+            $ BS.intercalate "\n"
+                [ meth
+                , _endpointHost
+                , _rqPath
+                , toBS query
+                ]
+
+        query =
+             pair "Version"          (_svcVersion svc)
+           . pair "SignatureVersion" ("2" :: ByteString)
+           . pair "SignatureMethod"  ("HmacSHA256" :: ByteString)
+           . pair "Timestamp"        time
+           . pair "AWSAccessKeyId"   (toBS _authAccess)
+           $ _rqQuery <> maybe mempty toQuery token
+
+        token = ("SecurityToken" :: ByteString,) . toBS <$> _authToken
+
+        headers = hdr hDate time _rqHeaders
+
+        time = toBS (LocaleTime l t :: ISO8601)
+
+        svc = serviceOf x
diff --git a/src/Network/AWS/Signing/Internal/V3.hs b/src/Network/AWS/Signing/Internal/V3.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/AWS/Signing/Internal/V3.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TupleSections     #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+-- Module      : Network.AWS.Signing.Internal.V3
+-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
+-- License     : This Source Code Form is subject to the terms of
+--               the Mozilla Public License, v. 2.0.
+--               A copy of the MPL can be found in the LICENSE file or
+--               you can obtain it at http://mozilla.org/MPL/2.0/.
+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+
+module Network.AWS.Signing.Internal.V3
+    ( V3
+    ) where
+
+import           Control.Applicative
+import           Control.Lens
+import           Data.ByteString              (ByteString)
+import qualified Data.ByteString.Base64       as Base64
+import           Data.List                    (sortBy, intersperse)
+import           Data.Maybe
+import           Data.Monoid
+import           Data.Ord
+import           Data.Time
+import           Network.AWS.Data
+import           Network.AWS.Request.Internal
+import           Network.AWS.Signing.Internal
+import           Network.AWS.Types
+import           Network.HTTP.Types.Header
+
+data V3
+
+data instance Meta V3 = Meta
+    { _mSignature :: ByteString
+    , _mTime      :: UTCTime
+    }
+
+instance ToBuilder (Meta V3) where
+    build Meta{..} = mconcat $ intersperse "\n"
+        [ "[Version 3 Metadata] {"
+        , "  signature = " <> build _mSignature
+        , "  time      = " <> build _mTime
+        , "}"
+        ]
+
+instance AWSSigner V3 where
+    signed AuthEnv{..} r x@Request{..} l t = Signed meta rq
+      where
+        meta = Meta
+            { _mSignature = signature
+            , _mTime      = t
+            }
+
+        rq = clientRequest
+            & method         .~ toBS _rqMethod
+            & host           .~ _endpointHost
+            & path           .~ _rqPath
+            & queryString    .~ toBS _rqQuery
+            & requestHeaders .~ headers
+            & requestBody    .~ _bdyBody _rqBody
+
+        Endpoint{..} = endpoint (serviceOf x) r
+
+        headers = sortBy (comparing fst)
+            . hdr hAMZAuth authorisation
+            . hdr hHost _endpointHost
+            . hdr hDate (toBS (LocaleTime l t :: RFC822))
+            $ _rqHeaders
+                ++ maybeToList ((hAMZToken,) . toBS <$> _authToken)
+
+        authorisation = "AWS3-HTTPS AWSAccessKeyId="
+            <> toBS _authAccess
+            <> ", Algorithm=HmacSHA256, Signature="
+            <> signature
+
+        signature = Base64.encode
+            $ hmacSHA256 (toBS _authSecret) (toBS (LocaleTime l t :: AWSTime))
diff --git a/src/Network/AWS/Signing/Internal/V4.hs b/src/Network/AWS/Signing/Internal/V4.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/AWS/Signing/Internal/V4.hs
@@ -0,0 +1,200 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TupleSections     #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+-- Module      : Network.AWS.Signing.Internal.V4
+-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
+-- License     : This Source Code Form is subject to the terms of
+--               the Mozilla Public License, v. 2.0.
+--               A copy of the MPL can be found in the LICENSE file or
+--               you can obtain it at http://mozilla.org/MPL/2.0/.
+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+
+module Network.AWS.Signing.Internal.V4
+    ( V4
+    ) where
+
+import           Control.Applicative
+import           Control.Lens
+import qualified Crypto.Hash.SHA256           as SHA256
+import           Data.ByteString              (ByteString)
+import qualified Data.ByteString.Base16       as Base16
+import qualified Data.ByteString.Char8        as BS
+import qualified Data.CaseInsensitive         as CI
+import qualified Data.Foldable                as Fold
+import           Data.Function
+import           Data.List                    (groupBy, intersperse, sortBy, sort)
+import           Data.Maybe
+import           Data.Monoid
+import           Data.Ord
+import           Data.Time
+import           Network.AWS.Data
+import           Network.AWS.Request.Internal
+import           Network.AWS.Signing.Internal
+import           Network.AWS.Types
+import           Network.HTTP.Types.Header
+import           System.Locale
+
+data V4
+
+data instance Meta V4 = Meta
+    { _mAlgorithm :: ByteString
+    , _mScope     :: ByteString
+    , _mSigned    :: ByteString
+    , _mCReq      :: ByteString
+    , _mSTS       :: ByteString
+    , _mSignature :: ByteString
+    , _mTime      :: UTCTime
+    }
+
+instance ToBuilder (Meta V4) where
+    build Meta{..} = mconcat $ intersperse "\n"
+        [ "[Version 4 Metadata] {"
+        , "  algorithm         = " <> build _mAlgorithm
+        , "  credential scope  = " <> build _mScope
+        , "  signed headers    = " <> build _mSigned
+        , "  canonical request = {"
+        , build _mCReq
+        , "  }"
+        , "  string to sign    = " <> build _mSTS
+        , "  signature         = " <> build _mSignature
+        , "  time              = " <> build _mTime
+        , "}"
+        ]
+
+instance AWSPresigner V4 where
+    presigned a r rq l t x = out
+        & sgRequest . queryString <>~ auth (out ^. sgMeta)
+      where
+        out = finalise Nothing qry service a r rq l t
+
+        qry cs sh =
+              pair (CI.original hAMZAlgorithm)     algorithm
+            . pair (CI.original hAMZCredential)    cs
+            . pair (CI.original hAMZDate)          (LocaleTime l t :: ISO8601)
+            . pair (CI.original hAMZExpires)       (LocaleTime l x :: ISO8601)
+            . pair (CI.original hAMZSignedHeaders) sh
+            . pair (CI.original hAMZToken)         (toBS <$> _authToken a)
+            . pair (CI.original hAMZContentSHA256) ("UNSIGNED-PAYLOAD" :: ByteString)
+
+        auth = mappend "&X-AMZ-Signature=" . _mSignature
+
+instance AWSSigner V4 where
+    signed a r rq l t = out
+        & sgRequest
+        %~ requestHeaders
+        %~ hdr hAuthorization (authorisation $ out ^. sgMeta)
+      where
+        out = finalise (Just "AWS4") (\_ _ -> id) service a r inp l t
+
+        inp = rq & rqHeaders %~ hdrs (maybeToList tok)
+
+        tok = (hAMZToken,) . toBS <$> _authToken a
+
+authorisation :: Meta V4 -> ByteString
+authorisation Meta{..} = BS.concat
+    [ _mAlgorithm
+    , " Credential="
+    , _mScope
+    , ", SignedHeaders="
+    , _mSigned
+    , ", Signature="
+    , _mSignature
+    ]
+
+algorithm :: ByteString
+algorithm = "AWS4-HMAC-SHA256"
+
+finalise :: Maybe ByteString
+         -> (ByteString -> ByteString -> Query -> Query)
+         -> Service (Sv a)
+         -> AuthEnv
+         -> Region
+         -> Request a
+         -> TimeLocale
+         -> UTCTime
+         -> Signed a V4
+finalise p qry s@Service{..} AuthEnv{..} r Request{..} l t = Signed meta rq
+  where
+    meta = Meta
+        { _mAlgorithm = algorithm
+        , _mCReq      = canonicalRequest
+        , _mScope     = toBS _authAccess <> "/" <> credentialScope
+        , _mSigned    = signedHeaders
+        , _mSTS       = stringToSign
+        , _mSignature = signature
+        , _mTime      = t
+        }
+
+    rq = clientRequest
+        & method         .~ meth
+        & host           .~ _endpointHost
+        & path           .~ _rqPath
+        & queryString    .~ toBS query
+        & requestHeaders .~ headers
+        & requestBody    .~ _bdyBody _rqBody
+
+    meth  = toBS _rqMethod
+    query = qry credentialScope signedHeaders _rqQuery
+
+    Endpoint{..} = endpoint s r
+
+    canonicalQuery = toBS (query & valuesOf %~ Just . fromMaybe "")
+
+    headers = sortBy (comparing fst)
+        . hdr hHost _endpointHost
+        . hdr hAMZDate (toBS (LocaleTime l t :: AWSTime))
+        $ _rqHeaders
+
+    joinedHeaders = map f $ groupBy ((==) `on` fst) headers
+      where
+        f []     = ("", "")
+        f (h:hs) = (fst h, g (h : hs))
+
+        g = BS.intercalate "," . sort . map snd
+
+    signedHeaders = mconcat
+        . intersperse ";"
+        . map (CI.foldedCase . fst)
+        $ joinedHeaders
+
+    canonicalHeaders = Fold.foldMap f joinedHeaders
+      where
+        f (k, v) = CI.foldedCase k
+            <> ":"
+            <> stripBS v
+            <> "\n"
+
+    canonicalRequest = mconcat $ intersperse "\n"
+       [ meth
+       , collapseURI _rqPath
+       , canonicalQuery
+       , canonicalHeaders
+       , signedHeaders
+       , bodyHash _rqBody
+       ]
+
+    scope =
+        [ toBS (LocaleTime l t :: BasicTime)
+        , toBS _endpointScope
+        , toBS _svcPrefix
+        , "aws4_request"
+        ]
+
+    credentialScope = BS.intercalate "/" scope
+
+    signingKey = Fold.foldl1 hmacSHA256 $
+        maybe (toBS _authSecret) (<> toBS _authSecret) p : scope
+
+    stringToSign = BS.intercalate "\n"
+        [ algorithm
+        , toBS (LocaleTime l t :: AWSTime)
+        , credentialScope
+        , Base16.encode (SHA256.hash canonicalRequest)
+        ]
+
+    signature = Base16.encode (hmacSHA256 signingKey stringToSign)
diff --git a/src/Network/AWS/Signing/V2.hs b/src/Network/AWS/Signing/V2.hs
deleted file mode 100644
--- a/src/Network/AWS/Signing/V2.hs
+++ /dev/null
@@ -1,101 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards   #-}
-{-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE TypeFamilies      #-}
-
--- Module      : Network.AWS.Signaing.V2
--- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
--- License     : This Source Code Form is subject to the terms of
---               the Mozilla Public License, v. 2.0.
---               A copy of the MPL can be found in the LICENSE file or
---               you can obtain it at http://mozilla.org/MPL/2.0/.
--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
--- Stability   : experimental
--- Portability : non-portable (GHC extensions)
-
-module Network.AWS.Signing.V2
-    (
-    -- * Types
-      V2
-    , Meta (..)
-
-    -- * Re-exports
-    , module Network.AWS.Signing.Internal
-    ) where
-
-import           Control.Applicative
-import           Control.Lens
-import           Data.ByteString              (ByteString)
-import qualified Data.ByteString.Base64       as Base64
-import qualified Data.ByteString.Char8        as BS
-import           Data.List                    (intersperse)
-import           Data.Monoid
-import           Data.Time
-import           Network.AWS.Data
-import           Network.AWS.Request.Internal
-import           Network.AWS.Signing.Internal
-import           Network.AWS.Types
-import           Network.HTTP.Types           hiding (toQuery)
-
-data V2
-
-data instance Meta V2 = Meta
-    { _mSignature :: ByteString
-    , _mTime      :: UTCTime
-    }
-
-instance ToBuilder (Meta V2) where
-    build Meta{..} = mconcat $ intersperse "\n"
-        [ "[Version 2 Metadata] {"
-        , "  signature = " <> build _mSignature
-        , "  time      = " <> build _mTime
-        , "}"
-        ]
-
-instance AWSSigner V2 where
-    signed AuthEnv{..} r x@Request{..} l t = Signed meta rq
-      where
-        meta = Meta
-            { _mSignature = signature
-            , _mTime      = t
-            }
-
-        rq = clientRequest
-            & method         .~ meth
-            & host           .~ _endpointHost
-            & path           .~ _rqPath
-            & queryString    .~ toBS authorised
-            & requestHeaders .~ headers
-            & requestBody    .~ _bdyBody _rqBody
-
-        meth = toBS _rqMethod
-
-        Endpoint{..} = endpoint svc r
-
-        authorised = pair "Signature" (urlEncode True signature) query
-
-        signature = Base64.encode
-            . hmacSHA256 (toBS _authSecret)
-            $ BS.intercalate "\n"
-                [ meth
-                , _endpointHost
-                , _rqPath
-                , toBS query
-                ]
-
-        query =
-             pair "Version"          (_svcVersion svc)
-           . pair "SignatureVersion" ("2" :: ByteString)
-           . pair "SignatureMethod"  ("HmacSHA256" :: ByteString)
-           . pair "Timestamp"        time
-           . pair "AWSAccessKeyId"   (toBS _authAccess)
-           $ _rqQuery <> maybe mempty toQuery token
-
-        token = ("SecurityToken" :: ByteString,) . toBS <$> _authToken
-
-        headers = hdr hDate time _rqHeaders
-
-        time = toBS (LocaleTime l t :: ISO8601)
-
-        svc = serviceOf x
diff --git a/src/Network/AWS/Signing/V3.hs b/src/Network/AWS/Signing/V3.hs
deleted file mode 100644
--- a/src/Network/AWS/Signing/V3.hs
+++ /dev/null
@@ -1,88 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards   #-}
-{-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE TypeFamilies      #-}
-
--- Module      : Network.AWS.Signing.V3
--- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
--- License     : This Source Code Form is subject to the terms of
---               the Mozilla Public License, v. 2.0.
---               A copy of the MPL can be found in the LICENSE file or
---               you can obtain it at http://mozilla.org/MPL/2.0/.
--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
--- Stability   : experimental
--- Portability : non-portable (GHC extensions)
-
-module Network.AWS.Signing.V3
-    (
-    -- * Types
-      V3
-    , Meta (..)
-
-    -- * Re-exports
-    , module Network.AWS.Signing.Internal
-    ) where
-
-import           Control.Applicative
-import           Control.Lens
-import           Data.ByteString              (ByteString)
-import qualified Data.ByteString.Base64       as Base64
-import           Data.List                    (sortBy, intersperse)
-import           Data.Maybe
-import           Data.Monoid
-import           Data.Ord
-import           Data.Time
-import           Network.AWS.Data
-import           Network.AWS.Request.Internal
-import           Network.AWS.Signing.Internal
-import           Network.AWS.Types
-import           Network.HTTP.Types.Header
-
-data V3
-
-data instance Meta V3 = Meta
-    { _mSignature :: ByteString
-    , _mTime      :: UTCTime
-    }
-
-instance ToBuilder (Meta V3) where
-    build Meta{..} = mconcat $ intersperse "\n"
-        [ "[Version 3 Metadata] {"
-        , "  signature = " <> build _mSignature
-        , "  time      = " <> build _mTime
-        , "}"
-        ]
-
-instance AWSSigner V3 where
-    signed AuthEnv{..} r x@Request{..} l t = Signed meta rq
-      where
-        meta = Meta
-            { _mSignature = signature
-            , _mTime      = t
-            }
-
-        rq = clientRequest
-            & method         .~ toBS _rqMethod
-            & host           .~ _endpointHost
-            & path           .~ _rqPath
-            & queryString    .~ toBS _rqQuery
-            & requestHeaders .~ headers
-            & requestBody    .~ _bdyBody _rqBody
-
-        Endpoint{..} = endpoint (serviceOf x) r
-
-        headers = sortBy (comparing fst)
-            . hdr hAMZAuth authorisation
-            . hdr hHost _endpointHost
-            . hdr hDate (toBS (LocaleTime l t :: RFC822))
-            $ _rqHeaders
-                ++ maybeToList ((hAMZToken,) . toBS <$> _authToken)
-
-        authorisation = "AWS3-HTTPS AWSAccessKeyId="
-            <> toBS _authAccess
-            <> ", Algorithm=HmacSHA256, Signature="
-            <> signature
-
-        signature = Base64.encode
-            $ hmacSHA256 (toBS _authSecret) (toBS (LocaleTime l t :: AWSTime))
diff --git a/src/Network/AWS/Signing/V4.hs b/src/Network/AWS/Signing/V4.hs
deleted file mode 100644
--- a/src/Network/AWS/Signing/V4.hs
+++ /dev/null
@@ -1,207 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards   #-}
-{-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE TypeFamilies      #-}
-
--- Module      : Network.AWS.Signing.V4
--- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
--- License     : This Source Code Form is subject to the terms of
---               the Mozilla Public License, v. 2.0.
---               A copy of the MPL can be found in the LICENSE file or
---               you can obtain it at http://mozilla.org/MPL/2.0/.
--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
--- Stability   : experimental
--- Portability : non-portable (GHC extensions)
-
-module Network.AWS.Signing.V4
-    (
-    -- * Types
-      V4
-    , Meta (..)
-    , authorisation
-
-    -- * Re-exports
-    , module Network.AWS.Signing.Internal
-    ) where
-
-import           Control.Applicative
-import           Control.Lens
-import qualified Crypto.Hash.SHA256           as SHA256
-import           Data.ByteString              (ByteString)
-import qualified Data.ByteString.Base16       as Base16
-import qualified Data.ByteString.Char8        as BS
-import qualified Data.CaseInsensitive         as CI
-import qualified Data.Foldable                as Fold
-import           Data.Function
-import           Data.List                    (groupBy, intersperse, sortBy, sort)
-import           Data.Maybe
-import           Data.Monoid
-import           Data.Ord
-import           Data.Time
-import           Network.AWS.Data
-import           Network.AWS.Request.Internal
-import           Network.AWS.Signing.Internal
-import           Network.AWS.Types
-import           Network.HTTP.Types.Header
-import           System.Locale
-
-data V4
-
-data instance Meta V4 = Meta
-    { _mAlgorithm :: ByteString
-    , _mScope     :: ByteString
-    , _mSigned    :: ByteString
-    , _mCReq      :: ByteString
-    , _mSTS       :: ByteString
-    , _mSignature :: ByteString
-    , _mTime      :: UTCTime
-    }
-
-instance ToBuilder (Meta V4) where
-    build Meta{..} = mconcat $ intersperse "\n"
-        [ "[Version 4 Metadata] {"
-        , "  algorithm         = " <> build _mAlgorithm
-        , "  credential scope  = " <> build _mScope
-        , "  signed headers    = " <> build _mSigned
-        , "  canonical request = {"
-        , build _mCReq
-        , "  }"
-        , "  string to sign    = " <> build _mSTS
-        , "  signature         = " <> build _mSignature
-        , "  time              = " <> build _mTime
-        , "}"
-        ]
-
-instance AWSPresigner V4 where
-    presigned a r rq l t x = out
-        & sgRequest . queryString <>~ auth (out ^. sgMeta)
-      where
-        out = finalise Nothing qry service a r rq l t
-
-        qry cs sh =
-              pair (CI.original hAMZAlgorithm)     algorithm
-            . pair (CI.original hAMZCredential)    cs
-            . pair (CI.original hAMZDate)          (LocaleTime l t :: ISO8601)
-            . pair (CI.original hAMZExpires)       (LocaleTime l x :: ISO8601)
-            . pair (CI.original hAMZSignedHeaders) sh
-            . pair (CI.original hAMZToken)         (toBS <$> _authToken a)
-            . pair (CI.original hAMZContentSHA256) ("UNSIGNED-PAYLOAD" :: ByteString)
-
-        auth = mappend "&X-AMZ-Signature=" . _mSignature
-
-instance AWSSigner V4 where
-    signed a r rq l t = out
-        & sgRequest
-        %~ requestHeaders
-        %~ hdr hAuthorization (authorisation $ out ^. sgMeta)
-      where
-        out = finalise (Just "AWS4") (\_ _ -> id) service a r inp l t
-
-        inp = rq & rqHeaders %~ hdrs (maybeToList tok)
-
-        tok = (hAMZToken,) . toBS <$> _authToken a
-
-authorisation :: Meta V4 -> ByteString
-authorisation Meta{..} = BS.concat
-    [ _mAlgorithm
-    , " Credential="
-    , _mScope
-    , ", SignedHeaders="
-    , _mSigned
-    , ", Signature="
-    , _mSignature
-    ]
-
-algorithm :: ByteString
-algorithm = "AWS4-HMAC-SHA256"
-
-finalise :: Maybe ByteString
-         -> (ByteString -> ByteString -> Query -> Query)
-         -> Service (Sv a)
-         -> AuthEnv
-         -> Region
-         -> Request a
-         -> TimeLocale
-         -> UTCTime
-         -> Signed a V4
-finalise p qry s@Service{..} AuthEnv{..} r Request{..} l t = Signed meta rq
-  where
-    meta = Meta
-        { _mAlgorithm = algorithm
-        , _mCReq      = canonicalRequest
-        , _mScope     = toBS _authAccess <> "/" <> credentialScope
-        , _mSigned    = signedHeaders
-        , _mSTS       = stringToSign
-        , _mSignature = signature
-        , _mTime      = t
-        }
-
-    rq = clientRequest
-        & method         .~ meth
-        & host           .~ _endpointHost
-        & path           .~ _rqPath
-        & queryString    .~ toBS query
-        & requestHeaders .~ headers
-        & requestBody    .~ _bdyBody _rqBody
-
-    meth  = toBS _rqMethod
-    query = qry credentialScope signedHeaders _rqQuery
-
-    Endpoint{..} = endpoint s r
-
-    canonicalQuery = toBS (query & valuesOf %~ Just . fromMaybe "")
-
-    headers = sortBy (comparing fst)
-        . hdr hHost _endpointHost
-        . hdr hAMZDate (toBS (LocaleTime l t :: AWSTime))
-        $ _rqHeaders
-
-    joinedHeaders = map f $ groupBy ((==) `on` fst) headers
-      where
-        f []     = ("", "")
-        f (h:hs) = (fst h, g (h : hs))
-
-        g = BS.intercalate "," . sort . map snd
-
-    signedHeaders = mconcat
-        . intersperse ";"
-        . map (CI.foldedCase . fst)
-        $ joinedHeaders
-
-    canonicalHeaders = Fold.foldMap f joinedHeaders
-      where
-        f (k, v) = CI.foldedCase k
-            <> ":"
-            <> stripBS v
-            <> "\n"
-
-    canonicalRequest = mconcat $ intersperse "\n"
-       [ meth
-       , collapseURI _rqPath
-       , canonicalQuery
-       , canonicalHeaders
-       , signedHeaders
-       , bodyHash _rqBody
-       ]
-
-    scope =
-        [ toBS (LocaleTime l t :: BasicTime)
-        , toBS _endpointScope
-        , toBS _svcPrefix
-        , "aws4_request"
-        ]
-
-    credentialScope = BS.intercalate "/" scope
-
-    signingKey = Fold.foldl1 hmacSHA256 $
-        maybe (toBS _authSecret) (<> toBS _authSecret) p : scope
-
-    stringToSign = BS.intercalate "\n"
-        [ algorithm
-        , toBS (LocaleTime l t :: AWSTime)
-        , credentialScope
-        , Base16.encode (SHA256.hash canonicalRequest)
-        ]
-
-    signature = Base16.encode (hmacSHA256 signingKey stringToSign)
diff --git a/src/Network/AWS/Types.hs b/src/Network/AWS/Types.hs
--- a/src/Network/AWS/Types.hs
+++ b/src/Network/AWS/Types.hs
@@ -39,6 +39,7 @@
     , Abbrev
     , AWSService    (..)
     , Service       (..)
+    , serviceOf
 
     -- * Endpoints
     , Endpoint      (..)
@@ -78,11 +79,8 @@
 
     -- * Regions
     , Region        (..)
-    , Zone          (..)
-    , zRegion
-    , zSuffix
 
-    -- * Shared
+    -- * Query Actions
     , Action        (..)
 
     -- * Convenience
@@ -99,11 +97,9 @@
 import           Control.Monad.IO.Class
 import           Control.Monad.Trans.Resource
 import           Data.Aeson                   hiding (Error)
-import qualified Data.Attoparsec.Text         as AText
 import           Data.ByteString              (ByteString)
 import qualified Data.ByteString              as BS
 import qualified Data.CaseInsensitive         as CI
-import           Data.Char
 import           Data.Conduit
 import           Data.Default.Class
 import qualified Data.HashSet                 as Set
@@ -113,12 +109,11 @@
 import           Data.Monoid
 import           Data.String
 import           Data.Text                    (Text)
-import qualified Data.Text                    as Text
 import qualified Data.Text.Encoding           as Text
 import           Data.Time
 import           Data.Typeable
 import           GHC.Generics
-import           Network.AWS.Data             hiding ((.:), (.:?))
+import           Network.AWS.Data
 import qualified Network.HTTP.Client          as Client
 import           Network.HTTP.Client          hiding (Request, Response)
 import           Network.HTTP.Types.Header
@@ -171,6 +166,9 @@
             -> Status
             -> Maybe (LazyByteString -> ServiceError (Er a))
 
+serviceOf :: AWSService (Sv a) => Request a -> Service (Sv a)
+serviceOf = const service
+
 -- | An alias for the common response 'Either' containing a service error in the
 -- 'Left' case, or the expected response in the 'Right'.
 type Response a = Either (ServiceError (Er (Sv a))) (Rs a)
@@ -235,30 +233,16 @@
 
 -- | Access key credential.
 newtype AccessKey = AccessKey ByteString
-    deriving (Eq, Show, IsString)
-
-instance ToByteString AccessKey where
-    toBS (AccessKey k) = k
-
-instance ToText AccessKey where
-    toText = Text.decodeUtf8 . toBS
+    deriving (Eq, Show, IsString, ToText, ToByteString, ToBuilder)
 
 -- | Secret key credential.
 newtype SecretKey = SecretKey ByteString
-    deriving (Eq, Show, IsString)
-
-instance ToByteString SecretKey where
-    toBS (SecretKey k) = k
-
-instance ToText SecretKey where
-    toText = Text.decodeUtf8 . toBS
+    deriving (Eq, IsString, ToText, ToByteString)
 
--- | A security token used by STS to temporarily authorise access to an AWS resource.
+-- | A security token used by STS to temporarily authorise access to
+-- an AWS resource.
 newtype SecurityToken = SecurityToken ByteString
-    deriving (Eq, Show, IsString)
-
-instance ToByteString SecurityToken where
-    toBS (SecurityToken t) = t
+    deriving (Eq, IsString, ToText, ToByteString)
 
 -- | The authorisation environment.
 data AuthEnv = AuthEnv
@@ -277,12 +261,26 @@
       where
         f g = fmap (g . Text.encodeUtf8)
 
--- | An authorisation environment containing AWS credentials and potentially
--- a reference which can be refreshed out-of-band as they expire.
+instance ToBuilder AuthEnv where
+    build AuthEnv{..} = mconcat $ intersperse "\n"
+        [ "[Authentication] {"
+        , " access key     = " <> build _authAccess
+        , " secret key     = ****"
+        , " security token = ****"
+        , " expiry         = " <> build _authExpiry
+        , "}"
+        ]
+
+-- | An authorisation environment containing AWS credentials, and potentially
+-- a reference which can be refreshed out-of-band as temporary credentials expire.
 data Auth
     = Ref  ThreadId (IORef AuthEnv)
     | Auth AuthEnv
 
+instance ToBuilder Auth where
+    build (Ref t _) = "[Authentication] { <thread:" <> build (show t) <> "> }"
+    build (Auth  e) = build e
+
 withAuth :: MonadIO m => Auth -> (AuthEnv -> m a) -> m a
 withAuth (Auth  e) f = f e
 withAuth (Ref _ r) f = liftIO (readIORef r) >>= f
@@ -453,22 +451,11 @@
         SaoPaulo        -> "sa-east-1"
 
 instance ToByteString Region
+instance ToBuilder    Region
 
 instance FromXML Region where parseXML = parseXMLText "Region"
 instance ToXML   Region where toXML    = toXMLText
 
--- | An availability zone.
-data Zone = Zone
-    { _zRegion :: !Region
-    , _zSuffix :: !Char
-    } deriving (Eq, Ord, Read, Show)
-
-instance FromText Zone where
-    parser = Zone <$> parser <*> AText.satisfy isAlpha <* AText.endOfInput
-
-instance ToText Zone where
-    toText Zone{..} = toText _zRegion `Text.snoc` _zSuffix
-
 -- | A service's query action.
 newtype Action = Action Text
     deriving (Eq, Ord, Show, IsString, ToText, ToByteString)
@@ -499,4 +486,3 @@
 
 makePrisms ''ServiceError
 makeLenses ''Request
-makeLenses ''Zone
