diff --git a/avers.cabal b/avers.cabal
--- a/avers.cabal
+++ b/avers.cabal
@@ -1,5 +1,5 @@
 name:                avers
-version:             0.0.4
+version:             0.0.5
 license:             GPL-3
 license-file:        LICENSE
 author:              Tomas Carnecky
@@ -63,5 +63,5 @@
       , inflections
       , influxdb
       , resource-pool
-      , rethinkdb-client-driver >= 0.0.11
+      , rethinkdb-client-driver >= 0.0.17
       , scrypt
diff --git a/src/Avers.hs b/src/Avers.hs
--- a/src/Avers.hs
+++ b/src/Avers.hs
@@ -67,6 +67,8 @@
   , strErr
   , parseValueAs
 
+  , bootstrap
+
     -- * Blob
   , BlobId(..)
   , Blob(..)
diff --git a/src/Avers/Index.hs b/src/Avers/Index.hs
--- a/src/Avers/Index.hs
+++ b/src/Avers/Index.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE GADTs #-}
+
 module Avers.Index where
 
 
@@ -5,15 +7,11 @@
 import qualified Database.RethinkDB as R
 
 
-
-data Index = Index
-  { indexName :: Text
-  , indexType :: IndexType
+data Index a = Index
+  { indexName       :: Text
+  , indexExpression :: R.Exp R.Object -> R.Exp a
   }
 
 
-data IndexType
-  = SimpleIndex Text
-  | CompoundIndex [R.Exp ()]
-  | MultiIndex (R.Exp ())
-  | ArbitraryIndex (R.Exp ()) Bool
+data SomeIndex where
+     SomeIndex :: (R.IsDatum a) => Index a -> SomeIndex
diff --git a/src/Avers/Storage.hs b/src/Avers/Storage.hs
--- a/src/Avers/Storage.hs
+++ b/src/Avers/Storage.hs
@@ -52,11 +52,14 @@
 import           Avers.Patching
 import           Avers.TH
 import           Avers.Views
+import           Avers.Index
 
 import           Avers.Storage.Backend
 import           Avers.Storage.Expressions
 
+import           Prelude
 
+
 requireResult :: AversError -> Maybe a -> Avers a
 requireResult err Nothing  = throwError err
 requireResult _   (Just v) = return v
@@ -614,3 +617,55 @@
 
 mapId:: R.Exp R.Object -> R.Exp Text
 mapId = R.GetField "id"
+
+
+
+
+--------------------------------------------------------------------------------
+-- | Bootstrap the Avers handle: Create necessary tables, indexes, views etc.
+-- This operation is idempotent.
+
+indexF :: R.Exp R.Object -> R.Exp (R.Array R.Datum)
+indexF obj = R.lift [ R.GetField "objectId" obj, R.GetField "revisionId" obj ]
+
+
+bootstrap :: Avers ()
+bootstrap = do
+    createTable "objects" []
+    createTable "sessions" []
+    createTable "blobs" []
+    createTable "secrets" []
+
+    createTable "patches"
+        [ SomeIndex $ Index "objectPatchSequence" indexF
+        ]
+
+    createTable "snapshots"
+        [ SomeIndex $ Index "objectSnapshotSequence" indexF
+        ]
+
+    types <- objectTypes <$> gets config
+    forM_ types $ \(SomeObjectType ObjectType{..}) -> do
+        forM_ otViews $ \(SomeView v@View{..}) -> do
+            createTable (viewTableName v) viewIndices
+
+    return ()
+
+
+createTable :: Text -> [SomeIndex] -> Avers ()
+createTable name indices = do
+    let table = R.Table Nothing $ R.lift name
+    db <- fmap R.Database $ fmap R.lift $ fmap databaseName $ gets config
+
+    tables <- runQuery $ R.ListTables db
+    when (name `V.notElem` tables) $ do
+        liftIO $ putStrLn $ "Creating table '" <> T.unpack name <> "'"
+        void $ runQuery $ R.CreateTable db (R.lift name)
+        void $ runQuery $ R.WaitTable table
+
+    existingIndices <- runQuery $ R.ListIndices table
+    forM_ indices $ \(SomeIndex Index{..}) -> do
+        when (indexName `V.notElem` existingIndices) $ do
+            liftIO $ putStrLn $ "Creating index '" <> T.unpack indexName <> "' on table '" <> T.unpack name <> "'"
+            void $ runQuery $ R.CreateIndex table (R.lift indexName) indexExpression
+            void $ runQuery $ R.WaitIndex table [R.lift indexName]
diff --git a/src/Avers/Types.hs b/src/Avers/Types.hs
--- a/src/Avers/Types.hs
+++ b/src/Avers/Types.hs
@@ -339,6 +339,12 @@
 
 -----------------------------------------------------------------------------
 -- | Secret
+--
+-- A 'Secret' is a password (encrypted with scrypt) that is attached to
+-- a 'SecretId' (for example the 'ObjId' of an account).
+--
+-- It is up to you to ensure that 'SecretId's are unique. If you use 'ObjId's
+-- then they by definition are.
 
 data Secret = Secret
   { secretId    :: !SecretId
@@ -414,11 +420,15 @@
 
 
 -----------------------------------------------------------------------------
--- | The record that is stored in the database.
+-- | The session record that is stored in the database.
+--
+-- A session is a unique identifier attached to a particular object. It
+-- contains the creation date and when it was last accessed. If you need to
+-- store additional data for a session, we recommend to use cookies.
 
 data Session = Session
   { sessionId             :: !SessionId
-  , sessionAccountId      :: !ObjId
+  , sessionObjId          :: !ObjId
   , sessionCreatedAt      :: !UTCTime
   , sessionLastAccessedAt :: !UTCTime
   }
@@ -594,7 +604,7 @@
     -- ^ Function which transforms an Avers Object into a type stored
     -- in the view.
 
-  , viewIndices :: [Index]
+  , viewIndices :: [SomeIndex]
     -- ^ Secondary indices defined on the view.
   }
 
diff --git a/src/Avers/Views.hs b/src/Avers/Views.hs
--- a/src/Avers/Views.hs
+++ b/src/Avers/Views.hs
@@ -10,6 +10,7 @@
 
 import           Control.Monad
 
+import           Data.Text (Text)
 import           Data.Aeson as Aeson
 import           Data.Monoid
 import qualified Data.HashMap.Strict as HMS
@@ -21,13 +22,14 @@
 
 
 
+viewTableName :: View obj a -> Text
+viewTableName View{..} = "view_" <> viewName
+
+
 -- | Construct the table name for the given view. The table names look
 -- something like this: "view_openGames"
 viewTable :: View obj a -> R.Exp R.Table
-viewTable View{..} = R.Table Nothing $ R.lift $ mconcat
-    [ "view_"
-    , viewName
-    ]
+viewTable view = R.Table Nothing $ R.lift $ viewTableName view
 
 
 data Record a = Record
