diff --git a/Changelog.md b/Changelog.md
new file mode 100644
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,10 @@
+# Change log for postgresql-migration-persistent project
+
+## Version 1.0.0
+import the code
+
+
+## Version 0.0.0 
+
+import [template](https://github.com/jappeace/template).
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Jappie Klooster
+
+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,83 @@
+
+> Don't you *ever* trust computers.
+> 
+> \- first rule of programist bible.
+
+
+This library combines [`postgresql-migration`](https://hackage.haskell.org/package/postgresql-migration) and [`persistent`](https://hackage.haskell.org/package/persistent),
+for the common use case of:
+1. Run my manually defined migrations.
+2. Check if the schema defined in persistent aligns with the database.
+3. If not, rollback and error with the migration plan persistent wants to do.
+
+## Usage
+
+It could for example look something like this with katip logging:
+```haskell
+import PostgreSQL.Migration.Persistent
+import Database.Schema.User()
+import Database.Schema.Company()
+
+migrateAll :: Migration
+migrateAll = migrateModels $(discoverEntities)
+
+main :: IO ()
+main = do
+  katipConfig <- mkKatipConfig "server" environment
+  let migDir = "migrations/up"
+  let migrationOptions = defaultOptions migDir $ \case
+        Left errmsg -> runContext katipConfig $ $logTM AlertS $ logStr errmsg
+        Right infoMsg -> runContext katipConfig $ $logTM InfoS $ logStr infoMsg
+
+  result <- runMigrations migrationOptions migrateAll rawPool
+  runContext katipConfig $ case result of
+    MigrationConsistent -> $logTM InfoS "migration consistent"
+    MigrationRollbackDueTo rollback -> do
+      $logTM ErrorS $ logStr $ errorMessage rollback
+      error "invalid migrations"
+    MigrationLibraryError err' -> do
+      $logTM ErrorS $ logStr err'
+      error "migration library error"
+    MigrationNotBackedByPg ->
+      error "app expects pg backing for migrations to work"
+      
+  runMyApp
+```
+
+By default the migrations are applied in a large transaction,
+but you can modify this behavior by overriding options record,
+for example:
+
+```haskell
+import Database.PostgreSQL.Simple.Migration qualified as Migration
+
+main = do
+  ...
+  let migrationOptions = defaultOptions migDir $ \case
+        Left errmsg -> runContext katipConfig $ $logTM AlertS $ logStr errmsg
+        Right infoMsg -> runContext katipConfig $ $logTM InfoS $ logStr infoMsg
+  let overridenOptions = migrationOptions { pmoMigrationOptions = (pmoMigrationOptions migrationOptions ) {Migration.optTransactionControl = Migration.TransactionPerStep }}
+```
+
+pretty much all options are exposed from the underlying postgresql-migration
+library.
+
+### Tools
+Enter the nix shell.
+```
+nix develop
+```
+You can checkout the makefile to see what's available:
+```
+cat makefile
+```
+
+### Running
+```
+make run
+```
+
+### Fast filewatch which runs tests
+```
+make ghcid
+```
diff --git a/postgresql-migration-persistent.cabal b/postgresql-migration-persistent.cabal
new file mode 100644
--- /dev/null
+++ b/postgresql-migration-persistent.cabal
@@ -0,0 +1,79 @@
+cabal-version:      3.0
+
+name:           postgresql-migration-persistent
+version:        1.0.0
+homepage:       https://github.com/jappeace/postgresql-migration-persistent#readme
+bug-reports:    https://github.com/jappeace/postgresql-migration-persistent/issues
+author:         Jappie Klooster, Jean-Paul Calderone 
+maintainer:     hi@jappie.me
+synopsis:       A PostgreSQL persistent schema migration utility
+description:    Wraps postgresql migration and persistent to make sure the persistent schema
+                aligns with what's in the database.
+                If not, it returns a list of suggested manual migrations
+                to be put in postgresql-migration.
+copyright:      2025 Jappie Klooster
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+category:       Database
+extra-source-files:
+    Readme.md
+    LICENSE
+extra-doc-files:
+    Changelog.md
+
+source-repository head
+  type: git
+  location: https://github.com/jappeace/postgresql-migration-persistent
+
+common common-options
+  default-extensions: 
+      EmptyCase
+      FlexibleContexts
+      FlexibleInstances
+      InstanceSigs
+      MultiParamTypeClasses
+      LambdaCase
+      MultiWayIf
+      NamedFieldPuns
+      TupleSections
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveLift
+      DeriveTraversable
+      DerivingStrategies
+      GeneralizedNewtypeDeriving
+      StandaloneDeriving
+      OverloadedStrings
+      TypeApplications
+      NumericUnderscores
+      ImportQualifiedPost
+
+  ghc-options:
+    -Wall -Wincomplete-uni-patterns
+    -Wincomplete-record-updates -Widentities -Wredundant-constraints
+    -Wcpp-undef -fwarn-tabs -Wpartial-fields
+    -fdefer-diagnostics -Wunused-packages
+    -fenable-th-splice-warnings
+    -fno-omit-yields
+    -threaded 
+
+  build-depends:
+      base >=4.9.1.0 && <5
+    , postgresql-migration < 0.3
+    , persistent-postgresql < 3
+    , persistent < 3
+    , postgresql-simple < 1
+    , text < 3
+    , resource-pool < 0.5
+    , mtl < 3
+
+  default-language: Haskell2010
+
+library
+  import: common-options
+  exposed-modules:
+      PostgreSQL.Migration.Persistent
+  hs-source-dirs:
+      src
diff --git a/src/PostgreSQL/Migration/Persistent.hs b/src/PostgreSQL/Migration/Persistent.hs
new file mode 100644
--- /dev/null
+++ b/src/PostgreSQL/Migration/Persistent.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE DataKinds #-}
+
+-- | uses Postgres migration to run all found
+--   migrations in a transaction.
+--   then use persistent to check if the persistent
+--   model aligns with what's in the database.
+module PostgreSQL.Migration.Persistent
+  ( runMigrations,
+    PMMigrationResult (..),
+
+    -- * options
+    defaultOptions,
+    PersistentMigrationOptions (..),
+  )
+where
+
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.Reader (ReaderT (..), asks)
+import Data.Pool (Pool)
+import Data.Text (Text)
+import Database.Persist.Postgresql (getSimpleConn, runSqlPool)
+import Database.Persist.Sql (Migration, SqlBackend, showMigration)
+import Database.PostgreSQL.Simple (Connection, rollback)
+import Database.PostgreSQL.Simple.Migration qualified as Migration
+import Database.PostgreSQL.Simple.Util qualified as Migration
+import Prelude
+
+-- | recommended default options for production settnigs
+defaultOptions ::
+  -- | migrations folder, for exmaple "migrations/up"
+  FilePath ->
+  -- | the logging options, for example
+  --
+  -- @
+  --    (\case
+  --       Left errmsg -> runInIO $ $logTM AlertS $ logStr errmsg
+  --       Right infoMsg -> runInIO $ $logTM InfoS $ logStr infoMsg)
+  -- @
+  (Either Text Text -> IO ()) ->
+  PersistentMigrationOptions
+defaultOptions filepath logMsgs =
+  PersistentMigrationOptions
+    { pmoMigrationOptions =
+        Migration.defaultOptions
+          { Migration.optVerbose = Migration.Verbose,
+            Migration.optLogWriter = logMsgs,
+            -- NB we do the transaction around the entire thing
+            Migration.optTransactionControl = Migration.NoNewTransaction
+          },
+      pmoMigrationSource = Migration.MigrationDirectory filepath,
+      pmoSchemaMigrationTableName = "schema_migrations"
+    }
+
+-- | The result of the postgresql-migration operation.
+data PMMigrationResult
+  = MigrationConsistent
+  | -- | rollback due to persistent inconsistencies
+    MigrationRollbackDueTo [Text]
+  | -- | some error from the postgres migration libraries
+    MigrationLibraryError String
+  | -- | caused by 'getSimpleConn' returning 'Nothing'
+    MigrationNotBackedByPg
+  deriving stock (Show, Eq)
+
+data PersistentMigrationOptions = PersistentMigrationOptions
+  { pmoMigrationOptions :: Migration.MigrationOptions,
+    pmoMigrationSource :: Migration.MigrationCommand,
+    pmoSchemaMigrationTableName :: String
+  }
+
+-- | Run the given migrations in a single transaction.  If the migration fails
+-- somehow the transaction is rolled back.
+runMigrations ::
+  -- | eg 'defaultOptions'
+  PersistentMigrationOptions ->
+  -- | the Automatic migration. eg 'migrateAll', or migrateModels $(discoverEntities). note this is
+  Migration ->
+  -- | sql pool, created with for example 'withPostgresqlPool'.
+  Pool SqlBackend ->
+  IO PMMigrationResult
+runMigrations config migrateAll pool =
+  flip runSqlPool pool $ do
+    asks getSimpleConn >>= \case
+      Nothing -> pure $ MigrationNotBackedByPg
+      Just conn -> do
+        result <- runMigrationCommands config conn
+        case result of
+          Migration.MigrationError err ->
+            pure $ MigrationLibraryError err
+          Migration.MigrationSuccess ->
+            assertPersistentConsistency migrateAll >>= \case
+              MigrationConsistent -> pure MigrationConsistent
+              failure -> liftIO $ failure <$ rollback conn
+
+runMigrationCommands :: PersistentMigrationOptions -> Connection -> ReaderT SqlBackend IO (Migration.MigrationResult String)
+runMigrationCommands options conn = do
+  initialized <- liftIO $ Migration.existsTable conn $ pmoSchemaMigrationTableName options
+  let migrations =
+        if initialized
+          then [pmoMigrationSource options]
+          else [Migration.MigrationInitialization, pmoMigrationSource options]
+  liftIO $ Migration.runMigrations conn (pmoMigrationOptions options) migrations
+
+assertPersistentConsistency :: Migration -> ReaderT SqlBackend IO PMMigrationResult
+assertPersistentConsistency migrateAll = do
+  persistentAutoMig <- showMigration migrateAll
+  if null persistentAutoMig
+    then pure MigrationConsistent
+    else do
+      pure $ MigrationRollbackDueTo persistentAutoMig
