diff --git a/eventium-sql-common.cabal b/eventium-sql-common.cabal
--- a/eventium-sql-common.cabal
+++ b/eventium-sql-common.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           eventium-sql-common
-version:        0.4.0
+version:        0.5.0
 synopsis:       Common library for SQL event stores
 description:    Eventium-sql-common provides shared utilities and abstractions for SQL-based event stores.
                 It includes Persistent entity definitions, JSON serialization helpers, and common database
@@ -51,7 +51,7 @@
       aeson >=1.5 && <2.3
     , base >=4.9 && <5
     , bytestring >=0.10 && <0.13
-    , eventium-core >=0.4.0 && <0.5.0
+    , eventium-core >=0.5.0 && <0.6.0
     , mtl >=2.2 && <2.4
     , persistent >=2.14 && <2.18
     , text >=1.2 && <2.2
diff --git a/src/Eventium/Store/Sql/JSONString.hs b/src/Eventium/Store/Sql/JSONString.hs
--- a/src/Eventium/Store/Sql/JSONString.hs
+++ b/src/Eventium/Store/Sql/JSONString.hs
@@ -4,6 +4,7 @@
 module Eventium.Store.Sql.JSONString
   ( JSONString,
     jsonStringCodec,
+    upcastingJsonStringCodec,
     encodeJSON,
     decodeJSON,
   )
@@ -14,7 +15,8 @@
 import qualified Data.Text.Lazy.Encoding as TLE
 import Database.Persist
 import Database.Persist.Sql
-import Eventium.Codec (Codec (..))
+import Eventium.Codec (Codec (..), composeCodecs)
+import Eventium.SchemaEvolution (EventTypeName, SchemaRegistry, upcastingValueCodec)
 
 -- | A more specific type than just ByteString for JSON data.
 newtype JSONString = JSONString Text
@@ -31,6 +33,25 @@
   Codec
     encodeJSON
     decodeJSON
+
+-- | Like 'jsonStringCodec', but applies event schema evolution
+-- (upcast-on-read) between the domain type and the stored JSON. Drop-in
+-- replacement for 'jsonStringCodec' at reader/writer call sites: writes are
+-- wrapped in the current-version envelope, and reads normalize older stored
+-- events to the current shape via the 'SchemaRegistry'. The @eventTypeOf@
+-- function reads the event-type name from a payload (the app's tagging
+-- convention).
+--
+-- 'jsonStringCodec' specialises to @Codec 'Aeson.Value' JSONString@ (a 'Value'
+-- is trivially 'Aeson.ToJSON'\/'Aeson.FromJSON'), so this is just the
+-- value-level upcasting codec composed with the text (de)serialisation.
+upcastingJsonStringCodec ::
+  (Aeson.ToJSON a, Aeson.FromJSON a) =>
+  (Aeson.Value -> Maybe EventTypeName) ->
+  SchemaRegistry ->
+  Codec a JSONString
+upcastingJsonStringCodec eventTypeOf registry =
+  composeCodecs (upcastingValueCodec eventTypeOf registry) jsonStringCodec
 
 encodeJSON :: (Aeson.ToJSON a) => a -> JSONString
 encodeJSON = JSONString . TLE.decodeUtf8 . Aeson.encode
