diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+All notable changes to this project since `v1.0.0` will be documented in this
+file.
+
+This project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).
+
+## 1.0.0 (2023-08-22)
+
+### Added
+
+- First package release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, Alexander Pankoff
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder nor the names of its
+   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 HOLDER 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# starter-snake-haskell - A Haskell Battlesnake starter
+
+This repository contains a Haskell [Battlesnake](https://play.battlesnake.com) starter project.
+It implements `v1` of the Battlesnake API and can be deployed to Heroku.
+
+# Usage
+
+This starter repo can be used in two different ways
+
+1. Installed as a hackage library in your haskell project
+1. As a fork that is subsequently modified to your needs
+
+# Deployment
+
+To deploy on Heroku you can use the provided `Procfile` when using the template
+as a fork. Otherwise you will need to define your own `Procfile`.
+Addtionally the
+[heroku-buildpack-stack](https://github.com/mfine/heroku-buildpack-stack)
+buildpack is required for the application to run on heroku
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/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,45 @@
+module Main where
+
+import Battlesnake.API.GameRequest
+import qualified Battlesnake.API.InfoResponse as Info
+import Battlesnake.API.MoveResponse
+import Battlesnake.Core.Direction
+import Battlesnake.Server
+
+main :: IO ()
+main =
+  runBattlesnakeServer
+    snakeInfo
+    startHandler
+    moveHandler
+    endHandler
+
+snakeInfo :: Info.InfoResponse
+snakeInfo =
+  Info.InfoResponse
+    { Info.apiversion = "1",
+      -- Optional Customization:
+      Info.author = Nothing, -- TODO: Your Battlsnake username here
+      Info.color = Nothing, -- TODO: pesonalize
+      Info.head = Nothing, -- TODO: pesonalize
+      Info.tail = Nothing, -- TODO: pesonalize
+      Info.version = Nothing -- TODO: Version number or tag for your battlesnake
+    }
+
+-- TODO:
+-- implement start handler if you need to allocate ressources or data at the
+-- beginning of a game
+startHandler :: GameRequest -> IO ()
+startHandler = const $ return ()
+
+-- TODO:
+-- implement your game logic here.
+moveHandler :: GameRequest -> IO MoveResponse
+moveHandler g = do
+  print g
+  return $ MoveResponse UP (Just "always going up!")
+
+-- TODO:
+-- implement end handler if you want to clean up after a game has ended
+endHandler :: GameRequest -> IO ()
+endHandler = const $ return ()
diff --git a/src/Battlesnake/API/GameRequest.hs b/src/Battlesnake/API/GameRequest.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/API/GameRequest.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.API.GameRequest where
+
+import Battlesnake.Core.Battlesnake (Battlesnake)
+import Battlesnake.Core.Board (Board)
+import Battlesnake.Core.Game (Game)
+import Data.Aeson (FromJSON)
+import GHC.Generics
+
+{-|
+  The request sent to your battlesnake server on each turn.
+  This contains all the information you need to determine your snakes next move.}
+-}
+data GameRequest = GameRequest
+  { game :: Game, -- ^ Information about the current game. (See "Battlesnake.Core.Game")
+    turn :: Integer, -- ^ The turn beeing played.
+    board :: Board, -- ^ The game board. (See "Battlesnake.Core.Board")
+    you :: Battlesnake -- ^ Your snake. (See "Battlesnake.Core.Battlesnake"")
+  }
+  deriving (Show, Generic)
+
+instance FromJSON GameRequest
diff --git a/src/Battlesnake/API/InfoResponse.hs b/src/Battlesnake/API/InfoResponse.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/API/InfoResponse.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.API.InfoResponse where
+
+import Data.Aeson (ToJSON)
+import Data.Text
+import GHC.Generics
+
+{-|
+  The response to an info request.
+  This can be used to modify the appearance of your snake on the game board.
+-}
+data InfoResponse = InfoResponse
+  { apiversion :: Text, -- ^ The API version supported by the battlesnake server (currently 1).
+    author :: Maybe Text, -- ^ The username of the author of the snake.
+    color :: Maybe Text, -- ^ A color code used to set the color of the snake.
+    head :: Maybe Text, -- ^ A custom head to use for the snake. (See https://play.battlesnake.com/customizations for options.)
+    tail :: Maybe Text, -- ^ A custom tail to use for the snake. (See https://play.battlesnake.com/customizations for options.)
+    version :: Maybe Text -- ^ The version of the snake.
+  }
+  deriving (Show, Generic)
+
+instance ToJSON InfoResponse
diff --git a/src/Battlesnake/API/MoveResponse.hs b/src/Battlesnake/API/MoveResponse.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/API/MoveResponse.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.API.MoveResponse where
+
+import Battlesnake.Core.Direction
+import Data.Aeson (ToJSON)
+import Data.Text
+import GHC.Generics
+
+-- | The response to a move request.
+data MoveResponse = MoveResponse
+  { move :: Direction, -- ^ The direction for the next move.
+    shout :: Maybe Text -- ^ An optional shout to be displayed with the move.
+  }
+  deriving (Show, Generic)
+
+instance ToJSON MoveResponse
diff --git a/src/Battlesnake/Core/Battlesnake.hs b/src/Battlesnake/Core/Battlesnake.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Battlesnake.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.Core.Battlesnake where
+
+import Battlesnake.Core.Coordinate (Coordinate)
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text
+import GHC.Generics
+
+
+{-|
+  A snake in the game.
+  See: https://docs.battlesnake.com/api/objects/battlesnake
+-}
+data Battlesnake = Battlesnake
+  { id :: Text, -- ^ The ID of the snake.
+    name :: Text, -- ^ The name of the snake.
+    health :: Integer, -- ^ Current health of the snake (0-100).
+    body :: [Coordinate], -- ^ A list of coordinates representing the snake's body.
+    latency :: Text, -- ^ The snake's latency.
+    head :: Coordinate, -- ^ The position of the snakes head
+    length :: Integer, -- ^ The length of the snake.
+    shout :: Text, -- ^ The snake's last moves shout.
+    squad :: Maybe Text -- ^ The squad the snake is a part of (In squad mode).
+  }
+  deriving (Show, Generic)
+
+instance ToJSON Battlesnake
+
+instance FromJSON Battlesnake
diff --git a/src/Battlesnake/Core/Board.hs b/src/Battlesnake/Core/Board.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Board.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.Core.Board where
+
+import Battlesnake.Core.Battlesnake (Battlesnake)
+import Battlesnake.Core.Coordinate (Coordinate)
+import Data.Aeson (FromJSON, ToJSON)
+import GHC.Generics
+
+{-|
+  The game board.
+  See: https://docs.battlesnake.com/api/objects/board
+-}
+data Board = Board
+  { height :: Integer, -- ^ The height of the board
+    width :: Integer, -- ^ The width of the board
+    food :: [Coordinate], -- ^ A list of coordinates representing food locations
+    hazards :: [Coordinate], -- ^ A list of coordinates representing hazard locations
+    snakes :: [Battlesnake] -- ^ A list of all snakes on the board
+  }
+  deriving (Show, Generic)
+
+instance ToJSON Board
+
+instance FromJSON Board
diff --git a/src/Battlesnake/Core/Coordinate.hs b/src/Battlesnake/Core/Coordinate.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Coordinate.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.Core.Coordinate where
+
+import Data.Aeson
+  ( FromJSON (parseJSON),
+    KeyValue ((.=)),
+    ToJSON (toEncoding),
+    pairs,
+    withObject,
+    (.:),
+  )
+import GHC.Generics
+
+-- | Represents a coordinate on the game board.
+data Coordinate = Coordinate
+  { coordX :: Integer, -- ^ The X coordinate (0 indexed, from left to right)
+    coordY :: Integer -- ^ The Y coordinate (0 indexed, from bottom to top)
+  }
+  deriving (Show, Eq, Generic)
+
+instance ToJSON Coordinate where
+  toEncoding (Coordinate x y) = pairs ("x" .= x <> "y" .= y)
+
+instance FromJSON Coordinate where
+  parseJSON = withObject "Coordinate" $ \v -> Coordinate <$> v .: "x" <*> v .: "y"
diff --git a/src/Battlesnake/Core/Direction.hs b/src/Battlesnake/Core/Direction.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Direction.hs
@@ -0,0 +1,13 @@
+module Battlesnake.Core.Direction where
+
+import Data.Aeson (ToJSON, Value (String), toJSON)
+import Data.Text
+
+{- |
+  The possible directions your snake can move in.
+  See: https://docs.battlesnake.com/api/requests/move
+ -}
+data Direction = UP | DOWN | LEFT | RIGHT deriving (Ord, Eq, Show)
+
+instance ToJSON Direction where
+  toJSON = String . toLower . pack . show
diff --git a/src/Battlesnake/Core/Game.hs b/src/Battlesnake/Core/Game.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Game.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.Core.Game where
+
+import Battlesnake.Core.Ruleset
+import Data.Aeson (FromJSON)
+import Data.Text
+import GHC.Generics
+
+{-|
+  Information about the current game configuration.
+  See: https://docs.battlesnake.com/api/objects/game
+-}
+data Game = Game
+  { id :: Text, -- ^ The ID of the game.
+    ruleset :: Ruleset, -- ^ The ruleset used in the game.
+    timeout :: Integer -- ^ The maximum time in milliseconds the server will wait for a response from your server.
+  }
+  deriving (Show, Eq, Generic)
+
+instance FromJSON Game
diff --git a/src/Battlesnake/Core/Ruleset.hs b/src/Battlesnake/Core/Ruleset.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Core/Ruleset.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Battlesnake.Core.Ruleset where
+
+import Data.Aeson (FromJSON)
+import Data.Text
+import GHC.Generics
+
+{-|
+  Information about the ruleset used in the game.
+  See: https://docs.battlesnake.com/api/objects/game
+-}
+data Ruleset = Ruleset
+  { name :: Text, -- ^ The name of the ruleset.
+    version :: Text -- ^ The version of the ruleset.
+  }
+  deriving (Show, Eq, Generic)
+
+instance FromJSON Ruleset
diff --git a/src/Battlesnake/Server.hs b/src/Battlesnake/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/Battlesnake/Server.hs
@@ -0,0 +1,72 @@
+module Battlesnake.Server (GameRequestHandler, runBattlesnakeServer) where
+
+import Battlesnake.API.GameRequest
+import qualified Battlesnake.API.InfoResponse as Info
+import Battlesnake.API.MoveResponse
+import Control.Monad.IO.Class (liftIO)
+import Data.Functor ((<&>))
+import Data.Maybe (fromMaybe)
+import System.Environment (lookupEnv)
+import System.IO (BufferMode(LineBuffering), hSetBuffering, stdout)
+import Text.Read (readMaybe)
+import Web.Scotty
+
+
+-- | A handler for a battlesnake server request.
+type GameRequestHandler a = GameRequest -> IO a
+
+{- |
+  Run a battlesnake server. Runs the server on the port specified by the environment variable "PORT" or port 3000 if the variable is not set.
+  * A "Battlesnake.API.InfoResponse" to be returned when the server receives an info request.
+  * A 'GameRequestHandler' to be called when the server receives a start request.
+  * A 'GameRequestHandler' to be called when the server receives a move request.
+  * A 'GameRequestHandler' to be called when the server receives an end request.
+-}
+runBattlesnakeServer ::
+  Info.InfoResponse ->
+  GameRequestHandler () ->
+  GameRequestHandler MoveResponse ->
+  GameRequestHandler () ->
+  IO ()
+runBattlesnakeServer info startHandler moveHandler endHandler = do
+  hSetBuffering stdout LineBuffering
+  envPort <- lookupEnv "PORT" <&> (readMaybe =<<)
+  scotty (fromMaybe 3000 envPort) $
+    routes
+      info
+      startHandler
+      moveHandler
+      endHandler
+
+routes ::
+  Info.InfoResponse ->
+  GameRequestHandler () ->
+  GameRequestHandler MoveResponse ->
+  GameRequestHandler () ->
+  ScottyM ()
+routes info startHandler moveHandler endHandler = do
+  get "/" $ handleInfoRequest info
+  post "/start" $ handleStartRequest startHandler
+  post "/move" $ handleMoveRequest moveHandler
+  post "/end" $ handleEndRequest endHandler
+
+handleInfoRequest :: Info.InfoResponse -> ActionM ()
+handleInfoRequest = json
+
+handleStartRequest :: GameRequestHandler () -> ActionM ()
+handleStartRequest handler = do
+  gameRequest <- jsonData
+  liftIO $ handler gameRequest
+  return ()
+
+handleMoveRequest :: GameRequestHandler MoveResponse -> ActionM ()
+handleMoveRequest handler = do
+  gameRequest <- jsonData
+  nextMove <- liftIO $ handler gameRequest
+  json nextMove
+
+handleEndRequest :: GameRequestHandler () -> ActionM ()
+handleEndRequest handler = do
+  gameRequest <- jsonData
+  liftIO $ handler gameRequest
+  return ()
diff --git a/starter-snake-haskell.cabal b/starter-snake-haskell.cabal
new file mode 100644
--- /dev/null
+++ b/starter-snake-haskell.cabal
@@ -0,0 +1,89 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.35.1.
+--
+-- see: https://github.com/sol/hpack
+
+name:           starter-snake-haskell
+version:        1.0.0
+synopsis:       A Haskell Battlesnake starter
+description:    See README at <https://github.com/ccntrq/starter-snake-haskell>
+category:       Game, Web
+homepage:       https://github.com/ccntrq/starter-snake-haskell#readme
+bug-reports:    https://github.com/ccntrq/starter-snake-haskell/issues
+author:         Alexander Pankoff <ccntrq@screenri.de
+maintainer:     Alexander Pankoff <ccntrq@screenri.de>
+copyright:      2021 Alexander Pankoff
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+    LICENSE
+
+source-repository head
+  type: git
+  location: https://github.com/ccntrq/starter-snake-haskell
+
+library
+  exposed-modules:
+      Battlesnake.API.GameRequest
+      Battlesnake.API.InfoResponse
+      Battlesnake.API.MoveResponse
+      Battlesnake.Core.Battlesnake
+      Battlesnake.Core.Board
+      Battlesnake.Core.Coordinate
+      Battlesnake.Core.Direction
+      Battlesnake.Core.Game
+      Battlesnake.Core.Ruleset
+      Battlesnake.Server
+  other-modules:
+      Paths_starter_snake_haskell
+  hs-source-dirs:
+      src
+  default-extensions:
+      OverloadedStrings
+  ghc-options: -Wall
+  build-depends:
+      aeson >=1.4 && <3
+    , base >=4.13 && <5
+    , containers >=0.6 && <1
+    , scotty >=0.11 && <1
+    , text >=1.2 && <2
+  default-language: Haskell2010
+
+executable starter-snake-haskell-exe
+  main-is: Main.hs
+  other-modules:
+      Paths_starter_snake_haskell
+  hs-source-dirs:
+      app
+  default-extensions:
+      OverloadedStrings
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:
+      aeson >=1.4 && <3
+    , base >=4.13 && <5
+    , containers >=0.6 && <1
+    , scotty >=0.11 && <1
+    , starter-snake-haskell
+    , text >=1.2 && <2
+  default-language: Haskell2010
+
+test-suite starter-snake-haskell
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_starter_snake_haskell
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:
+      aeson >=1.4 && <3
+    , base >=4.13 && <5
+    , containers >=0.6 && <1
+    , scotty >=0.11 && <1
+    , starter-snake-haskell
+    , text >=1.2 && <2
+  default-language: Haskell2010
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,3 @@
+main :: IO ()
+main = do
+    putStrLn "Not implemented"
