diff --git a/hikaru.cabal b/hikaru.cabal
index ee6ef198dbcdba29c5982d22d8a7f49f372581be..394387f5e9ac90e7e1d75b6c14d60692a50f35b9 100644
--- a/hikaru.cabal
+++ b/hikaru.cabal
@@ -81,6 +81,7 @@ common common
       , memory             >=0.15 && <0.16
       , mtl                >=2.2  && <2.3
       , praha              >=0.1  && <0.2
+      , praha-config       >=0.1  && <0.2
       , resourcet          >=1.2  && <1.3
       , text               >=1.2  && <1.3
       , time               >=1.9  && <1.12
@@ -98,7 +99,6 @@ library
         Hikaru
         Hikaru.Develop
         Hikaru.Action
-        Hikaru.Config
         Hikaru.CSRF
         Hikaru.Dispatch
         Hikaru.Widget
diff --git a/lib/Hikaru.hs b/lib/Hikaru.hs
index 9b4c5876c7756b622f3ad0132c89e723332b00fe..1aeccba8f2bcea10dbc08bff38488f595f2fa914 100644
--- a/lib/Hikaru.hs
+++ b/lib/Hikaru.hs
@@ -14,7 +14,6 @@ module Hikaru
   (
   -- * Exported Modules
     module Hikaru.Action
-  , module Hikaru.Config
   , module Hikaru.CSRF
   , module Hikaru.Develop
   , module Hikaru.Dispatch
@@ -27,7 +26,6 @@ module Hikaru
   )
 where
   import Hikaru.Action
-  import Hikaru.Config
   import Hikaru.CSRF
   import Hikaru.Develop
   import Hikaru.Dispatch
diff --git a/lib/Hikaru/Action.hs b/lib/Hikaru/Action.hs
index 1d7edbd515855db0ae993a5431c3844fd16982a4..9bb657e3159a763d7cdb9320e156dfd488d93f57 100644
--- a/lib/Hikaru/Action.hs
+++ b/lib/Hikaru/Action.hs
@@ -97,10 +97,6 @@ module Hikaru.Action
   , dropCache
   , dropCaches
 
-  -- ** Configuration
-  , getConfigMaybe
-  , getConfigDefault
-
   -- ** Finalizing
   , registerFinalizer
 
@@ -127,7 +123,6 @@ where
   import Data.Binary.Builder
   import Data.Dynamic
   import Data.List (deleteBy, lookup, map, filter)
-  import Hikaru.Config
   import Hikaru.Media
   import Hikaru.Types
   import Lucid
@@ -204,7 +199,6 @@ where
   data ActionEnv
     = ActionEnv
       { aeRequest      :: Request
-      , aeConfig       :: Config
       , aeBody         :: IORef RequestBody
       , aeRespStatus   :: IORef Status
       , aeRespHeaders  :: IORef ResponseHeaders
@@ -226,9 +220,9 @@ where
   --
   -- Whole operation is bracketed to ensure all finalizers are run.
   --
-  respond :: Config -> (ActionEnv -> IO ()) -> Application
-  respond cfg run req resp = do
-    env <- makeActionEnv cfg req
+  respond :: (ActionEnv -> IO ()) -> Application
+  respond run req resp = do
+    env <- makeActionEnv req
 
     bracket_ (return ()) (finalize env) do
       _   <- run env
@@ -278,10 +272,9 @@ where
   -- |
   -- Create an initial action environment to handle given 'Request'.
   --
-  makeActionEnv :: Config -> Request -> IO ActionEnv
-  makeActionEnv cfg req = do
+  makeActionEnv :: Request -> IO ActionEnv
+  makeActionEnv req = do
     aeRequest     <- pure req
-    aeConfig      <- pure cfg
     aeBody        <- newIORef BodyUnparsed
     aeRespStatus  <- newIORef status200
     aeRespHeaders <- newIORef []
@@ -1239,27 +1232,6 @@ where
     modifyActionField aeCache (const Map.empty)
 
 
-  -- Configuration -----------------------------------------------------------
-
-
-  -- |
-  -- Lookup a configuration value.
-  --
-  getConfigMaybe :: (MonadAction m, Param a) => Text -> m (Maybe a)
-  getConfigMaybe name = do
-    ActionEnv{aeConfig} <- getActionEnv
-    return $ fromParam =<< Map.lookup name aeConfig
-
-
-  -- |
-  -- Lookup a configuration value with a fallback to return if not found.
-  --
-  getConfigDefault :: (MonadAction m, Param a) => Text -> a -> m a
-  getConfigDefault name value = do
-    ActionEnv{aeConfig} <- getActionEnv
-    return $ fromMaybe value $ fromParam =<< Map.lookup name aeConfig
-
-
   -- Finalizing --------------------------------------------------------------
 
 
diff --git a/lib/Hikaru/CSRF.hs b/lib/Hikaru/CSRF.hs
index d7ceacbceb9e3a2baefec719b682e4b8f3280f94..bd980831d7fb9c2b947922d395c10f1943e741dd 100644
--- a/lib/Hikaru/CSRF.hs
+++ b/lib/Hikaru/CSRF.hs
@@ -16,24 +16,29 @@ module Hikaru.CSRF
   )
 where
   import Praha
