diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+## Version 0.1.0
+
+* Initial Release
diff --git a/Example.hs b/Example.hs
new file mode 100644
--- /dev/null
+++ b/Example.hs
@@ -0,0 +1,32 @@
+
+{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
+
+import RzPipe
+import GHC.Generics
+import Data.Aeson as JSON
+import Data.Word
+
+data Flag = Flag
+    { name :: String
+    , size :: Word64
+    , offset :: Word64 }
+    deriving (Show, Generic, JSON.FromJSON)
+
+showMainFunction :: RzContext -> IO ()
+showMainFunction ctx = do
+    cmd ctx "s main"
+    putStrLn =<< cmd ctx "pD `fl $$`"
+
+showFlags :: RzContext -> IO ()
+showFlags ctx = print =<< (cmdj ctx "fj" :: IO (Maybe [Flag]))
+
+main :: IO ()
+main = do
+    -- Spawn a new rizin instance and open the /bin/ls binary in it
+    ctx <- open $ Just "/bin/ls"
+    -- Pick up pipes from parent rizin process
+    -- ctx <- open Nothing
+    -- Connect to rizin via HTTP (e.g. if "rizin -qc=h /bin/ls" is running)
+    -- ctx <- open $ Just "http://127.0.0.1:9090"
+    showMainFunction ctx
+    showFlags ctx
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2020 rizinorg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,61 @@
+# rz-pipe for Haskell
+
+rz-pipe is a scripting interface for the Rizin Reverse Engineering
+Framework that builds upon Rizin's command interface as a simple
+point of interaction.
+
+It can be used by launching a Rizin instance from Haskell or
+connecting to an existing one using pipes or HTTP.
+
+## Usage
+
+The API revolves around the `RzContext` type, which represents
+a connection to a running Rizin instance. The `open` function
+will establish such a connection in `IO` based on its argument:
+
+```haskell
+do
+  -- Spawn a new rizin instance and open the /bin/ls binary in it
+  localCtx <- open $ Just "/bin/ls"
+  -- Pick up pipes from parent rizin process
+  parentCtx <- open Nothing
+  -- Connect to rizin via HTTP (e.g. if "rizin -qc=h /bin/ls" is running)
+  httpCtx <- open $ Just "http://127.0.0.1:9090"
+```
+
+Once a context has been opened, the `cmd` function can be used
+to run commands and return the result as a string in `IO`:
+
+```haskell
+putStrLn =<< cmd ctx "pd 3"
+-- prints e.g.
+--   0x00005b20      endbr64
+--   0x00005b24      xor   ebp, ebp
+--   0x00005b26      mov   r9, rdx
+```
+
+For scripting, it is highly recommended to use rizin commands returning
+json, commonly prefixed with `j`. To conveniently parse the results with
+[aeson](https://hackage.haskell.org/package/aeson), the `cmdj` can be
+used, which is polymorphic in its return type and can produce any object
+in the `Data.Aeson.FromJSON` type class:
+
+```haskell
+{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
+
+import RzPipe
+import GHC.Generics
+import Data.Aeson as JSON
+import Data.Word
+
+data Flag = Flag
+    { name :: String
+    , size :: Word64
+    , offset :: Word64 }
+    deriving (Show, Generic, JSON.FromJSON)
+
+showFlags :: RzContext -> IO ()
+showFlags ctx = print =<< (cmdj ctx "fj" :: IO (Maybe [Flag]))
+-- prints e.g.
+-- Just [Flag {name = "section.", size = 0, offset = 0},Flag {name = "section..comment", size = 76, offset = 0},Flag {name = "section..shstrtab", size = 247, offset = 0},Flag {name = "segment.LOAD0", size = 13584, offset = 0},Flag {name = "segment.GNU_STACK", size = 0, offset = 0},Flag {name = "segment.ehdr", size = 64, offset = 0} ...
+```
diff --git a/RzPipe/RzPipe.hs b/RzPipe/RzPipe.hs
new file mode 100644
--- /dev/null
+++ b/RzPipe/RzPipe.hs
@@ -0,0 +1,56 @@
+module RzPipe (RzContext(), open, cmd, cmdj) where
+import Data.Char
+import Data.Word
+import Network.HTTP
+import System.IO
+import System.Process
+import System.Environment (getEnv)
+import GHC.IO.Handle.FD
+import System.Posix.Internals (FD)
+import qualified Data.Aeson as JSON
+import qualified Data.ByteString.Lazy as B
+import qualified Data.ByteString.Lazy.UTF8 as U
+
+withPipes p = p { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }
+
+createProcess' args = fmap f $ createProcess (withPipes args) where
+    f (Just i, Just o, Just e, h) = (i, o, e, h)
+    f _ = error "createProcess': Failed to open pipes to the subprocess."
+
+lHTakeWhile :: (Word8 -> Bool) -> Handle -> IO B.ByteString
+lHTakeWhile p h = do
+    c <- fmap B.head $ B.hGet h 1
+    if p c
+        then fmap (c `B.cons`) $ lHTakeWhile p h
+        else return B.empty
+
+data RzContext = HttpCtx String
+               | PipeCtx Handle Handle
+
+open :: Maybe String -> IO RzContext
+open (Just url@('h':'t':'t':'p':_)) = return $ HttpCtx (url ++ "/cmd/")
+open (Just filename) = do
+    (hIn, hOut, _, _) <- createProcess' $ proc "rizin" ["-q0", filename]
+    lHTakeWhile (/= 0) hOut -- drop the inital null that rizin emits
+    return $ PipeCtx hIn hOut
+open Nothing = do
+    hIn <- fdToHandle =<< (read::(String -> FD)) <$> getEnv "RZ_PIPE_OUT"
+    hOut <- fdToHandle =<< (read::(String -> FD)) <$> getEnv "RZ_PIPE_IN"
+    return $ PipeCtx hIn hOut
+
+cmdHttp :: String -> String -> IO String
+cmdHttp url cmd = getResponseBody =<< simpleHTTP (getRequest (url ++ urlEncode cmd))
+
+cmdPipe :: Handle -> Handle -> String -> IO B.ByteString
+cmdPipe hIn hOut cmd = hPutStrLn hIn cmd >> hFlush hIn >> lHTakeWhile (/= 0) hOut
+
+cmdB :: RzContext -> String -> IO B.ByteString
+cmdB (HttpCtx url) cmd = U.fromString <$> cmdHttp url cmd
+cmdB (PipeCtx hIn hOut) cmd = cmdPipe hIn hOut cmd
+
+cmd :: RzContext -> String -> IO String
+cmd (HttpCtx url) cmd = cmdHttp url cmd
+cmd (PipeCtx hIn hOut) cmd = U.toString <$> cmdPipe hIn hOut cmd
+
+cmdj :: JSON.FromJSON a => RzContext -> String -> IO (Maybe a)
+cmdj = (fmap JSON.decode .) . cmdB
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/rz-pipe.cabal b/rz-pipe.cabal
new file mode 100644
--- /dev/null
+++ b/rz-pipe.cabal
@@ -0,0 +1,39 @@
+cabal-version:       >=1.10
+
+name:                rz-pipe
+version:             0.1.0
+synopsis:            Pipe interface for Rizin
+category:            Reverse Engineering, Security
+license:             MIT
+license-file:        LICENSE
+author:              rizinorg
+maintainer:          Florian Märkl <rizin@metallic.software>
+build-type:          Simple
+description:
+    rz-pipe is a scripting interface for the Rizin Reverse Engineering
+    Framework that builds upon Rizin's command interface as a simple
+    point of interaction.
+    .
+    It can be used by launching a Rizin instance from Haskell or
+    connecting to an existing one using pipes or HTTP.
+
+extra-source-files:
+    README.md CHANGELOG.md
+
+library
+  exposed-modules:     RzPipe
+  hs-source-dirs:      RzPipe
+  build-depends:       base >=4.12 && <5,
+                       HTTP        >= 4000.3.15 && < 4000.4,
+                       bytestring  >= 0.10.12 && < 0.11,
+                       utf8-string >= 1.0.2 && < 1.1,
+                       aeson       >= 1.5.5 && < 1.6,
+                       process     >= 1.6.9 && < 1.7
+  default-language:    Haskell2010
+
+executable example
+  main-is:             Example.hs
+  build-depends:       base >=4.12 && <5,
+                       rz-pipe,
+                       aeson
+  default-language:    Haskell2010
