diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for filesystem-abstractions
+
+## 0 -- 2018-12-28
+
+* First version.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2018, davean
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of davean nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/filesystem-abstractions.cabal b/filesystem-abstractions.cabal
new file mode 100644
--- /dev/null
+++ b/filesystem-abstractions.cabal
@@ -0,0 +1,34 @@
+cabal-version:       2.2
+
+name:                filesystem-abstractions
+version:             0
+synopsis:            A shared set of abstractions and types for representing filessytem data.
+description:
+  A shared set of abstractions and types for representing filessytem data, and functions to work with them.
+  Focused on a correct representation of important aspects of specific views of filesystems and type safety.
+homepage:            https://oss.xkcd.com/
+bug-reports:         https://code.xkrd.net/haskell/filesystem-abstractions/issues
+license:             BSD-3-Clause
+license-file:        LICENSE
+author:              davean
+maintainer:          oss@xkcd.com
+copyright:           Copyright (C) 2018 davean
+category:            System
+extra-source-files:  CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://code.xkrd.net/haskell/filesystem-abstractions.git
+
+library
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  exposed-modules:
+    System.Filesystem.FileTree
+    System.Filesystem.PathComponent
+  build-depends:
+      base >= 4.9.0.0 && < 4.13
+    , bytestring ^>= 0.10
+    , list-tries ^>= 0.6
+    , posix-paths ^>= 0.2
+    , semigroups ^>= 0.18
diff --git a/src/System/Filesystem/FileTree.hs b/src/System/Filesystem/FileTree.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Filesystem/FileTree.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections     #-}
+
+-- | Description: A convenient way to work with the files in a filesystem structure.
+
+module System.Filesystem.FileTree (FileTree) where
+
+import           Data.ListTrie.Map.Ord (TrieMap)
+import           System.Filesystem.PathComponent
+
+-- | A representation of the files in a filesystem, ignoring directories.
+type FileTree a = TrieMap PathComponent a
+
+-- for debugging purposes only
+{-
+showTreeWith :: Show a => (FileData -> a) -> FileTree FileData -> String
+showTreeWith f t = LT.showTrie (fmap f t) ""
+
+showTree :: FileTree FileData -> String
+showTree = showTreeWith go
+    where
+      go (fd, exec) =
+        Prelude.concat ["(", show . BSL.length $ fd, " bytes", if exec then ", *" else "", ")"]
+-}
diff --git a/src/System/Filesystem/PathComponent.hs b/src/System/Filesystem/PathComponent.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Filesystem/PathComponent.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | Description: This module provides a type safe way to work with specific, well formed, components of a POSIX path. 
+
+module System.Filesystem.PathComponent
+ ( PathComponent
+ , pathComponent
+ , isPathComponent
+ , getPC
+ , slashify
+ , splitPathComponents
+ ) where
+
+import           Control.Monad.Fail
+import qualified Data.ByteString       as B
+import           Data.Maybe (fromMaybe)
+import           Data.Semigroup
+import           Data.String
+import           Prelude hiding (fail)
+import           System.Posix.FilePath
+import           Text.Printf
+
+-- | The restricted subset of 'B.ByteString's that are valid names in a POSIX path.
+newtype PathComponent = PC {
+    getPC :: B.ByteString -- ^ Get the 'B.ByteString' out of a 'PathComponent'
+  } deriving (Eq, Ord, Show, Semigroup)
+
+instance IsString PathComponent where
+  fromString = fromMaybe (error "not a PathComponent") . pathComponent . fromString
+
+-- | Safe constructor for 'PathComponent'
+pathComponent :: MonadFail m => B.ByteString -> m PathComponent
+pathComponent b | isPathComponent b = pure (PC b)
+                | otherwise         = fail (printf "Not a valud PathComponent (%s)" (show b))
+
+-- | True when 'pathComponent' will result in a Just
+isPathComponent :: B.ByteString -> Bool
+isPathComponent "" = False
+isPathComponent b  = B.all (`B.notElem` "\x00/") b
+
+-- | Add a trailing @/@ unconditionally.
+slashify :: PathComponent -> B.ByteString
+slashify (PC p) = B.snoc p 0x2f
+
+-- | A version of 'splitDirectories' for 'PathComponent's.
+splitPathComponents :: MonadFail m => RawFilePath -> m [PathComponent]
+splitPathComponents =
+  -- We can only get a Nothing when the future path component:
+  --   1) contains a slash, which it can't due to coming from spliDirectories
+  --   2) contains a Null byte, which is illegal in a path
+  --      and thus the path is not a valid RawFilePath
+  maybe (fail "path contained a null byte") pure . mapM pathComponent . splitDirectories