+  import Praha.Config.Environment
 
   import Crypto.Hash
   import Crypto.MAC.HMAC
+  import Crypto.Random.Entropy
+
   import Data.Text (splitOn)
   import Data.Time.Clock.POSIX (getPOSIXTime)
 
+  import Data.ByteArray.Encoding
+
   import Hikaru.Action
 
 
   -- |
   -- Generate an anti-CSRF token to be used with forms.
   --
-  -- Uses the @CSRF_SECRET@ configuration key.
+  -- Uses the @HIKARU_SECRET@ configuration key.
   --
   generateToken :: (MonadAction m) => m Text
   generateToken = do
     now    <- getTimestamp
-    secret <- getConfigDefault "CSRF_SECRET" ""
+    secret <- getSecret
 
     let signature = sign now secret
      in return $ mconcat [ tshow now, ":", signature ]
@@ -71,6 +76,21 @@ where
   getTimestamp = round <$> liftIO getPOSIXTime
 
 
+  -- |
+  -- Use the secret from environment or put there an actual one.
+  --
+  getSecret :: (MonadIO m) => m Text
+  getSecret = do
+    maybeSecret <- getConfigMaybe "HIKARU_SECRET"
+
+    case maybeSecret of
+      Just secret -> return secret
+      Nothing -> do
+        secret <- generateSecret 16
+        setConfig "HIKARU_SECRET" secret
+        return secret
+
+
   sign :: Int64 -> Text -> Text
   sign timestamp secret = tshow $ hmacGetDigest digest
     where
@@ -79,4 +99,17 @@ where
       timeBytes   = cs (show timestamp) :: ByteString
 
 
+  -- |
+  -- Generate a random base64-encoded secret of given length
+  -- (in decoded bytes).
+  --
+  generateSecret :: (MonadIO m) => Int -> m Text
+  generateSecret n = do
+    (bstr :: ByteString) <- liftIO $ getEntropy n
+
+    let (bstr64 :: ByteString) = convertToBase Base64 bstr
+     in return (cs bstr64)
+
+
+
 -- vim:set ft=haskell sw=2 ts=2 et:
