diff --git a/lib/Proteome/Init.hs b/lib/Proteome/Init.hs
--- a/lib/Proteome/Init.hs
+++ b/lib/Proteome/Init.hs
@@ -12,11 +12,10 @@
 import System.Log.Logger (updateGlobalLogger, setLevel, Priority(ERROR))
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Neovim (Neovim)
-import UnliftIO.STM (TVar, newTVarIO)
 import Ribosome.Config.Setting (settingE, updateSetting)
 import Ribosome.Data.Ribo (Ribo)
 import qualified Ribosome.Data.Ribo as Ribo (inspect)
-import Ribosome.Data.Ribosome (Ribosome(Ribosome))
+import Ribosome.Data.Ribosome (Ribosome(Ribosome), newInternalTVar)
 import Ribosome.Internal.IO (retypeNeovim)
 import Proteome.Data.Env (Env(Env))
 import qualified Proteome.Data.Env as Env (mainProject)
@@ -75,11 +74,11 @@
   main <- mainProject
   initWithMain main
 
-initialize :: Neovim e (TVar Env)
+initialize :: Neovim e Env
 initialize = do
   liftIO $ updateGlobalLogger "Neovim.Plugin" (setLevel ERROR)
-  env <- retypeNeovim (Ribosome "proteome") initialize'
-  newTVarIO env
+  internal <- newInternalTVar
+  retypeNeovim (Ribosome "proteome" internal) initialize'
 
 proteomeStage1 :: Proteome ()
 proteomeStage1 = loadBuffers
diff --git a/lib/Proteome/PersistBuffers.hs b/lib/Proteome/PersistBuffers.hs
--- a/lib/Proteome/PersistBuffers.hs
+++ b/lib/Proteome/PersistBuffers.hs
@@ -17,6 +17,7 @@
 import Neovim (vim_get_current_buffer', vim_get_buffers', buffer_get_name', vim_command')
 import Ribosome.Api.Buffer (edit)
 import Ribosome.Persist (persistStore, persistLoad)
+import Ribosome.Data.Ribo (lockOrSkip)
 import Proteome.Data.Proteome (Proteome)
 import Proteome.Data.Project (
   Project(Project),
@@ -45,6 +46,7 @@
     Project (DirProject (ProjectName name) _ (Just (ProjectType tpe))) _ _ _ -> Just $ tpe </> name
     _ -> Nothing
 
+-- TODO lock process in state to avoid multiple processes trying to access the file
 storeBuffers' :: FilePath -> Proteome ()
 storeBuffers' path = do
   active <- vim_get_current_buffer'
@@ -58,16 +60,21 @@
 decodePersistBuffers :: FilePath -> Proteome (Either String PersistBuffers)
 decodePersistBuffers path = runExceptT $ persistLoad (path </> "buffers")
 
+-- TODO only restore current buffer when none is active
 restoreBuffers :: PersistBuffers -> Proteome ()
 restoreBuffers (PersistBuffers current' buffers') = do
   mapM_ edit current'
   traverse_ (\a -> vim_command' ("silent! badd " ++ a)) buffers'
 
-loadBuffers' :: FilePath -> Proteome ()
-loadBuffers' path = do
+unsafeLoadBuffers :: FilePath -> Proteome ()
+unsafeLoadBuffers path = do
   pb <- decodePersistBuffers path
   mapM_ restoreBuffers pb
 
+safeLoadBuffers :: FilePath -> Proteome ()
+safeLoadBuffers path =
+  lockOrSkip "load-buffers" $ unsafeLoadBuffers path
+
 storeBuffers :: Proteome ()
 storeBuffers = do
   sub <- projectSubPath
@@ -76,4 +83,4 @@
 loadBuffers :: Proteome ()
 loadBuffers = do
   sub <- projectSubPath
-  mapM_ loadBuffers' sub
+  mapM_ safeLoadBuffers sub
diff --git a/lib/Proteome/Plugin.hs b/lib/Proteome/Plugin.hs
--- a/lib/Proteome/Plugin.hs
+++ b/lib/Proteome/Plugin.hs
@@ -7,18 +7,18 @@
 
 import UnliftIO.STM (TVar)
 import Neovim (Plugin(..), function', Neovim, StartupConfig, NeovimConfig, NeovimPlugin, Synchronous(..), wrapPlugin)
-import Ribosome.Data.Ribosome (Ribosome(Ribosome))
-import Proteome.Init
-import Proteome.Data.Env
+import Ribosome.Data.Ribosome (Ribosome, newRibosome)
+import Proteome.Init (proteomePoll, proteomeStage1, proteomeStage2, proteomeStage4, initialize)
+import Proteome.Data.Env (Env)
 import Proteome.Add (proAdd)
 import Proteome.Config (proReadConfig)
 import Proteome.Tags (proTags)
 import Proteome.Save (proSave)
 
-plugin' :: TVar Env -> Plugin (Ribosome (TVar Env))
+plugin' :: Ribosome (TVar Env) -> Plugin (Ribosome (TVar Env))
 plugin' env =
   Plugin {
-    environment = Ribosome "proteome" env,
+    environment = env,
     exports = [
       $(function' 'proteomePoll) Sync,
       $(function' 'proteomeStage1) Async,
@@ -34,4 +34,5 @@
 plugin :: Neovim (StartupConfig NeovimConfig) NeovimPlugin
 plugin = do
   env <- initialize
-  wrapPlugin $ plugin' env
+  env' <- newRibosome "proteome" env
+  wrapPlugin $ plugin' env'
diff --git a/lib/Proteome/Tags.hs b/lib/Proteome/Tags.hs
--- a/lib/Proteome/Tags.hs
+++ b/lib/Proteome/Tags.hs
@@ -71,6 +71,8 @@
 tagsProcess (ProjectRoot root) cmd args =
   readCreateProcessWithExitCode (Proc.proc cmd (words args)) { Proc.cwd = Just root } ""
 
+-- TODO write to temp file, move to actual file after
+-- TODO lock process in state to avoid multiple processes trying to access the file
 executeTags :: ProjectRoot -> String -> String -> Proteome ()
 executeTags root@(ProjectRoot rootS) cmd args = do
   deleteTags root
diff --git a/lib/Ribosome/Data/Ribo.hs b/lib/Ribosome/Data/Ribo.hs
--- a/lib/Ribosome/Data/Ribo.hs
+++ b/lib/Ribosome/Data/Ribo.hs
@@ -4,18 +4,23 @@
   inspect,
   modify,
   name,
+  lockOrSkip,
 ) where
 
 import Control.Concurrent.STM.TVar (modifyTVar)
+import qualified Control.Lens as Lens (view, over, at)
+import qualified Data.Map.Strict as Map (insert, delete)
+import UnliftIO (finally)
 import UnliftIO.STM (TVar, atomically, readTVarIO)
 import Neovim (Neovim, ask)
-import Ribosome.Data.Ribosome (Ribosome(Ribosome))
+import Ribosome.Data.Ribosome (Ribosome(Ribosome), Locks)
+import qualified Ribosome.Data.Ribosome as Ribosome (_locks, locks)
 
 type Ribo e = Neovim (Ribosome e)
 
 state :: Ribo (TVar e) e
 state = do
-  Ribosome _ t <- ask
+  Ribosome _ _ t <- ask
   readTVarIO t
 
 inspect :: (e -> a) -> Ribo (TVar e) a
@@ -23,10 +28,42 @@
 
 modify :: (e -> e) -> Ribo (TVar e) ()
 modify f = do
-  Ribosome _ t <- ask
+  Ribosome _ _ t <- ask
   atomically $ modifyTVar t f
 
 name :: Ribo e String
 name = do
-  Ribosome n _ <- ask
+  Ribosome n _ _ <- ask
   return n
+
+getLocks :: Ribo e Locks
+getLocks = do
+  Ribosome _ intTv _ <- ask
+  int <- readTVarIO intTv
+  return $ Ribosome.locks int
+
+inspectLocks :: (Locks -> a) -> Ribo e a
+inspectLocks f = fmap f getLocks
+
+modifyLocks :: (Locks -> Locks) -> Ribo e ()
+modifyLocks f = do
+  Ribosome _ intTv _ <- ask
+  atomically $ modifyTVar intTv $ Lens.over Ribosome._locks f
+
+unlock :: String -> Ribo e ()
+unlock key =
+  modifyLocks $ Map.delete key
+
+lock :: String -> Ribo e Bool
+lock key = do
+  currentLock <- inspectLocks $ Lens.view $ Lens.at key
+  case currentLock of
+    Just _ -> return True
+    Nothing -> do
+      modifyLocks $ Map.insert key ()
+      return False
+
+lockOrSkip :: String -> Ribo e () -> Ribo e ()
+lockOrSkip key thunk = do
+  running <- lock key
+  if running then return () else finally thunk $ unlock key
diff --git a/lib/Ribosome/Data/Ribosome.hs b/lib/Ribosome/Data/Ribosome.hs
--- a/lib/Ribosome/Data/Ribosome.hs
+++ b/lib/Ribosome/Data/Ribosome.hs
@@ -1,18 +1,45 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE FlexibleInstances #-}
+
 module Ribosome.Data.Ribosome(
   Ribosome (..),
   newRibosome,
+  RibosomeInternal (..),
+  _internal,
+  _locks,
+  Locks,
+  newInternalTVar,
 ) where
 
+import Control.Lens (makeClassy_)
 import UnliftIO.STM (TVar, newTVarIO)
 import Control.Monad.IO.Class (MonadIO)
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as Map (empty)
 
+type Locks = Map String ()
+
+newtype RibosomeInternal =
+  RibosomeInternal {
+    locks :: Locks
+  }
+makeClassy_ ''RibosomeInternal
+
 data Ribosome e =
   Ribosome {
     name :: String,
+    internal :: TVar RibosomeInternal,
     env :: e
   }
+makeClassy_ ''Ribosome
 
+newInternalTVar :: MonadIO m => m (TVar RibosomeInternal)
+newInternalTVar = newTVarIO (RibosomeInternal Map.empty)
+
 newRibosome :: MonadIO m => String -> e -> m (Ribosome (TVar e))
 newRibosome name' env' = do
-  tv <- newTVarIO env'
-  return $ Ribosome name' tv
+  envTv <- newTVarIO env'
+  intTv <- newInternalTVar
+  return $ Ribosome name' intTv envTv
diff --git a/lib/Ribosome/Test/Embed.hs b/lib/Ribosome/Test/Embed.hs
--- a/lib/Ribosome/Test/Embed.hs
+++ b/lib/Ribosome/Test/Embed.hs
@@ -12,7 +12,7 @@
 import Neovim (Neovim, Object, vim_set_var')
 import Neovim.Test (testWithEmbeddedNeovim, Seconds(..))
 import Ribosome.Data.Ribo (Ribo)
-import Ribosome.Data.Ribosome (Ribosome(Ribosome))
+import Ribosome.Data.Ribosome (Ribosome(Ribosome), newInternalTVar)
 import Ribosome.Api.Option (rtpCat)
 
 type Runner env = TestConfig -> Neovim env () -> Neovim env ()
@@ -43,5 +43,6 @@
   setVars vars
 
 unsafeEmbeddedSpec :: Runner (Ribosome e) -> TestConfig -> e -> Ribo e () -> IO ()
-unsafeEmbeddedSpec runner conf env spec =
-  testWithEmbeddedNeovim Nothing (Seconds 5) (Ribosome (pluginName conf) env) $ runner conf spec
+unsafeEmbeddedSpec runner conf env spec = do
+  internal <- newInternalTVar
+  testWithEmbeddedNeovim Nothing (Seconds 5) (Ribosome (pluginName conf) internal env) $ runner conf spec
diff --git a/proteome.cabal b/proteome.cabal
--- a/proteome.cabal
+++ b/proteome.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: eaeaa5e19fbd3b145b1a7a2b04d61f2f2b8d7f0e7cb6e0653b18a41a8b2105c1
+-- hash: 3835aa8ef5ed7fc0653a8b4d5cf6e0200e154f66d9076b365a05f2318ad73f73
 
 name:           proteome
-version:        0.3.6.0
+version:        0.3.7.0
 synopsis:       neovim project manager
 description:    Please see the README on GitHub at <https://github.com/tek/proteome-hs>
 category:       Neovim
