diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2025, Nikita Volkov
+
+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 @@
+# postgresql-types-algebra
+
+[![Hackage](https://img.shields.io/hackage/v/postgresql-types-algebra.svg)](https://hackage.haskell.org/package/postgresql-types-algebra)
+[![Continuous Haddock](https://img.shields.io/badge/haddock-master-blue)](https://nikita-volkov.github.io/postgresql-types-algebra/)
+
+Core type classes and algebra for PostgreSQL type mappings, extracted from ["postgresql-types"](https://github.com/nikita-volkov/postgresql-types).
+
+## Overview
+
+This package provides the fundamental abstractions for mapping Haskell types to PostgreSQL types:
+
+- **Type classes** defining the algebra for PostgreSQL type mappings
+- **Error types** for decoding failures
+- **Metadata support** for type names, OIDs, and parameters
+- **No concrete implementations** - just the algebra
+
+## Type Classes
+
+### `IsScalar`
+
+The core type class for types that map to PostgreSQL values:
+
+```haskell
+class IsScalar a where
+  -- Type metadata
+  typeName :: Tagged a Text
+  baseOid :: Tagged a (Maybe Word32)
+  arrayOid :: Tagged a (Maybe Word32)
+  typeParams :: Tagged a [Text]
+  typeSignature :: Tagged a Text
+  
+  -- Binary format
+  binaryEncoder :: a -> Write.Write
+  binaryDecoder :: PtrPeeker.Variable (Either DecodingError a)
+  
+  -- Textual format
+  textualEncoder :: a -> TextBuilder.TextBuilder
+  textualDecoder :: Attoparsec.Parser a
+```
+
+This class enables:
+- **Binary encoding/decoding** using PostgreSQL's native binary format
+- **Textual encoding/decoding** using PostgreSQL's text representation  
+- **Type metadata** including OIDs and type signatures for parameterized types
+- **Round-trip fidelity** through all encoding combinations
+
+## Use Cases
+
+### For Library Authors
+
+Use this package to:
+- Define custom PostgreSQL type mappings compatible with the "postgresql-types" ecosystem
+- Create adapter libraries for different PostgreSQL client libraries
+- Build generic tools that work with any `IsScalar` instance
+
+### For Application Developers
+
+This package is typically used indirectly through:
+- ["postgresql-types"](https://github.com/nikita-volkov/postgresql-types) - Concrete type implementations
+- ["hasql-postgresql-types"](https://github.com/nikita-volkov/hasql-postgresql-types) - "hasql" integration
+- ["postgresql-simple-postgresql-types"](https://github.com/nikita-volkov/postgresql-simple-postgresql-types) - "postgresql-simple" integration
diff --git a/postgresql-types-algebra.cabal b/postgresql-types-algebra.cabal
new file mode 100644
--- /dev/null
+++ b/postgresql-types-algebra.cabal
@@ -0,0 +1,109 @@
+cabal-version: 3.0
+name: postgresql-types-algebra
+version: 0.0.1
+category: PostgreSQL, Codecs
+synopsis: Type classes for PostgreSQL type mappings
+description:
+  Core type classes and algebra extracted from the postgresql-types library.
+
+  Defines the fundamental abstractions for mapping Haskell types to PostgreSQL types,
+  including binary and textual encoding/decoding, type metadata, and error handling.
+
+  This package provides the type class algebra without concrete type implementations,
+  making it suitable as a lightweight dependency for libraries that want to define
+  their own PostgreSQL type mappings or work with the postgresql-types ecosystem.
+
+homepage: https://github.com/nikita-volkov/postgresql-types-algebra
+bug-reports: https://github.com/nikita-volkov/postgresql-types-algebra/issues
+author: Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright: (c) 2025, Nikita Volkov
+license: MIT
+license-file: LICENSE
+extra-doc-files:
+  LICENSE
+  README.md
+
+source-repository head
+  type: git
+  location: https://github.com/nikita-volkov/postgresql-types-algebra
+
+common base
+  default-language: Haskell2010
+  default-extensions:
+    ApplicativeDo
+    BangPatterns
+    BinaryLiterals
+    BlockArguments
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveFoldable
+    DeriveFunctor
+    DeriveTraversable
+    DerivingStrategies
+    DerivingVia
+    DuplicateRecordFields
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    LiberalTypeSynonyms
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    NamedFieldPuns
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    NumericUnderscores
+    OverloadedStrings
+    ParallelListComp
+    PatternGuards
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    StrictData
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UnboxedTuples
+    ViewPatterns
+
+common executable
+  import: base
+  ghc-options:
+    -O2
+    -threaded
+    -with-rtsopts=-N
+    -rtsopts
+    -funbox-strict-fields
+
+common test
+  import: base
+  ghc-options:
+    -threaded
+    -with-rtsopts=-N
+
+library
+  import: base
+  hs-source-dirs: src/library
+  exposed-modules:
+    PostgresqlTypes.Algebra
+
+  build-depends:
+    attoparsec >=0.14 && <0.19,
+    base >=4.11 && <5,
+    bytestring >=0.10 && <0.13,
+    ptr-peeker ^>=0.1,
+    ptr-poker ^>=0.1.2.16,
+    tagged ^>=0.8.9,
+    text >=1.2 && <3,
+    text-builder ^>=1.0.0.4,
diff --git a/src/library/PostgresqlTypes/Algebra.hs b/src/library/PostgresqlTypes/Algebra.hs
new file mode 100644
--- /dev/null
+++ b/src/library/PostgresqlTypes/Algebra.hs
@@ -0,0 +1,117 @@
+module PostgresqlTypes.Algebra where
+
+import qualified Data.Attoparsec.Text as Attoparsec
+import Data.ByteString (ByteString)
+import Data.Tagged (Tagged (..), untag)
+import Data.Text (Text)
+import Data.Word (Word32)
+import qualified PtrPeeker
+import qualified PtrPoker.Write as Write
+import TextBuilder (TextBuilder)
+import qualified TextBuilder
+import Prelude
+
+-- | Evidence that a type maps to a PostgreSQL scalar value.
+class IsScalar a where
+  -- | PostgreSQL schema name, if applicable.
+  schemaName :: Tagged a (Maybe Text)
+
+  -- | PostgreSQL type name.
+  typeName :: Tagged a Text
+
+  -- | PostgreSQL type OID, if known at compile time.
+  baseOid :: Tagged a (Maybe Word32)
+  baseOid = Tagged Nothing
+
+  -- | PostgreSQL array type OID, if known at compile time.
+  arrayOid :: Tagged a (Maybe Word32)
+  arrayOid = Tagged Nothing
+
+  -- | Static type parameters. E.g., the @n@ in @char(n)@.
+  typeParams :: Tagged a [Text]
+  typeParams = Tagged []
+
+  -- | Get the PostgreSQL type signature for a given Haskell type.
+  --
+  -- In case of parameterized types, the type parameters are included in the signature.
+  -- For example, for @'PostgresqlTypes.Types.Bpchar.Bpchar' 10@, the signature will be @bpchar(10)@.
+  typeSignature :: Tagged a Text
+  typeSignature =
+    let params = untag (typeParams @a)
+        name = untag (typeName @a)
+     in Tagged
+          if null params
+            then name
+            else
+              TextBuilder.toText
+                ( mconcat
+                    [ TextBuilder.text name,
+                      "(",
+                      TextBuilder.intercalateMap ", " TextBuilder.text params,
+                      ")"
+                    ]
+                )
+
+  -- | Encode the value in PostgreSQL binary format.
+  binaryEncoder :: a -> Write.Write
+
+  -- | Decode the value from PostgreSQL binary format.
+  binaryDecoder :: PtrPeeker.Variable (Either DecodingError a)
+
+  -- | Represent the value in PostgreSQL textual format.
+  textualEncoder :: a -> TextBuilder.TextBuilder
+
+  -- | Decode the value from PostgreSQL textual format.
+  textualDecoder :: Attoparsec.Parser a
+
+-- | Evidence that a type can be used as an element of a PostgreSQL range type.
+class (IsScalar a, Ord a) => IsRangeElement a where
+  -- | PostgreSQL range type name.
+  rangeTypeName :: Tagged a Text
+
+  -- | Statically known OID for the range type.
+  rangeBaseOid :: Tagged a (Maybe Word32)
+
+  -- | Statically known OID for the range array-type.
+  rangeArrayOid :: Tagged a (Maybe Word32)
+
+-- | Evidence that a type can be used as an element of a PostgreSQL multirange type.
+class (IsRangeElement a) => IsMultirangeElement a where
+  -- | PostgreSQL multirange type name.
+  multirangeTypeName :: Tagged a Text
+
+  -- | Statically known OID for the multirange type.
+  multirangeBaseOid :: Tagged a (Maybe Word32)
+
+  -- | Statically known OID for the multirange array-type.
+  multirangeArrayOid :: Tagged a (Maybe Word32)
+
+data DecodingError = DecodingError
+  { location :: [Text],
+    reason :: DecodingErrorReason
+  }
+  deriving stock (Show, Eq)
+
+data DecodingErrorReason
+  = ParsingDecodingErrorReason
+      -- | Details.
+      Text
+      -- | Input.
+      ByteString
+  | UnexpectedValueDecodingErrorReason
+      -- | Expected.
+      Text
+      -- | Actual.
+      Text
+  | -- | Unsupported server-side value.
+    --
+    -- Useful for defining decoders on a narrower type than the underlying PostgreSQL type.
+    --
+    -- This one does not signal a problem with the data itself, but rather that the data does not meet
+    -- the additional constraints imposed by the refinement.
+    UnsupportedValueDecodingErrorReason
+      -- | Details.
+      Text
+      -- | Value.
+      Text
+  deriving stock (Show, Eq)