diff --git a/lib/Hikaru/Config.hs b/lib/Hikaru/Config.hs
deleted file mode 100644
index a52c981b4d35a54f9665996e0b1bac2f09c11bbd..0000000000000000000000000000000000000000
--- a/lib/Hikaru/Config.hs
+++ /dev/null
@@ -1,150 +0,0 @@
---
--- Module      :  Hikaru.Config
--- Copyright   :  Jan Hamal Dvořák
--- License     :  AGPL-3.0-or-later
---
--- Maintainer  :  mordae@anilinux.org
--- Stability   :  unstable
--- Portability :  non-portable (ghc)
---
--- This module provides means to read configuration from environment and files.
---
--- Example:
---
--- @
--- base <- 'configDefault'
--- file <- 'configFromFile' \"site.env\"
--- env  <- 'configFromEnv'
---
--- let cfg = env <> file <> base
--- @
-
-module Hikaru.Config
-  ( Config
-  , configFromEnv
-  , configFromFile
-  , configDefault
-
-  -- * Reading Config
-  , configGet
-  , configGetMaybe
-  , configGetDefault
-
-  -- * Secrets
-  , generateSecret
-  )
-where
-  import Praha
-
-  import Hikaru.Types
-
-  import UnliftIO.Environment
-  import Crypto.Random.Entropy
-  import Data.ByteArray.Encoding
-
-  import Data.Text (lines, strip, drop, span, isPrefixOf)
-  import Data.Text.IO (readFile)
-  import Data.List (map)
-
-  import qualified Data.Map as Map
-
-
-  -- |
-  -- Website configuration to be passed to the action.
-  --
-  type Config = Map.Map Text Text
-
-
-  -- |
-  -- Read configuration from program environment.
-  --
-  configFromEnv :: (MonadIO m) => m Config
-  configFromEnv = Map.fromList <$> map conv <$> getEnvironment
-    where conv (k, v) = (cs k, cs v)
-
-
-  -- |
-  -- Read configuration from file.
-  --
-  -- To read configuration from a file and then update it from the environment
-  -- use "<>" like this:
-  --
-  -- @
-  -- def <- 'configFromFile' \"site.env\"
-  -- env <- 'configFromEnv'
-  --
-  -- let cfg = env <> def
-  -- @
-  --
-  -- Configuration file format is approximately this:
-  --
-  -- @
-  -- # How many seconds before forms need to be reloaded?
-  -- CSRF_VALIDITY = 3600
-  --
-  -- # Secret key to protect against CSRF. Don't tell anyone!
-  -- CSRF_SECRET   = Ain9eec8aighoiri
-  -- @
-  --
-  configFromFile :: (MonadIO m) => FilePath -> m Config
-  configFromFile path = Map.fromList <$> parseFile <$> contents
-    where
-      contents    = liftIO (readFile path)
-      parseFile   = mapMaybe parseLine . lines
-      parseLine   = fmap tidy . fmap parseKV . reject . strip
-      parseKV     = fmap (drop 1) . span (/= '=')
-      reject      = guarded (not . isPrefixOf "#") <=< guarded ("" /=)
-      tidy (k, v) = (strip k, strip v)
-
-
-  -- |
-  -- Generate default configuration with keys required by Hikaru.
-  --
-  -- This includes @CSRF_SECRET@ which should be set to something persistent,
-  -- but if it's not, it must be set to a random value upon startup at least.
-  --
-  configDefault :: (MonadIO m) => m Config
-  configDefault = do
-    secret <- generateSecret 16
-    return $ Map.fromList [ ("CSRF_SECRET",   secret)
-                          , ("CSRF_VALIDITY", "86400")
-                          ]
-
-
-  -- |
-  -- Obtain value of a configuration key or raise an error.
-  -- You really should not use this function, you know?
-  --
-  configGet :: (Param a) => Text -> Config -> a
-  configGet name cfg = case configGetMaybe name cfg of
-                         Just value -> value
-                         Nothing -> error (cs name <> " not set!")
-
-
-  -- |
-  -- Try to obtain value of a configuration key.
-  --
-  configGetMaybe :: (Param a) => Text -> Config -> Maybe a
-  configGetMaybe name cfg = fromParam =<< Map.lookup name cfg
-
-
-  -- |
-  -- Obtain value of a configuration key or the provided default.
-  --
-  configGetDefault :: (Param a) => Text -> a -> Config -> a
-  configGetDefault name value = fromMaybe value . configGetMaybe name
-
-
-  -- |
-  -- Generate a random base64-encoded secret of given length
-  -- (in decoded bytes).
-  --
-  generateSecret :: (MonadIO m) => Int -> m Text
-  generateSecret n = do
-    (bstr :: ByteString) <- liftIO $ getEntropy n
-
-    let (bstr64 :: ByteString) = convertToBase Base64 bstr
-     in return (cs bstr64)
-
-
--- vim:set ft=haskell sw=2 ts=2 et: