diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 4ba9f499f..8daa03f84 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,6 +1,11 @@
 version: 2
 updates:
-  - package-ecosystem: ""
+  - package-ecosystem: "github-actions"
     directory: "/"
     schedule:
-      interval: "weekly"
\ No newline at end of file
+      interval: "weekly"
+
+  - package-ecosystem: "nuget"
+    directory: "/"
+    schedule:
+      interval: "daily"
\ No newline at end of file
diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml
index 5cbb8f1b6..338e3c5d7 100644
--- a/.github/workflows/preview.yml
+++ b/.github/workflows/preview.yml
@@ -5,21 +5,18 @@ on:
     branches:
       - dev
     paths-ignore:
-      - 'docs/**/*'
       - '**.md'
-      - '**.scss'
-      - '**.toml'
-      - '**.html'
 
 jobs:
   nuget:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v1
-      - uses: actions/setup-dotnet@v1
+      - uses: actions/checkout@v3
+      - uses: actions/setup-dotnet@v3
         with:
-          dotnet-version: '7.0.x'
+          dotnet-version: '8.0.x'
+          dotnet-quality: 'preview'
       - name: Create and push NuGet package
         run: |
           dotnet pack -c Debug -o nuget
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 525addb76..407a4e52d 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -5,21 +5,18 @@ on:
     tags:
       - '*'
     paths-ignore:
-      - 'docs/**/*'
       - '**.md'
-      - '**.scss'
-      - '**.toml'
-      - '**.html'
 
 jobs:
   nuget:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v1
-      - uses: actions/setup-dotnet@v1
+      - uses: actions/checkout@v3
+      - uses: actions/setup-dotnet@v3
         with:
-          dotnet-version: '7.0.x'
+          dotnet-version: '8.0.x'
+          dotnet-quality: 'preview'
       - name: Create and push NuGet package
         run: |
           dotnet pack -c Release -o nuget -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
new file mode 100644
index 000000000..cdff70950
--- /dev/null
+++ b/.github/workflows/pull-request.yml
@@ -0,0 +1,42 @@
+name: PR Build and test
+on:
+  workflow_dispatch:
+  pull_request:
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+#    env:
+#      TC_CLOUD_TOKEN: ${{ secrets.TC_TOKEN }}
+    steps:
+      -  
+        name: Checkout
+        uses: actions/checkout@v3
+      - 
+        name: Setup .NET
+        uses: actions/setup-dotnet@v3
+        with:
+          dotnet-version: '8.0.x'
+          dotnet-quality: 'preview'
+      -  
+        name: Install dependencies
+        run: dotnet restore
+      -  
+        name: Build
+        run: dotnet build --configuration Debug --no-restore
+      -  
+        name: Prepare Testcontainers Cloud agent
+        if: env.TC_CLOUD_TOKEN != ''
+        uses: atomicjar/testcontainers-cloud-setup-action@main
+      -  
+        name: Run tests
+        run: dotnet test --no-build
+      -  
+        name: Publish Test Results
+        uses: EnricoMi/publish-unit-test-result-action@v2
+        if: always()
+        with:
+          files: |
+            test-results/**/*.xml
+            test-results/**/*.trx
+            test-results/**/*.json
\ No newline at end of file
diff --git a/Eventuous.sln b/Eventuous.sln
index 3ab19b926..b0ad8f373 100644
--- a/Eventuous.sln
+++ b/Eventuous.sln
@@ -127,7 +127,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Eventuous.Tests.Kafka", "sr
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Eventuous.Gateway", "src\Gateway\src\Eventuous.Gateway\Eventuous.Gateway.csproj", "{E3D5B654-C68D-456B-8535-A9C38D37DFD2}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Eventuous.Gateway.Tests", "src\Gateway\test\Eventuous.Gateway.Tests\Eventuous.Gateway.Tests.csproj", "{F89E7F27-D198-41DD-AC24-79709EF07537}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Eventuous.Tests.Gateway", "src\Gateway\test\Eventuous.Tests.Gateway\Eventuous.Tests.Gateway.csproj", "{F89E7F27-D198-41DD-AC24-79709EF07537}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B1AAD6CA-2710-41E0-9495-B43C313D6BCA}"
 EndProject
diff --git a/src/Core/src/Eventuous.Application/Eventuous.Application.csproj b/src/Core/src/Eventuous.Application/Eventuous.Application.csproj
index a5d1c69cd..ae060c265 100644
--- a/src/Core/src/Eventuous.Application/Eventuous.Application.csproj
+++ b/src/Core/src/Eventuous.Application/Eventuous.Application.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <RootNamespace>Eventuous</RootNamespace>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="..\Eventuous.Domain\Eventuous.Domain.csproj"/>
diff --git a/src/Core/src/Eventuous.Diagnostics/Eventuous.Diagnostics.csproj b/src/Core/src/Eventuous.Diagnostics/Eventuous.Diagnostics.csproj
index 7d5c31aba..9a6c0bb53 100644
--- a/src/Core/src/Eventuous.Diagnostics/Eventuous.Diagnostics.csproj
+++ b/src/Core/src/Eventuous.Diagnostics/Eventuous.Diagnostics.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="..\Eventuous.Shared\Eventuous.Shared.csproj" />
     </ItemGroup>
diff --git a/src/Core/src/Eventuous.Domain/Eventuous.Domain.csproj b/src/Core/src/Eventuous.Domain/Eventuous.Domain.csproj
index 9e9f2a632..aa91350bc 100644
--- a/src/Core/src/Eventuous.Domain/Eventuous.Domain.csproj
+++ b/src/Core/src/Eventuous.Domain/Eventuous.Domain.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <RootNamespace>Eventuous</RootNamespace>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <EmbeddedResource Include="Exceptions\ExceptionMessages.restext">
diff --git a/src/Core/src/Eventuous.Persistence/Eventuous.Persistence.csproj b/src/Core/src/Eventuous.Persistence/Eventuous.Persistence.csproj
index aded805f4..b9e93b3c5 100644
--- a/src/Core/src/Eventuous.Persistence/Eventuous.Persistence.csproj
+++ b/src/Core/src/Eventuous.Persistence/Eventuous.Persistence.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <RootNamespace>Eventuous</RootNamespace>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <Compile Include="..\Eventuous.Diagnostics\DiagnosticName.cs">
diff --git a/src/Core/src/Eventuous.Producers/Eventuous.Producers.csproj b/src/Core/src/Eventuous.Producers/Eventuous.Producers.csproj
index 696272806..15ad0b516 100644
--- a/src/Core/src/Eventuous.Producers/Eventuous.Producers.csproj
+++ b/src/Core/src/Eventuous.Producers/Eventuous.Producers.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="..\Eventuous.Diagnostics\Eventuous.Diagnostics.csproj"/>
     </ItemGroup>
diff --git a/src/Core/src/Eventuous.Serialization/Eventuous.Serialization.csproj b/src/Core/src/Eventuous.Serialization/Eventuous.Serialization.csproj
index e6750e9de..227fbab9f 100644
--- a/src/Core/src/Eventuous.Serialization/Eventuous.Serialization.csproj
+++ b/src/Core/src/Eventuous.Serialization/Eventuous.Serialization.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <RootNamespace>Eventuous</RootNamespace>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="..\Eventuous.Shared\Eventuous.Shared.csproj"/>
diff --git a/src/Core/src/Eventuous.Shared/Eventuous.Shared.csproj b/src/Core/src/Eventuous.Shared/Eventuous.Shared.csproj
index 29826f633..e47b6aae7 100644
--- a/src/Core/src/Eventuous.Shared/Eventuous.Shared.csproj
+++ b/src/Core/src/Eventuous.Shared/Eventuous.Shared.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <RootNamespace>Eventuous</RootNamespace>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapEventSource.cs b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapEventSource.cs
index ead8e0e0f..c2f854260 100644
--- a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapEventSource.cs
+++ b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapEventSource.cs
@@ -11,9 +11,10 @@ namespace Eventuous;
 class TypeMapEventSource : EventSource {
     public static readonly TypeMapEventSource Log = new();
 
-    const int TypeNotMappedToNameId     = 8;
-    const int TypeNameNotMappedToTypeId = 9;
-    const int TypeMapRegisteredId       = 10;
+    const int TypeNotMappedToNameId      = 8;
+    const int TypeNameNotMappedToTypeId  = 9;
+    const int TypeMapRegisteredId        = 10;
+    const int TypeMapAlreadyRegisteredId = 11;
 
     [NonEvent]
     public void TypeNotMappedToName(Type type)
@@ -31,4 +32,9 @@ public void TypeNameNotMappedToType(string typeName)
     public void TypeMapRegistered(string type, string typeName) {
         if (IsEnabled(EventLevel.Verbose, EventKeywords.All)) WriteEvent(TypeMapRegisteredId, type, typeName);
     }
+
+    [Event(TypeMapAlreadyRegisteredId, Message = "Type already {0} registered as {1}", Level = EventLevel.Verbose)]
+    public void TypeAlreadyRegistered(string type, string typeName) {
+        if (IsEnabled(EventLevel.Warning, EventKeywords.All)) WriteEvent(TypeMapAlreadyRegisteredId, type, typeName);
+    }
 }
diff --git a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapper.cs b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapper.cs
index b526a0ebb..9ce375ab7 100644
--- a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapper.cs
+++ b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapper.cs
@@ -42,6 +42,7 @@ public class TypeMapper {
     public string GetTypeName<T>() {
         if (!_map.TryGetValue(typeof(T), out var name)) {
             Log.TypeNotMappedToName(typeof(T));
+
             throw new UnregisteredTypeException(typeof(T));
         }
 
@@ -54,6 +55,7 @@ public string GetTypeName(object o, bool fail = true) {
         if (!fail) return "unknown";
 
         Log.TypeNotMappedToName(o.GetType());
+
         throw new UnregisteredTypeException(o.GetType());
     }
 
@@ -61,6 +63,7 @@ public string GetTypeName(object o, bool fail = true) {
     public string GetTypeNameByType(Type type) {
         if (!_map.TryGetValue(type, out var name)) {
             Log.TypeNotMappedToName(type);
+
             throw new UnregisteredTypeException(type);
         }
 
@@ -70,6 +73,7 @@ public string GetTypeNameByType(Type type) {
     public Type GetType(string typeName) {
         if (!_reverseMap.TryGetValue(typeName, out var type)) {
             Log.TypeNameNotMappedToType(typeName);
+
             throw new UnregisteredTypeException(typeName);
         }
 
@@ -84,8 +88,19 @@ public void AddType<T>(string name)
 
     readonly object _lock = new();
 
+    [PublicAPI]
     public void AddType(Type type, string name) {
         lock (_lock) {
+            if (_map.TryGetValue(type, out var registeredName)) {
+                if (registeredName != name) {
+                    throw new ArgumentException($"Type {type.FullName} is already registered with a different name {registeredName}", nameof(name));
+                }
+
+                Log.TypeAlreadyRegistered(type.Name, name);
+
+                return;
+            }
+
             _reverseMap[name] = type;
             _map[type]        = name;
         }
@@ -129,15 +144,16 @@ IEnumerable<Assembly> Get(Assembly assembly) {
                 // ReSharper disable once ConvertClosureToMethodGroup
                 var referenced = assembly.GetReferencedAssemblies().Where(name => NamePredicate(name));
                 var assemblies = referenced.Select(Assembly.Load).ToList();
+
                 return assemblies.Concat(assemblies.SelectMany(Get)).Distinct();
             }
         }
 
         bool NamePredicate(AssemblyName name)
-            => name.Name != null                   &&
-               !name.Name.StartsWith("System.")    &&
-               !name.Name.StartsWith("Microsoft.") &&
-               !name.Name.StartsWith("netstandard");
+            => name.Name != null                    &&
+                !name.Name.StartsWith("System.")    &&
+                !name.Name.StartsWith("Microsoft.") &&
+                !name.Name.StartsWith("netstandard");
     }
 
     static readonly Type AttributeType = typeof(EventTypeAttribute);
@@ -167,7 +183,9 @@ public class EventTypeAttribute(string eventType) : Attribute {
 
 public class UnregisteredTypeException : Exception {
     // ReSharper disable once SuggestBaseTypeForParameterInConstructor
-    public UnregisteredTypeException(Type type) : base($"Type {type.Name} is not registered in the type map") { }
+    public UnregisteredTypeException(Type type)
+        : base($"Type {type.Name} is not registered in the type map") { }
 
-    public UnregisteredTypeException(string type) : base($"Type name {type} is not registered in the type map") { }
+    public UnregisteredTypeException(string type)
+        : base($"Type name {type} is not registered in the type map") { }
 }
diff --git a/src/Core/src/Eventuous.Subscriptions/Checkpoints/NoOpCheckpointStore.cs b/src/Core/src/Eventuous.Subscriptions/Checkpoints/NoOpCheckpointStore.cs
index 83a5ace0a..945599300 100644
--- a/src/Core/src/Eventuous.Subscriptions/Checkpoints/NoOpCheckpointStore.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Checkpoints/NoOpCheckpointStore.cs
@@ -9,9 +9,10 @@ public class NoOpCheckpointStore(ulong? start = null) : ICheckpointStore {
     Checkpoint _start = new("", start);
 
     public ValueTask<Checkpoint> GetLastCheckpoint(string checkpointId, CancellationToken cancellationToken) {
-        Logger.Current.CheckpointLoaded(this, _start);
+        var checkpoint = _start with { Id = checkpointId };
+        Logger.Current.CheckpointLoaded(this, checkpoint);
 
-        return new ValueTask<Checkpoint>(_start);
+        return new ValueTask<Checkpoint>(checkpoint);
     }
 
     public ValueTask<Checkpoint> StoreCheckpoint(Checkpoint checkpoint, bool force, CancellationToken cancellationToken) {
diff --git a/src/Core/src/Eventuous.Subscriptions/Context/IMessageConsumeContext.cs b/src/Core/src/Eventuous.Subscriptions/Context/IMessageConsumeContext.cs
index ce21f47e4..f596bec3f 100644
--- a/src/Core/src/Eventuous.Subscriptions/Context/IMessageConsumeContext.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Context/IMessageConsumeContext.cs
@@ -11,27 +11,82 @@ namespace Eventuous.Subscriptions.Context;
 /// Base interface for a consume context, which doesn't include the payload.
 /// </summary>
 public interface IBaseConsumeContext {
-    string            MessageId         { get; }
-    string            MessageType       { get; }
+    /// <summary>
+    /// Unique identifier of the message
+    /// </summary>
+    string MessageId { get; }
+    /// <summary>
+    /// Message type delivered in the message metadata or headers
+    /// </summary>
+    string MessageType { get; }
+    /// <summary>
+    /// Message content type, e.g. application/json
+    /// </summary>
     string            ContentType       { get; }
+    /// <summary>
+    /// Stream name where message was read from. When reading from a category or $all, this will be the original stream name.
+    /// </summary>
     StreamName        Stream            { get; }
+    /// <summary>
+    /// Event number in the original event stream
+    /// </summary>
+    ulong             EventNumber       { get; }
+    /// <summary>
+    /// Position of the received message in the subscription stream. It can differ from <see cref="EventNumber"/> when reading from a category or $all.
+    /// </summary>
     ulong             StreamPosition    { get; }
+    /// <summary>
+    /// Message position in the global log
+    /// </summary>
     ulong             GlobalPosition    { get; }
+    /// <summary>
+    /// System metadata value of the message produce date and time in UTC
+    /// </summary>
     DateTime          Created           { get; }
+    /// <summary>
+    /// Metadata stored alongside the message
+    /// </summary>
     Metadata?         Metadata          { get; }
+    /// <summary>
+    /// Baggage items for the consume context
+    /// </summary>
     ContextItems      Items             { get; }
+    /// <summary>
+    /// Diagnostic activity parent context (can be remote context)
+    /// </summary>
     ActivityContext?  ParentContext     { get; set; }
+    /// <summary>
+    /// Collection of message handling results, expected one result per handler
+    /// </summary>
     HandlingResults   HandlingResults   { get; }
+    /// <summary>
+    /// Cancellation token passed from the subscription
+    /// </summary>
     CancellationToken CancellationToken { get; set; }
+    /// <summary>
+    /// Message sequence number in the subscription (can be maintained locally, in process)
+    /// </summary>
     ulong             Sequence          { get; }
+    /// <summary>
+    /// Subscription identifier, can also be used as a checkpoint
+    /// </summary>
     string            SubscriptionId    { get; }
+    /// <summary>
+    /// Logging context for the consume pipeline
+    /// </summary>
     LogContext        LogContext        { get; set; }
 }
 
 public interface IMessageConsumeContext : IBaseConsumeContext {
+    /// <summary>
+    /// Deserialized message payload
+    /// </summary>
     object? Message { get; }
 }
 
 public interface IMessageConsumeContext<out T> : IBaseConsumeContext where T : class {
+    /// <summary>
+    /// Deserialized message payload
+    /// </summary>
     T Message { get; }
 }
diff --git a/src/Core/src/Eventuous.Subscriptions/Context/MessageConsumeContext.cs b/src/Core/src/Eventuous.Subscriptions/Context/MessageConsumeContext.cs
index 8e8b96d1c..f608bf1e6 100644
--- a/src/Core/src/Eventuous.Subscriptions/Context/MessageConsumeContext.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Context/MessageConsumeContext.cs
@@ -12,6 +12,7 @@ public class MessageConsumeContext(
         string            eventType,
         string            contentType,
         string            stream,
+        ulong             eventNumber,
         ulong             streamPosition,
         ulong             globalPosition,
         ulong             sequence,
@@ -22,21 +23,39 @@ public class MessageConsumeContext(
         CancellationToken cancellationToken
     )
     : IMessageConsumeContext {
+    /// <inheritdoc />
     public string            MessageId         { get; } = eventId;
+    /// <inheritdoc />
     public string            MessageType       { get; } = eventType;
+    /// <inheritdoc />
     public string            ContentType       { get; } = contentType;
+    /// <inheritdoc />
     public StreamName        Stream            { get; } = new(stream);
+    /// <inheritdoc />
+    public ulong             EventNumber       { get; } = eventNumber;
+    /// <inheritdoc />
     public ulong             StreamPosition    { get; } = streamPosition;
+    /// <inheritdoc />
     public ulong             GlobalPosition    { get; } = globalPosition;
+    /// <inheritdoc />
     public DateTime          Created           { get; } = created;
+    /// <inheritdoc />
     public Metadata?         Metadata          { get; } = metadata;
+    /// <inheritdoc />
     public object?           Message           { get; } = message;
+    /// <inheritdoc />
     public ContextItems      Items             { get; } = new();
+    /// <inheritdoc />
     public ActivityContext?  ParentContext     { get; set; }
+    /// <inheritdoc />
     public HandlingResults   HandlingResults   { get; }      = new();
+    /// <inheritdoc />
     public CancellationToken CancellationToken { get; set; } = cancellationToken;
+    /// <inheritdoc />
     public ulong             Sequence          { get; }      = sequence;
+    /// <inheritdoc />
     public string            SubscriptionId    { get; }      = subscriptionId;
+    /// <inheritdoc />
     public LogContext        LogContext        { get; set; } = Logger.Current;
 }
 
diff --git a/src/Core/src/Eventuous.Subscriptions/Context/WrappedConsumeContext.cs b/src/Core/src/Eventuous.Subscriptions/Context/WrappedConsumeContext.cs
index 32ea63a1e..d12aabe2d 100644
--- a/src/Core/src/Eventuous.Subscriptions/Context/WrappedConsumeContext.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Context/WrappedConsumeContext.cs
@@ -14,6 +14,7 @@ public abstract class WrappedConsumeContext(IMessageConsumeContext innerContext)
     public string          MessageType     => InnerContext.MessageType;
     public string          ContentType     => InnerContext.ContentType;
     public StreamName      Stream          => InnerContext.Stream;
+    public ulong           EventNumber     => InnerContext.EventNumber;
     public ulong           StreamPosition  => InnerContext.StreamPosition;
     public ulong           GlobalPosition  => InnerContext.GlobalPosition;
     public DateTime        Created         => InnerContext.Created;
diff --git a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
index d70b72e12..1363c6827 100644
--- a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
+++ b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
@@ -34,7 +34,7 @@ static ConsumePipe ConfigurePipe(ConsumePipe pipe, int concurrencyLimit)
         options.BatchSize,
         loggerFactory
     );
-    ICheckpointStore CheckpointStore { get; } = Ensure.NotNull<ICheckpointStore>(checkpointStore);
+    ICheckpointStore CheckpointStore { get; } = Ensure.NotNull(checkpointStore);
 
     protected abstract EventPosition GetPositionFromContext(IMessageConsumeContext context);
 
diff --git a/src/Core/src/Eventuous.Subscriptions/Eventuous.Subscriptions.csproj b/src/Core/src/Eventuous.Subscriptions/Eventuous.Subscriptions.csproj
index 48961086e..7983c44b5 100644
--- a/src/Core/src/Eventuous.Subscriptions/Eventuous.Subscriptions.csproj
+++ b/src/Core/src/Eventuous.Subscriptions/Eventuous.Subscriptions.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
diff --git a/src/Core/src/Eventuous.Subscriptions/SubscriptionOptions.cs b/src/Core/src/Eventuous.Subscriptions/SubscriptionOptions.cs
index f33253006..d58d23db1 100644
--- a/src/Core/src/Eventuous.Subscriptions/SubscriptionOptions.cs
+++ b/src/Core/src/Eventuous.Subscriptions/SubscriptionOptions.cs
@@ -4,7 +4,7 @@
 namespace Eventuous.Subscriptions;
 
 [PublicAPI]
-public record SubscriptionOptions {
+public abstract record SubscriptionOptions {
     /// <summary>
     /// Subscription id is used to match event handlers with one subscription
     /// </summary>
@@ -21,7 +21,7 @@ public record SubscriptionOptions {
     public IEventSerializer? EventSerializer { get; set; }
 }
 
-public record SubscriptionWithCheckpointOptions : SubscriptionOptions {
+public abstract record SubscriptionWithCheckpointOptions : SubscriptionOptions {
     public int BatchSize     { get; set; } = 100;
     public int CommitDelayMs { get; set; } = 5000;
 }
diff --git a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj
index ce2a4f551..840d05396 100644
--- a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj
+++ b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHelpers>true</IncludeTestHelpers>
         <IncludeSutApp>true</IncludeSutApp>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
 </Project>
diff --git a/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj b/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj
index efb248739..1d616f398 100644
--- a/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj
+++ b/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
         <IncludeTestHelpers>true</IncludeTestHelpers>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
 </Project>
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj
index 54dfc6ff6..8ef6c259e 100644
--- a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj
+++ b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
         <IncludeTestHelpers>true</IncludeTestHelpers>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(DiagRoot)\Eventuous.Diagnostics.OpenTelemetry\Eventuous.Diagnostics.OpenTelemetry.csproj" />
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs
index ec83fac27..49a9ca864 100644
--- a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs
+++ b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs
@@ -56,6 +56,7 @@ public async Task SubsShouldHaveHandlers(int position, Type handlerType) {
             0,
             0,
             0,
+            0,
             DateTime.UtcNow,
             new TestEvent(),
             new Metadata(),
diff --git a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj
index c4cf64ca1..9397ff86f 100644
--- a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj
+++ b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutApp>true</IncludeSutApp>
         <IncludeTestHelpers>true</IncludeTestHelpers>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="NodaTime.Serialization.SystemTextJson" />
diff --git a/src/Diagnostics/src/Eventuous.Diagnostics.Logging/Eventuous.Diagnostics.Logging.csproj b/src/Diagnostics/src/Eventuous.Diagnostics.Logging/Eventuous.Diagnostics.Logging.csproj
index 9b7999d92..140afebfc 100644
--- a/src/Diagnostics/src/Eventuous.Diagnostics.Logging/Eventuous.Diagnostics.Logging.csproj
+++ b/src/Diagnostics/src/Eventuous.Diagnostics.Logging/Eventuous.Diagnostics.Logging.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.Logging.Abstractions"/>
     </ItemGroup>
diff --git a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj
index a723c8758..4a9a13432 100644
--- a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj
+++ b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Subscriptions\Eventuous.Subscriptions.csproj"/>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Application\Eventuous.Application.csproj"/>
diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj
index e901e334e..66582221f 100644
--- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj
+++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj
@@ -1,10 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
+        <IncludeTestHelpers>true</IncludeTestHelpers>
         <IncludeSutApp>true</IncludeSutApp>
         <IncludeSutSubs>true</IncludeSutSubs>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.Diagnostics.OpenTelemetry\Eventuous.Diagnostics.OpenTelemetry.csproj" />
@@ -13,7 +12,6 @@
     </ItemGroup>
     <ItemGroup>
         <PackageReference Include="NodaTime.Serialization.SystemTextJson" />
-        <PackageReference Include="Testcontainers.EventStoreDb" />
     </ItemGroup>
     <ItemGroup>
         <Using Include="NodaTime" />
diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/IntegrationFixture.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/IntegrationFixture.cs
index c680f7c55..de36f6e00 100644
--- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/IntegrationFixture.cs
+++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/IntegrationFixture.cs
@@ -27,9 +27,7 @@ public IntegrationFixture() {
     }
 
     public virtual async Task InitializeAsync() {
-        _esdbContainer = new EventStoreDbBuilder()
-            .WithImage("eventstore/eventstore:22.10.2-alpha-arm64v8")
-            .Build();
+        _esdbContainer = new EventStoreDbContainerBuilder().Build();
         await _esdbContainer.StartAsync();
         var settings = EventStoreClientSettings.Create(_esdbContainer.GetConnectionString());
         Client         = new EventStoreClient(settings);
diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
index e5d44bea2..986c8ce83 100644
--- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
+++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
@@ -9,22 +9,31 @@ public sealed class MetricsTests(SubscriptionFixture fixture, ITestOutputHelper
     const string SubscriptionId = "test-sub";
 
     [Fact]
-    public void ShouldMeasureSubscription() {
+    public void Debug() {
         _exporter.Collect(Timeout.Infinite);
         var values = _exporter.CollectValues();
 
         foreach (var value in values) {
             outputHelper.WriteLine(value.ToString());
         }
+    }
+
+    [Fact]
+    public void ShouldMeasureSubscriptionGapCount() {
+        _exporter.Collect(Timeout.Infinite);
+        var values = _exporter.CollectValues();
 
         var counter  = _host.Services.GetRequiredService<MessageCounter>();
         var gapCount = GetValue(values, SubscriptionMetrics.GapCountMetricName)!;
+        var duration = GetValue(values, SubscriptionMetrics.ProcessingRateName)!;
+
+        var expectedGap = SubscriptionFixture.Count - counter.Count + 1;
+
         gapCount.Should().NotBeNull();
-        gapCount.Value.Should().Be(SubscriptionFixture.Count - counter.Count + 1);
+        gapCount.Value.Should().BeInRange(expectedGap, expectedGap + 5);
         GetTag(gapCount, SubscriptionMetrics.SubscriptionIdTag).Should().Be(SubscriptionId);
         GetTag(gapCount, "test").Should().Be("foo");
 
-        var duration = GetValue(values, SubscriptionMetrics.ProcessingRateName)!;
         duration.Should().NotBeNull();
         GetTag(duration, SubscriptionMetrics.SubscriptionIdTag).Should().Be(SubscriptionId);
         GetTag(duration, SubscriptionMetrics.MessageTypeTag).Should().Be(TestEvent.TypeName);
@@ -68,9 +77,12 @@ public async Task InitializeAsync() {
 
         _host = new TestServer(builder);
         var counter = _host.Services.GetRequiredService<MessageCounter>();
+
         while (counter.Count < SubscriptionFixture.Count / 3) {
             await Task.Delay(10);
         }
+
+        await Task.Delay(500);
     }
 
     public Task DisposeAsync() {
@@ -90,7 +102,7 @@ class TestHandler(MessageCounter counter, ILogger<TestHandler> log) : BaseEventH
         public override async ValueTask<EventHandlingStatus> HandleEvent(IMessageConsumeContext context) {
             await Task.Delay(10, context.CancellationToken);
             counter.Increment();
-            log.LogInformation("Handled event {Number} {EventId}", counter.Count, context.MessageId);
+            log.LogDebug("Handled event {Number} {EventId}", counter.Count, context.MessageId);
 
             return EventHandlingStatus.Success;
         }
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 297da4e8f..e074e568c 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -14,6 +14,32 @@
         <LocalRoot>..\..\src</LocalRoot>
     </PropertyGroup>
 
+    <PropertyGroup Condition="$(MSBuildProjectName.StartsWith('Eventuous.Tests'))">
+        <VSTestLogger>trx%3bLogFileName=$(MSBuildProjectName).trx</VSTestLogger>
+        <VSTestResultsDirectory>$(RepoRoot)/test-results/$(TargetFramework)</VSTestResultsDirectory>
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="!($(MSBuildProjectName.StartsWith('Eventuous.Tests')))">
+        <IsPackable>true</IsPackable>
+        <IncludeSymbols>true</IncludeSymbols>
+        <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+        <PublishRepositoryUrl>true</PublishRepositoryUrl>
+        <EmbedUntrackedSources>true</EmbedUntrackedSources>
+        <UpdateVersionProperties>true</UpdateVersionProperties>
+        <PackageIcon>e-logo.png</PackageIcon>
+        <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
+        <RepositoryUrl>https://github.com/eventuous/Eventuous.git</RepositoryUrl>
+        <RepositoryType>git</RepositoryType>
+        <Description>Production-grade Event Sourcing library</Description>
+        <Authors>Alexey Zimarev and Eventuous community</Authors>
+        <Copyright>Ubiquitous AS</Copyright>
+        <PackageProjectUrl>https://eventuous.dev</PackageProjectUrl>
+        <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).xml</DocumentationFile>
+        <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    </PropertyGroup>
+
     <ItemGroup Condition="'$(IsPackable)' == 'true'">
         <PackageReference Include="MinVer" PrivateAssets="All"/>
         <PackageReference Include="JetBrains.Annotations" PrivateAssets="All"/>
@@ -41,28 +67,6 @@
         <Using Include="Microsoft.Extensions.Logging"/>
     </ItemGroup>
 
-    <PropertyGroup Condition="'$(IsTestProject)' == 'true'">
-        <IsPackable>false</IsPackable>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(IsTestProject)' != 'true'">
-        <IsPackable>true</IsPackable>
-        <IncludeSymbols>true</IncludeSymbols>
-        <SymbolPackageFormat>snupkg</SymbolPackageFormat>
-        <PublishRepositoryUrl>true</PublishRepositoryUrl>
-        <EmbedUntrackedSources>true</EmbedUntrackedSources>
-        <UpdateVersionProperties>true</UpdateVersionProperties>
-        <PackageIcon>e-logo.png</PackageIcon>
-        <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
-        <RepositoryUrl>https://github.com/eventuous/Eventuous.git</RepositoryUrl>
-        <RepositoryType>git</RepositoryType>
-        <Description>Production-grade Event Sourcing library</Description>
-        <Authors>Alexey Zimarev and Eventuous community</Authors>
-        <Copyright>Ubiquitous AS</Copyright>
-        <PackageProjectUrl>https://eventuous.dev</PackageProjectUrl>
-        <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).xml</DocumentationFile>
-    </PropertyGroup>
-
     <ItemGroup Condition="'$(IncludeTestHost)' == 'true'">
         <PackageReference Include="Microsoft.AspNetCore.TestHost"/>
     </ItemGroup>
diff --git a/src/Elastic/docker-compose.yml b/src/Elastic/docker-compose.yml
deleted file mode 100644
index 37cafffb3..000000000
--- a/src/Elastic/docker-compose.yml
+++ /dev/null
@@ -1,129 +0,0 @@
-version: '3.7'
-
-services:
-  es01:
-    image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
-    container_name: eventuous-es01
-    environment:
-      - node.name=es01
-      - cluster.name=es-docker-cluster
-      - discovery.seed_hosts=es02,es03,es04
-      - cluster.initial_master_nodes=es01,es02,es03,es04
-      - node.roles=master,data_content,data_hot
-      - path.repo=/usr/share/elasticsearch/snapshots
-      - xpack.security.enabled=false
-      #- bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    ulimits:
-      memlock:
-        soft: -1
-        hard: -1
-    mem_limit: 1Gb
-    volumes:
-      - hot_data:/usr/share/elasticsearch/data
-      - snapshots:/usr/share/elasticsearch/snapshots
-    ports:
-      - "9200:9200"
-    networks:
-      - elastic
-  
-  es02:
-    image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
-    container_name: eventuous-es02
-    environment:
-      - node.name=es02
-      - cluster.name=es-docker-cluster
-      - discovery.seed_hosts=es01,es03,es04
-      - cluster.initial_master_nodes=es01,es02,es03,es04
-      - node.roles=master,data_warm
-      - path.repo=/usr/share/elasticsearch/snapshots
-      - xpack.security.enabled=false
-      #- bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    ulimits:
-      memlock:
-        soft: -1
-        hard: -1
-    mem_limit: 1Gb
-    volumes:
-      - warm_data:/usr/share/elasticsearch/data
-      - snapshots:/usr/share/elasticsearch/snapshots
-    networks:
-      - elastic
-  
-  es03:
-    image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
-    container_name: eventuous-es03
-    environment:
-      - node.name=es03
-      - cluster.name=es-docker-cluster
-      - discovery.seed_hosts=es01,es02,es04
-      - cluster.initial_master_nodes=es01,es02,es03,es04
-      - node.roles=master,data_cold
-      - path.repo=/usr/share/elasticsearch/snapshots
-      - xpack.security.enabled=false
-      #- bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    ulimits:
-      memlock:
-        soft: -1
-        hard: -1
-    mem_limit: 1Gb
-    volumes:
-      - cold_data:/usr/share/elasticsearch/data
-      - snapshots:/usr/share/elasticsearch/snapshots
-    networks:
-      - elastic
-  
-  es04:
-    image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
-    container_name: eventuous-es04
-    environment:
-      - node.name=es04
-      - cluster.name=es-docker-cluster
-      - discovery.seed_hosts=es01,es02,es03
-      - cluster.initial_master_nodes=es01,es02,es03,es04
-      - node.roles=master,data_frozen
-      - path.repo=/usr/share/elasticsearch/snapshots
-      - xpack.searchable.snapshot.shared_cache.size=10%
-      - xpack.security.enabled=false
-      #- bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    ulimits:
-      memlock:
-        soft: -1
-        hard: -1
-    mem_limit: 1Gb
-    volumes:
-      - frozen_data:/usr/share/elasticsearch/data
-      - snapshots:/usr/share/elasticsearch/snapshots
-    networks:
-      - elastic
-  
-  kib01:
-    image: docker.elastic.co/kibana/kibana:7.15.0
-    container_name: eventuous-kibana
-    ports:
-      - "5601:5601"
-    environment:
-      ELASTICSEARCH_URL: http://es01:9200
-      ELASTICSEARCH_HOSTS: '["http://es01:9200","http://es02:9200","http://es03:9200","http://es04:9200"]'
-    mem_limit: 1Gb
-    networks:
-      - elastic
-
-volumes:
-  hot_data:
-    driver: local
-  warm_data:
-    driver: local
-  cold_data:
-    driver: local
-  frozen_data:
-    driver: local
-  snapshots:
-    driver: local
-
-networks:
-  elastic:
-    driver: bridge
\ No newline at end of file
diff --git a/src/EventStore/src/Eventuous.EventStore/Eventuous.EventStore.csproj b/src/EventStore/src/Eventuous.EventStore/Eventuous.EventStore.csproj
index f544c4437..03ffedec2 100644
--- a/src/EventStore/src/Eventuous.EventStore/Eventuous.EventStore.csproj
+++ b/src/EventStore/src/Eventuous.EventStore/Eventuous.EventStore.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <NoWarn>CA2254</NoWarn>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllPersistentSubscription.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllPersistentSubscription.cs
index dc6c266b9..14f134b52 100644
--- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllPersistentSubscription.cs
+++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllPersistentSubscription.cs
@@ -16,11 +16,12 @@ public class AllPersistentSubscription : PersistentSubscriptionBase<AllPersisten
     /// <param name="consumePipe">Consume pipe, usually provided by the builder</param>
     /// <param name="loggerFactory">Optional logger factory</param>
     public AllPersistentSubscription(
-        EventStoreClient                 eventStoreClient,
-        AllPersistentSubscriptionOptions options,
-        ConsumePipe                      consumePipe,
-        ILoggerFactory?                  loggerFactory
-    ) : base(eventStoreClient, options, consumePipe, loggerFactory) { }
+            EventStoreClient                 eventStoreClient,
+            AllPersistentSubscriptionOptions options,
+            ConsumePipe                      consumePipe,
+            ILoggerFactory?                  loggerFactory
+        )
+        : base(eventStoreClient, options, consumePipe, loggerFactory) { }
 
     /// <summary>
     /// Creates EventStoreDB persistent subscription service for a given stream
@@ -32,22 +33,23 @@ public AllPersistentSubscription(
     /// <param name="metaSerializer"></param>
     /// <param name="loggerFactory"></param>
     public AllPersistentSubscription(
-        EventStoreClient     eventStoreClient,
-        string               subscriptionId,
-        ConsumePipe          consumerPipe,
-        IEventSerializer?    eventSerializer = null,
-        IMetadataSerializer? metaSerializer  = null,
-        ILoggerFactory?      loggerFactory   = null
-    ) : this(
-        eventStoreClient,
-        new AllPersistentSubscriptionOptions {
-            SubscriptionId     = subscriptionId,
-            EventSerializer    = eventSerializer,
-            MetadataSerializer = metaSerializer
-        },
-        consumerPipe,
-        loggerFactory
-    ) { }
+            EventStoreClient     eventStoreClient,
+            string               subscriptionId,
+            ConsumePipe          consumerPipe,
+            IEventSerializer?    eventSerializer = null,
+            IMetadataSerializer? metaSerializer  = null,
+            ILoggerFactory?      loggerFactory   = null
+        )
+        : this(
+            eventStoreClient,
+            new AllPersistentSubscriptionOptions {
+                SubscriptionId     = subscriptionId,
+                EventSerializer    = eventSerializer,
+                MetadataSerializer = metaSerializer
+            },
+            consumerPipe,
+            loggerFactory
+        ) { }
 
     /// <summary>
     /// Creates EventStoreDB persistent subscription consumer group for $all
@@ -72,10 +74,10 @@ protected override Task CreatePersistentSubscription(PersistentSubscriptionSetti
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
     protected override Task<PersistentSubscription> LocalSubscribe(
-        Func<PersistentSubscription, ResolvedEvent, int?, CancellationToken, Task> eventAppeared,
-        Action<PersistentSubscription, SubscriptionDroppedReason, Exception?>?     subscriptionDropped,
-        CancellationToken                                                          cancellationToken
-    )
+            Func<PersistentSubscription, ResolvedEvent, int?, CancellationToken, Task> eventAppeared,
+            Action<PersistentSubscription, SubscriptionDroppedReason, Exception?>?     subscriptionDropped,
+            CancellationToken                                                          cancellationToken
+        )
         => SubscriptionClient.SubscribeToAllAsync(
             Options.SubscriptionId,
             eventAppeared,
@@ -85,11 +87,7 @@ CancellationToken                                                          cance
             cancellationToken
         );
 
-    /// <summary>
-    /// Gets the position of the event in the stream
-    /// </summary>
-    /// <param name="re"></param>
-    /// <returns></returns>
+    /// <inheritdoc />
     protected override ulong GetContextStreamPosition(ResolvedEvent re)
         => re.Event.Position.CommitPosition;
 
diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllStreamSubscription.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllStreamSubscription.cs
index b9f88d93a..da2a75bdf 100644
--- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllStreamSubscription.cs
+++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/AllStreamSubscription.cs
@@ -114,6 +114,7 @@ IMessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancell
             re.Event.ContentType,
             re.Event.EventStreamId,
             re.Event.EventNumber,
+            re.OriginalEventNumber,
             re.Event.Position.CommitPosition,
             _sequence++,
             re.Event.Created,
diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/Options/EventStoreSubscriptionWithCheckpointOptions.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/Options/EventStoreSubscriptionWithCheckpointOptions.cs
index ac1ab1cec..e8f75388e 100644
--- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/Options/EventStoreSubscriptionWithCheckpointOptions.cs
+++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/Options/EventStoreSubscriptionWithCheckpointOptions.cs
@@ -3,11 +3,14 @@
 
 namespace Eventuous.EventStore.Subscriptions;
 
-public record EventStoreSubscriptionWithCheckpointOptions : SubscriptionWithCheckpointOptions {
+/// <summary>
+/// Options base record for EventStoreDB checkpoint-based subscriptions
+/// </summary>
+public abstract record EventStoreSubscriptionWithCheckpointOptions : SubscriptionWithCheckpointOptions {
     /// <summary>
     /// User credentials
     /// </summary>
-    public UserCredentials? Credentials { get; set; }
+    public UserCredentials? Credentials { get; [PublicAPI] set; }
 
     /// <summary>
     /// Resolve link events
diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/PersistentSubscriptionBase.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/PersistentSubscriptionBase.cs
index 0c8d557ee..cedf7c82f 100644
--- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/PersistentSubscriptionBase.cs
+++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/PersistentSubscriptionBase.cs
@@ -190,6 +190,7 @@ IMessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancell
             re.Event.EventType,
             re.Event.ContentType,
             re.Event.EventStreamId,
+            re.Event.EventNumber,
             GetContextStreamPosition(re),
             re.Event.Position.CommitPosition,
             re.OriginalEventNumber,
@@ -202,9 +203,9 @@ IMessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancell
     }
 
     /// <summary>
-    /// Get a stream position from the resolved event
+    /// Get stream position from the resolved event
     /// </summary>
-    /// <param name="re"></param>
+    /// <param name="re">Resolved event received from the database</param>
     /// <returns></returns>
     protected abstract ulong GetContextStreamPosition(ResolvedEvent re);
 
diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/StreamSubscription.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/StreamSubscription.cs
index 7c0f54bd5..f4ce14a47 100644
--- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/StreamSubscription.cs
+++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/StreamSubscription.cs
@@ -123,22 +123,25 @@ IMessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancell
             re.Event.EventNumber
         );
 
+        var meta = Options.MetadataSerializer.DeserializeMeta(
+            Options,
+            re.Event.Metadata,
+            re.Event.EventStreamId,
+            re.Event.EventNumber
+        );
+
         return new MessageConsumeContext(
             re.Event.EventId.ToString(),
             re.Event.EventType,
             re.Event.ContentType,
             re.Event.EventStreamId,
             re.Event.EventNumber,
+            re.OriginalEventNumber.ToUInt64(),
             re.Event.Position.CommitPosition,
             _sequence++,
             re.Event.Created,
             evt,
-            Options.MetadataSerializer.DeserializeMeta(
-                Options,
-                re.Event.Metadata,
-                re.Event.EventStreamId,
-                re.Event.EventNumber
-            ),
+            meta,
             SubscriptionId,
             cancellationToken
         );
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj
index 614fc7e71..81db5fb88 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj
@@ -1,10 +1,11 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
+        <IncludeTestHelpers>true</IncludeTestHelpers>
         <IncludeSutApp>true</IncludeSutApp>
         <IncludeSutSubs>true</IncludeSutSubs>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
+<!--        <VSTestLogger>trx%3bLogFileName=$(MSBuildProjectName).trx</VSTestLogger>-->
+<!--        <VSTestResultsDirectory>$(MSBuildThisFileDirectory)/TestResults/$(TargetFramework)</VSTestResultsDirectory>-->
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(DiagRoot)\Eventuous.Diagnostics.OpenTelemetry\Eventuous.Diagnostics.OpenTelemetry.csproj" />
@@ -15,7 +16,6 @@
     <ItemGroup>
         <PackageReference Include="NodaTime.Serialization.SystemTextJson" />
         <PackageReference Include="MicroElements.AutoFixture.NodaTime" />
-        <PackageReference Include="Testcontainers.EventStoreDb" />
     </ItemGroup>
     <ItemGroup>
         <Using Include="NodaTime" />
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/IntegrationFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/IntegrationFixture.cs
index 2a9defd0c..1b9d7157d 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/IntegrationFixture.cs
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/IntegrationFixture.cs
@@ -4,6 +4,7 @@
 using Eventuous.Diagnostics;
 using Eventuous.Diagnostics.Tracing;
 using Eventuous.EventStore;
+using Eventuous.TestHelpers;
 using MicroElements.AutoFixture.NodaTime;
 using NodaTime.Serialization.SystemTextJson;
 using Testcontainers.EventStoreDb;
@@ -31,9 +32,7 @@ public IntegrationFixture() {
     }
 
     public async Task InitializeAsync() {
-        _esdbContainer = new EventStoreDbBuilder()
-            .WithImage("eventstore/eventstore:22.10.2-alpha-arm64v8")
-            .Build();
+        _esdbContainer = new EventStoreDbContainerBuilder().Build();
         await _esdbContainer.StartAsync();
         var settings = EventStoreClientSettings.Create(_esdbContainer.GetConnectionString());
         Client         = new EventStoreClient(settings);
@@ -43,7 +42,12 @@ public async Task InitializeAsync() {
 
     public async Task DisposeAsync() {
         _listener.Dispose();
-        await Client.DisposeAsync();
-        await _esdbContainer.DisposeAsync();
+
+        try {
+            await Client.DisposeAsync();
+            await _esdbContainer.DisposeAsync();
+        } catch (Exception e) {
+            Console.WriteLine(e);
+        }
     }
 }
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/PersistentSubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/PersistentSubscriptionFixture.cs
index cd84b084a..840afd70f 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/PersistentSubscriptionFixture.cs
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/PersistentSubscriptionFixture.cs
@@ -6,7 +6,7 @@
 
 namespace Eventuous.Tests.EventStore.Fixtures;
 
-public abstract class PersistentSubscriptionFixture<T> : IAsyncLifetime where T : class, IEventHandler {
+public abstract class PersistentSubscriptionFixture<T> : IClassFixture<IntegrationFixture>, IAsyncLifetime where T : class, IEventHandler {
     static PersistentSubscriptionFixture()
         => TypeMap.Instance.RegisterKnownEventTypes(typeof(TestEvent).Assembly);
 
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/PersistentPublishAndSubscribeManyTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/PersistentPublishAndSubscribeManyTests.cs
index fae86f0ba..96f56be13 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/PersistentPublishAndSubscribeManyTests.cs
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/PersistentPublishAndSubscribeManyTests.cs
@@ -4,7 +4,7 @@
 
 namespace Eventuous.Tests.EventStore;
 
-public class PersistentPublishAndSubscribeManyTests : PersistentSubscriptionFixture<TestEventHandler>, IClassFixture<IntegrationFixture> {
+public class PersistentPublishAndSubscribeManyTests : PersistentSubscriptionFixture<TestEventHandler> {
     public PersistentPublishAndSubscribeManyTests(IntegrationFixture fixture, ITestOutputHelper outputHelper)
         : base(fixture, outputHelper, new TestEventHandler(), false) { }
 
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/StreamSubscriptionWithLinksTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/StreamSubscriptionWithLinksTests.cs
index 6956fb3f6..8be7db4be 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/StreamSubscriptionWithLinksTests.cs
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/StreamSubscriptionWithLinksTests.cs
@@ -53,6 +53,7 @@ public StreamSubscriptionWithLinksTests(IntegrationFixture fixture, ITestOutputH
     Faker Faker { get; } = new();
 
     async Task Seed(IServiceProvider provider, int count) {
+        TypeMap.Instance.AddType<TestEvent>(TestEvent.TypeName);
         var producer = provider.GetRequiredService<IEventProducer>();
 
         _output.WriteLine("Producing events...");
@@ -72,8 +73,10 @@ void AddCheckpointStore(ulong? start) {
         checkpointStore.CheckpointStored += CheckpointStoreOnCheckpointStored;
         _services.AddSingleton<ICheckpointStore>(checkpointStore);
 
+        return;
+
         void CheckpointStoreOnCheckpointStored(object? sender, Checkpoint e) {
-            _output.WriteLine($"Stored checkpoint: {e.Position}");
+            _output.WriteLine($"Stored checkpoint {e.Id}: {e.Position}");
             _checkpoints.Add(e);
         }
     }
@@ -148,12 +151,15 @@ await services.Select(
         _checkpoints.Last().Position.Should().Be(count - 1);
     }
 
-    class TestHandler : BaseEventHandler {
+    // ReSharper disable once ClassNeverInstantiated.Local
+    class TestHandler(ILogger<TestHandler> logger) : BaseEventHandler {
         public List<object> Handled { get; } = new();
 
         public override ValueTask<EventHandlingStatus> HandleEvent(IMessageConsumeContext ctx) {
             Handled.Add(ctx.Message!);
 
+            logger.LogDebug("Handled event from {Stream} at {Position}", ctx.Stream, ctx.StreamPosition);
+
             return ValueTask.FromResult(EventHandlingStatus.Success);
         }
     }
diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/SubscriptionIgnoredMessagesTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/SubscriptionIgnoredMessagesTests.cs
index 78752130d..11ea860b3 100644
--- a/src/EventStore/test/Eventuous.Tests.EventStore/SubscriptionIgnoredMessagesTests.cs
+++ b/src/EventStore/test/Eventuous.Tests.EventStore/SubscriptionIgnoredMessagesTests.cs
@@ -25,6 +25,7 @@ public async Task SubscribeAndProduceManyWithIgnored() {
 
         handler.AssertThat().Exactly(count, x => testEvents.Contains(x));
 
+        TypeMap.Instance.AddType<TestEvent>(TestEvent.TypeName);
         TypeMap.Instance.AddType<UnknownEvent>("ignored");
         await producer.Produce(stream, testEvents, new Metadata());
 
diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Eventuous.AspNetCore.Web.csproj b/src/Extensions/src/Eventuous.AspNetCore.Web/Eventuous.AspNetCore.Web.csproj
index 2c531f068..0169903a3 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Eventuous.AspNetCore.Web.csproj
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Eventuous.AspNetCore.Web.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(DiagRoot)\Eventuous.Diagnostics.Logging\Eventuous.Diagnostics.Logging.csproj"/>
         <ProjectReference Include="..\Eventuous.AspNetCore\Eventuous.AspNetCore.csproj"/>
diff --git a/src/Extensions/src/Eventuous.AspNetCore/Eventuous.AspNetCore.csproj b/src/Extensions/src/Eventuous.AspNetCore/Eventuous.AspNetCore.csproj
index c57df42b2..b651b27e7 100644
--- a/src/Extensions/src/Eventuous.AspNetCore/Eventuous.AspNetCore.csproj
+++ b/src/Extensions/src/Eventuous.AspNetCore/Eventuous.AspNetCore.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <PackageReadmeFile>README.md</PackageReadmeFile>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="..\Eventuous.Extensions.DependencyInjection\Eventuous.Extensions.DependencyInjection.csproj"/>
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj
index 1bf6399dc..448112999 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/Extensions/src/Eventuous.Subscriptions.Polly/Eventuous.Subscriptions.Polly.csproj b/src/Extensions/src/Eventuous.Subscriptions.Polly/Eventuous.Subscriptions.Polly.csproj
index af87870bf..ef7005a1e 100644
--- a/src/Extensions/src/Eventuous.Subscriptions.Polly/Eventuous.Subscriptions.Polly.csproj
+++ b/src/Extensions/src/Eventuous.Subscriptions.Polly/Eventuous.Subscriptions.Polly.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Subscriptions\Eventuous.Subscriptions.csproj"/>
     </ItemGroup>
diff --git a/src/Extensions/test/Eventuous.Tests.AspNetCore.Web/Eventuous.Tests.AspNetCore.Web.csproj b/src/Extensions/test/Eventuous.Tests.AspNetCore.Web/Eventuous.Tests.AspNetCore.Web.csproj
index 2a21c3419..99b98731f 100644
--- a/src/Extensions/test/Eventuous.Tests.AspNetCore.Web/Eventuous.Tests.AspNetCore.Web.csproj
+++ b/src/Extensions/test/Eventuous.Tests.AspNetCore.Web/Eventuous.Tests.AspNetCore.Web.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutApp>true</IncludeSutApp>
         <IncludeTestHelpers>true</IncludeTestHelpers>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.AspNetCore.Web\Eventuous.AspNetCore.Web.csproj" />
diff --git a/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj b/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj
index 4822bbea5..c729223a6 100644
--- a/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj
+++ b/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
         <RootNamespace>Eventuous.Tests.AspNetCore</RootNamespace>
     </PropertyGroup>
     
diff --git a/src/Gateway/src/Eventuous.Gateway/Eventuous.Gateway.csproj b/src/Gateway/src/Eventuous.Gateway/Eventuous.Gateway.csproj
index ae4360ca2..7167358a4 100644
--- a/src/Gateway/src/Eventuous.Gateway/Eventuous.Gateway.csproj
+++ b/src/Gateway/src/Eventuous.Gateway/Eventuous.Gateway.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Producers\Eventuous.Producers.csproj" />
         <ProjectReference Include="$(CoreRoot)\Eventuous.Subscriptions\Eventuous.Subscriptions.csproj" />
diff --git a/src/Gateway/test/Eventuous.Gateway.Tests/Eventuous.Gateway.Tests.csproj b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj
similarity index 69%
rename from src/Gateway/test/Eventuous.Gateway.Tests/Eventuous.Gateway.Tests.csproj
rename to src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj
index b69df074d..5042cac8d 100644
--- a/src/Gateway/test/Eventuous.Gateway.Tests/Eventuous.Gateway.Tests.csproj
+++ b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.Gateway\Eventuous.Gateway.csproj" />
diff --git a/src/Gateway/test/Eventuous.Gateway.Tests/RegistrationTests.cs b/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs
similarity index 96%
rename from src/Gateway/test/Eventuous.Gateway.Tests/RegistrationTests.cs
rename to src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs
index de2286470..172146013 100644
--- a/src/Gateway/test/Eventuous.Gateway.Tests/RegistrationTests.cs
+++ b/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs
@@ -1,4 +1,5 @@
-﻿using Eventuous.Producers;
+﻿using Eventuous.Gateway;
+using Eventuous.Producers;
 using Eventuous.Subscriptions;
 using Eventuous.Subscriptions.Context;
 using Eventuous.Subscriptions.Filters;
@@ -10,7 +11,7 @@
 
 // ReSharper disable ClassNeverInstantiated.Local
 
-namespace Eventuous.Gateway.Tests;
+namespace Eventuous.Tests.Gateway;
 
 public class RegistrationTests {
     readonly IServiceProvider _provider;
diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/CloudRunPubSubSubscription.cs b/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/CloudRunPubSubSubscription.cs
index 9bbd34023..526370a0b 100644
--- a/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/CloudRunPubSubSubscription.cs
+++ b/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/CloudRunPubSubSubscription.cs
@@ -73,6 +73,7 @@ public static void MapSubscription(WebApplication app, string path = "/") {
                     subscription.Options.TopicId,
                     0,
                     0,
+                    0,
                     sequence++,
                     envelope.Message.PublishTime,
                     message,
diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/Eventuous.GooglePubSub.CloudRun.csproj b/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/Eventuous.GooglePubSub.CloudRun.csproj
index b4c10045d..69229171c 100644
--- a/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/Eventuous.GooglePubSub.CloudRun.csproj
+++ b/src/GooglePubSub/src/Eventuous.GooglePubSub.CloudRun/Eventuous.GooglePubSub.CloudRun.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <FrameworkReference Include="Microsoft.AspNetCore.App"/>
     </ItemGroup>
diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub/Eventuous.GooglePubSub.csproj b/src/GooglePubSub/src/Eventuous.GooglePubSub/Eventuous.GooglePubSub.csproj
index 55b82c9a9..d03e6d7da 100644
--- a/src/GooglePubSub/src/Eventuous.GooglePubSub/Eventuous.GooglePubSub.csproj
+++ b/src/GooglePubSub/src/Eventuous.GooglePubSub/Eventuous.GooglePubSub.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub/Subscriptions/GooglePubSubSubscription.cs b/src/GooglePubSub/src/Eventuous.GooglePubSub/Subscriptions/GooglePubSubSubscription.cs
index 6068320e5..8136e7b80 100644
--- a/src/GooglePubSub/src/Eventuous.GooglePubSub/Subscriptions/GooglePubSubSubscription.cs
+++ b/src/GooglePubSub/src/Eventuous.GooglePubSub/Subscriptions/GooglePubSubSubscription.cs
@@ -107,6 +107,7 @@ async Task<Reply> Handle(PubsubMessage msg, CancellationToken ct) {
                 0,
                 0,
                 0,
+                0,
                 msg.PublishTime.ToDateTime(),
                 evt,
                 AsMeta(msg.Attributes),
diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj
index d46fd9481..6594edca6 100644
--- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj
+++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutSubs>true</IncludeSutSubs>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.GooglePubSub\Eventuous.GooglePubSub.csproj" />
diff --git a/src/Kafka/src/Eventuous.Kafka/Eventuous.Kafka.csproj b/src/Kafka/src/Eventuous.Kafka/Eventuous.Kafka.csproj
index c4f5c5047..33eb8fe9f 100644
--- a/src/Kafka/src/Eventuous.Kafka/Eventuous.Kafka.csproj
+++ b/src/Kafka/src/Eventuous.Kafka/Eventuous.Kafka.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj b/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj
index ab854ae94..a8d1f506d 100644
--- a/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj
+++ b/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj
@@ -1,10 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IsPackable>false</IsPackable>
         <IncludeSutSubs>true</IncludeSutSubs>
         <RuntimeIdentifier Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx.13-arm64</RuntimeIdentifier>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
+        <SelfContained>false</SelfContained>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.Kafka\Eventuous.Kafka.csproj"/>
diff --git a/src/Mongo/src/Eventuous.Projections.MongoDB/Eventuous.Projections.MongoDB.csproj b/src/Mongo/src/Eventuous.Projections.MongoDB/Eventuous.Projections.MongoDB.csproj
index e0e27fc7c..7758f0aa4 100644
--- a/src/Mongo/src/Eventuous.Projections.MongoDB/Eventuous.Projections.MongoDB.csproj
+++ b/src/Mongo/src/Eventuous.Projections.MongoDB/Eventuous.Projections.MongoDB.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj
index d4493ac67..73fe95152 100644
--- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj
+++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj
@@ -1,9 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeTestHost>true</IncludeTestHost>
+        <IncludeTestHelpers>true</IncludeTestHelpers>
         <IncludeSutApp>true</IncludeSutApp>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(EsdbRoot)\Eventuous.EventStore\Eventuous.EventStore.csproj" />
@@ -13,7 +12,6 @@
     <ItemGroup>
         <PackageReference Include="NodaTime.Serialization.SystemTextJson" />
         <PackageReference Include="MongoDb.Bson.NodaTime" />
-        <PackageReference Include="Testcontainers.EventStoreDb" />
         <PackageReference Include="Testcontainers.MongoDb" />
     </ItemGroup>
 </Project>
diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs
index 9bc96e8ba..52e0e71d4 100644
--- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs
+++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs
@@ -1,6 +1,7 @@
 using System.Text.Json;
 using EventStore.Client;
 using Eventuous.EventStore;
+using Eventuous.TestHelpers;
 using MongoDb.Bson.NodaTime;
 using MongoDB.Driver;
 using NodaTime;
@@ -39,9 +40,7 @@ static IntegrationFixture() {
     MongoDbContainer      _mongoContainer = null!;
 
     public async Task InitializeAsync() {
-        _esdbContainer = new EventStoreDbBuilder()
-            .WithImage("eventstore/eventstore:22.10.2-alpha-arm64v8")
-            .Build();
+        _esdbContainer = new EventStoreDbContainerBuilder().Build();
         await _esdbContainer.StartAsync();
         var settings = EventStoreClientSettings.Create(_esdbContainer.GetConnectionString());
         Client         = new EventStoreClient(settings);
diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs
index 36e95158b..543a3c772 100644
--- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs
+++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs
@@ -29,7 +29,7 @@ public async Task ShouldProjectImported() {
 
         first.Doc.Should().Be(expected);
 
-        var payment = new BookingPaymentRegistered(fixture.Auto.Create<string>(), evt.Price);
+        var payment = new BookingPaymentRegistered(Fixture.Auto.Create<string>(), evt.Price);
 
         var second = await Act(stream, payment);
 
@@ -44,11 +44,11 @@ public async Task ShouldProjectImported() {
 
     async Task<(AppendEventsResult Append, BookingDocument? Doc)> Act<T>(StreamName stream, T evt)
         where T : class {
-        var append = await fixture.AppendEvent(stream, evt);
+        var append = await Fixture.AppendEvent(stream, evt);
 
         await Task.Delay(500);
 
-        var actual = await fixture.Mongo.LoadDocument<BookingDocument>(stream.GetId());
+        var actual = await Fixture.Mongo.LoadDocument<BookingDocument>(stream.GetId());
 
         return (append, actual);
     }
diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs
index 9d075dec2..ebc1adcde 100644
--- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs
+++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs
@@ -15,7 +15,7 @@ public async Task ShouldProjectImported() {
         var id     = new BookingId(CreateId());
         var stream = StreamNameFactory.For<Booking, BookingState, BookingId>(id);
 
-        var append = await fixture.AppendEvent(stream, evt);
+        var append = await Fixture.AppendEvent(stream, evt);
 
         await Task.Delay(500);
 
@@ -29,7 +29,7 @@ public async Task ShouldProjectImported() {
             StreamPosition = (ulong)append.NextExpectedVersion
         };
 
-        var actual = await fixture.Mongo.LoadDocument<BookingDocument>(id.ToString());
+        var actual = await Fixture.Mongo.LoadDocument<BookingDocument>(id.ToString());
         actual.Should().Be(expected);
     }
 
diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs
index 69cf0ec19..e26a10ecb 100644
--- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs
+++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs
@@ -9,10 +9,10 @@ namespace Eventuous.Tests.Projections.MongoDB;
 
 public class ProjectionTestBase<TProjection> : IClassFixture<IntegrationFixture>, IAsyncLifetime where TProjection : class, IEventHandler {
     readonly IHost              _host;
-    readonly IntegrationFixture _fixture;
+    protected IntegrationFixture Fixture;
 
     protected ProjectionTestBase(string id, IntegrationFixture fixture, ITestOutputHelper output) {
-        _fixture = fixture;
+        Fixture = fixture;
 
         var builder = Host.CreateDefaultBuilder()
             .ConfigureLogging(cfg => cfg.AddXunit(output, LogLevel.Debug).SetMinimumLevel(LogLevel.Trace))
@@ -24,8 +24,8 @@ protected ProjectionTestBase(string id, IntegrationFixture fixture, ITestOutputH
 
     void ConfigureServices(IServiceCollection services, string id)
         => services
-            .AddSingleton(_fixture.Client)
-            .AddSingleton(_fixture.Mongo)
+            .AddSingleton(Fixture.Client)
+            .AddSingleton(Fixture.Mongo)
             .AddCheckpointStore<MongoCheckpointStore>()
             .AddSubscription<AllStreamSubscription, AllStreamSubscriptionOptions>(
                 id,
diff --git a/src/Postgres/src/Eventuous.Postgresql/Eventuous.Postgresql.csproj b/src/Postgres/src/Eventuous.Postgresql/Eventuous.Postgresql.csproj
index dad1b2649..d420d7b70 100644
--- a/src/Postgres/src/Eventuous.Postgresql/Eventuous.Postgresql.csproj
+++ b/src/Postgres/src/Eventuous.Postgresql/Eventuous.Postgresql.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Subscriptions\Eventuous.Subscriptions.csproj"/>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Persistence\Eventuous.Persistence.csproj"/>
diff --git a/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresAllStreamSubscription.cs b/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresAllStreamSubscription.cs
index 741446361..e1c727a80 100644
--- a/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresAllStreamSubscription.cs
+++ b/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresAllStreamSubscription.cs
@@ -39,8 +39,9 @@ protected override IMessageConsumeContext AsContext(PersistedEvent evt, object?
             evt.MessageType,
             ContentType,
             Ensure.NotEmptyString(evt.StreamName),
-            (ulong)evt.GlobalPosition - 1,
-            (ulong)evt.GlobalPosition - 1,
+            (ulong)evt.StreamPosition,
+            (ulong)evt.StreamPosition,
+            (ulong)evt.GlobalPosition,
             _sequence++,
             evt.Created,
             e,
diff --git a/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresStreamSubscription.cs b/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresStreamSubscription.cs
index af8f3a8f5..817710deb 100644
--- a/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresStreamSubscription.cs
+++ b/src/Postgres/src/Eventuous.Postgresql/Subscriptions/PostgresStreamSubscription.cs
@@ -52,6 +52,7 @@ protected override IMessageConsumeContext AsContext(PersistedEvent evt, object?
             ContentType,
             _streamName,
             (ulong)evt.StreamPosition,
+            (ulong)evt.StreamPosition,
             (ulong)evt.GlobalPosition,
             _sequence++,
             evt.Created,
diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj
index bdf871777..3abc157c6 100644
--- a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj
+++ b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj
@@ -1,10 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutSubs>true</IncludeSutSubs>
         <IncludeSutApp>true</IncludeSutApp>
         <IncludeTestHost>true</IncludeTestHost>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="MicroElements.AutoFixture.NodaTime" />
diff --git a/src/RabbitMq/src/Eventuous.RabbitMq/Eventuous.RabbitMq.csproj b/src/RabbitMq/src/Eventuous.RabbitMq/Eventuous.RabbitMq.csproj
index d24b2e4cb..c5d16654b 100644
--- a/src/RabbitMq/src/Eventuous.RabbitMq/Eventuous.RabbitMq.csproj
+++ b/src/RabbitMq/src/Eventuous.RabbitMq/Eventuous.RabbitMq.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
     <ItemGroup>
diff --git a/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs b/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs
index 8cc82702a..2673bf5a9 100644
--- a/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs
+++ b/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs
@@ -171,6 +171,7 @@ IMessageConsumeContext CreateContext(object sender, BasicDeliverEventArgs receiv
             received.Exchange,
             0,
             0,
+            0,
             received.DeliveryTag,
             received.BasicProperties.Timestamp.ToDateTime(),
             evt,
diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj
index c871b1f38..a36d9d4ec 100644
--- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj
+++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutSubs>true</IncludeSutSubs>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(LocalRoot)\Eventuous.RabbitMq\Eventuous.RabbitMq.csproj" />
diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs
index ff6b1f3f2..ddb9fc383 100644
--- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs
+++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs
@@ -13,9 +13,9 @@ public class SubscriptionSpec : IAsyncLifetime, IClassFixture<RabbitMqFixture> {
 
     static readonly Fixture Auto = new();
 
-    RabbitMqSubscription               _subscription;
-    RabbitMqProducer                   _producer;
-    TestEventHandler                   _handler;
+    RabbitMqSubscription               _subscription = null!;
+    RabbitMqProducer                   _producer     = null!;
+    TestEventHandler                   _handler      = null!;
     readonly StreamName                _exchange;
     readonly ILogger<SubscriptionSpec> _log;
     readonly TestEventListener         _es;
@@ -23,9 +23,8 @@ public class SubscriptionSpec : IAsyncLifetime, IClassFixture<RabbitMqFixture> {
     readonly RabbitMqFixture           _fixture;
 
     public SubscriptionSpec(RabbitMqFixture fixture, ITestOutputHelper outputHelper) {
-        _fixture = fixture;
-        _es      = new TestEventListener(outputHelper);
-
+        _fixture  = fixture;
+        _es       = new TestEventListener(outputHelper);
         _exchange = new StreamName(Auto.Create<string>());
 
         _loggerFactory = LoggerFactory.Create(
diff --git a/src/Redis/src/Eventuous.Redis/Subscriptions/RedisSubscriptionBase.cs b/src/Redis/src/Eventuous.Redis/Subscriptions/RedisSubscriptionBase.cs
index da947a7b7..5e43bc883 100644
--- a/src/Redis/src/Eventuous.Redis/Subscriptions/RedisSubscriptionBase.cs
+++ b/src/Redis/src/Eventuous.Redis/Subscriptions/RedisSubscriptionBase.cs
@@ -95,6 +95,7 @@ IMessageConsumeContext AsContext(ReceivedEvent evt, object? e, Metadata? meta, C
             ContentType,
             evt.StreamName,
             (ulong)evt.StreamPosition,
+            (ulong)evt.StreamPosition,
             (ulong)evt.GlobalPosition,
             _sequence++,
             evt.Created,
diff --git a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj
index 3bd2182d7..d96a60de4 100644
--- a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj
+++ b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutSubs>true</IncludeSutSubs>
         <IncludeSutApp>true</IncludeSutApp>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
         <ItemGroup>
             <PackageReference Include="MicroElements.AutoFixture.NodaTime" />
diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs
index b89a93347..a2fea1ab9 100644
--- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs
+++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs
@@ -10,13 +10,13 @@
 namespace Eventuous.Tests.Redis.Fixtures;
 
 public sealed class IntegrationFixture : IAsyncLifetime {
-    public IEventWriter     EventWriter    { get; set; }
-    public IEventReader     EventReader    { get; set; }
-    public IAggregateStore  AggregateStore { get; set; }
-    public GetRedisDatabase GetDatabase    { get; set; }
+    public IEventWriter     EventWriter    { get; private set; } = null!;
+    public IEventReader     EventReader    { get; private set; } = null!;
+    public IAggregateStore  AggregateStore { get; private set; } = null!;
+    public GetRedisDatabase GetDatabase    { get; private set; } = null!;
 
-    readonly ActivityListener _listener = DummyActivityListener.Create();
-    RedisContainer            _redisContainer;
+    readonly ActivityListener _listener       = DummyActivityListener.Create();
+    RedisContainer            _redisContainer = null!;
 
     IEventSerializer Serializer { get; } = new DefaultEventSerializer(
         new JsonSerializerOptions(JsonSerializerDefaults.Web)
@@ -25,8 +25,6 @@ public sealed class IntegrationFixture : IAsyncLifetime {
 
     public IntegrationFixture() {
         DefaultEventSerializer.SetDefaultSerializer(Serializer);
-
-        return;
     }
 
     public async Task InitializeAsync() {
diff --git a/src/SqlServer/src/Eventuous.SqlServer/Eventuous.SqlServer.csproj b/src/SqlServer/src/Eventuous.SqlServer/Eventuous.SqlServer.csproj
index aec43773c..5c7deb193 100644
--- a/src/SqlServer/src/Eventuous.SqlServer/Eventuous.SqlServer.csproj
+++ b/src/SqlServer/src/Eventuous.SqlServer/Eventuous.SqlServer.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    </PropertyGroup>
     <ItemGroup>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Subscriptions\Eventuous.Subscriptions.csproj"/>
         <ProjectReference Include="$(CoreRoot)\Eventuous.Persistence\Eventuous.Persistence.csproj"/>
diff --git a/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerAllStreamSubscription.cs b/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerAllStreamSubscription.cs
index 4ac0ab646..c0c2d6a82 100644
--- a/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerAllStreamSubscription.cs
+++ b/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerAllStreamSubscription.cs
@@ -16,12 +16,13 @@ namespace Eventuous.SqlServer.Subscriptions;
 /// </summary>
 public class SqlServerAllStreamSubscription : SqlServerSubscriptionBase<SqlServerAllStreamSubscriptionOptions> {
     public SqlServerAllStreamSubscription(
-        GetSqlServerConnection                getConnection,
-        SqlServerAllStreamSubscriptionOptions options,
-        ICheckpointStore                      checkpointStore,
-        ConsumePipe                           consumePipe,
-        ILoggerFactory?                       loggerFactory = null
-    ) : base(getConnection, options, checkpointStore, consumePipe, loggerFactory) { }
+            GetSqlServerConnection                getConnection,
+            SqlServerAllStreamSubscriptionOptions options,
+            ICheckpointStore                      checkpointStore,
+            ConsumePipe                           consumePipe,
+            ILoggerFactory?                       loggerFactory = null
+        )
+        : base(getConnection, options, checkpointStore, consumePipe, loggerFactory) { }
 
     protected override SqlCommand PrepareCommand(SqlConnection connection, long start)
         => connection.GetStoredProcCommand(Schema.ReadAllForwards)
@@ -34,18 +35,19 @@ protected override long MoveStart(PersistedEvent evt)
     ulong _sequence;
 
     protected override IMessageConsumeContext AsContext(
-        PersistedEvent    evt,
-        object?           e,
-        Metadata?         meta,
-        CancellationToken cancellationToken
-    )
+            PersistedEvent    evt,
+            object?           e,
+            Metadata?         meta,
+            CancellationToken cancellationToken
+        )
         => new MessageConsumeContext(
             evt.MessageId.ToString(),
             evt.MessageType,
             ContentType,
             Ensure.NotEmptyString(evt.StreamName),
-            (ulong)evt.GlobalPosition - 1,
-            (ulong)evt.GlobalPosition - 1,
+            (ulong)evt.StreamPosition,
+            (ulong)evt.StreamPosition,
+            (ulong)evt.GlobalPosition,
             _sequence++,
             evt.Created,
             e,
diff --git a/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerStreamSubscription.cs b/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerStreamSubscription.cs
index 844c1b090..2a38a8bd1 100644
--- a/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerStreamSubscription.cs
+++ b/src/SqlServer/src/Eventuous.SqlServer/Subscriptions/SqlServerStreamSubscription.cs
@@ -59,6 +59,7 @@ protected override IMessageConsumeContext AsContext(PersistedEvent evt, object?
             ContentType,
             _streamName,
             (ulong)evt.StreamPosition,
+            (ulong)evt.StreamPosition,
             (ulong)evt.GlobalPosition,
             _sequence++,
             evt.Created,
diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj
index 2802f2853..3d8e54990 100644
--- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj
+++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <IsTestProject>true</IsTestProject>
         <IncludeSutSubs>true</IncludeSutSubs>
         <IncludeSutApp>true</IncludeSutApp>
-        <GenerateDocumentationFile>false</GenerateDocumentationFile>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="NodaTime.Serialization.SystemTextJson" />
diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeToAll.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeToAll.cs
index 18ee4c5bf..969dac901 100644
--- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeToAll.cs
+++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeToAll.cs
@@ -12,7 +12,7 @@ namespace Eventuous.Tests.SqlServer.Subscriptions;
 
 public class SubscribeToAll : SubscriptionFixture<TestEventHandler> {
     readonly BookingService _service;
-    List<ImportBooking>     _commands;
+    List<ImportBooking>     _commands = null!;
 
     public SubscribeToAll(IntegrationFixture fixture, ITestOutputHelper outputHelper)
         : base(fixture, outputHelper, new TestEventHandler(), true, false) {
@@ -20,12 +20,13 @@ public SubscribeToAll(IntegrationFixture fixture, ITestOutputHelper outputHelper
         var store      = new AggregateStore(eventStore);
         _service = new BookingService(store);
     }
-        const int count = 10;
+
+    const int Count = 10;
 
     [Fact]
     public async Task ShouldConsumeProducedEvents() {
         var testEvents = _commands.Select(ToEvent).ToList();
-        Handler.AssertThat().Exactly(count, x => testEvents.Contains(x));
+        Handler.AssertThat().Exactly(Count, x => testEvents.Contains(x));
 
         await Start();
         await Handler.Validate(2.Seconds());
@@ -49,7 +50,7 @@ public async Task ShouldUseExistingCheckpoint() {
     public override async Task InitializeAsync() {
         await base.InitializeAsync();
 
-        _commands = await GenerateAndHandleCommands(count);
+        _commands = await GenerateAndHandleCommands(Count);
     }
 
     static BookingImported ToEvent(ImportBooking cmd)
diff --git a/test/Eventuous.Sut.Subs/TestEventHandler.cs b/test/Eventuous.Sut.Subs/TestEventHandler.cs
index f1ca32f85..509fa9a9b 100644
--- a/test/Eventuous.Sut.Subs/TestEventHandler.cs
+++ b/test/Eventuous.Sut.Subs/TestEventHandler.cs
@@ -11,31 +11,27 @@ public record TestEvent(string Data, int Number) {
     public const string TypeName = "test-event";
 }
 
-public class TestEventHandler : BaseEventHandler {
-    readonly ITestOutputHelper? _output;
-    readonly TimeSpan           _delay;
-    
+public class TestEventHandler(TimeSpan? delay = null, ITestOutputHelper? output = null) : BaseEventHandler {
+    readonly TimeSpan _delay = delay ?? TimeSpan.Zero;
+
     public int Count { get; private set; }
 
     IHypothesis<object>? _hypothesis;
 
-    public TestEventHandler(TimeSpan? delay = null, ITestOutputHelper? output = null) {
-        _output = output;
-        _delay  = delay ?? TimeSpan.Zero;
-    }
-
     public IHypothesis<object> AssertThat() {
         _hypothesis = Hypothesis.For<object>();
+
         return _hypothesis;
     }
 
     public Task Validate(TimeSpan timeout) => EnsureHypothesis.Validate(timeout);
 
     public override async ValueTask<EventHandlingStatus> HandleEvent(IMessageConsumeContext context) {
-        _output?.WriteLine(context.Message!.ToString());
+        output?.WriteLine(context.Message!.ToString());
         await Task.Delay(_delay);
         await EnsureHypothesis.Test(context.Message!, context.CancellationToken);
         Count++;
+
         return EventHandlingStatus.Success;
     }
 
@@ -43,4 +39,4 @@ public override async ValueTask<EventHandlingStatus> HandleEvent(IMessageConsume
 
     IHypothesis<object> EnsureHypothesis =>
         _hypothesis ?? throw new InvalidOperationException("Test handler not specified");
-}
\ No newline at end of file
+}
diff --git a/test/Eventuous.TestHelpers/EventStoreDbContainerBuilder.cs b/test/Eventuous.TestHelpers/EventStoreDbContainerBuilder.cs
new file mode 100644
index 000000000..65ec194d4
--- /dev/null
+++ b/test/Eventuous.TestHelpers/EventStoreDbContainerBuilder.cs
@@ -0,0 +1,73 @@
+// Copyright (C) Ubiquitous AS.All rights reserved
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.InteropServices;
+using Docker.DotNet.Models;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Configurations;
+using Testcontainers.EventStoreDb;
+
+namespace Eventuous.TestHelpers;
+
+public sealed class EventStoreDbContainerBuilder : ContainerBuilder<EventStoreDbContainerBuilder, EventStoreDbContainer, EventStoreDbConfiguration> {
+    public static readonly string ContainerTag = RuntimeInformation.ProcessArchitecture switch {
+        Architecture.Arm64 => "22.10.2-alpha-arm64v8",
+        _                  => "22.10.2-buster-slim"
+    };
+
+    public static readonly string EventStoreDbImage = $"eventstore/eventstore:{ContainerTag}";
+
+    public const ushort EventStoreDbPort = 2113;
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="EventStoreDbBuilder" /> class.
+    /// </summary>
+    public EventStoreDbContainerBuilder()
+        : this(new EventStoreDbConfiguration()) {
+        DockerResourceConfiguration = Init().DockerResourceConfiguration;
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="EventStoreDbBuilder" /> class.
+    /// </summary>
+    /// <param name="dockerResourceConfiguration">The Docker resource configuration.</param>
+    private EventStoreDbContainerBuilder(EventStoreDbConfiguration dockerResourceConfiguration)
+        : base(dockerResourceConfiguration) {
+        DockerResourceConfiguration = dockerResourceConfiguration;
+    }
+
+    /// <inheritdoc />
+    protected override EventStoreDbConfiguration DockerResourceConfiguration { get; }
+
+    /// <inheritdoc />
+    public override EventStoreDbContainer Build() {
+        return new EventStoreDbContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
+    }
+
+    /// <inheritdoc />
+    protected override EventStoreDbContainerBuilder Init() {
+        return base.Init()
+            .WithImage(EventStoreDbImage)
+            .WithPortBinding(EventStoreDbPort, true)
+            .WithEnvironment("EVENTSTORE_CLUSTER_SIZE", "1")
+            .WithEnvironment("EVENTSTORE_RUN_PROJECTIONS", "All")
+            .WithEnvironment("EVENTSTORE_START_STANDARD_PROJECTIONS", "true")
+            .WithEnvironment("EVENTSTORE_INSECURE", "true")
+            .WithWaitStrategy(Wait.ForUnixContainer().UntilContainerIsHealthy());
+    }
+
+    /// <inheritdoc />
+    protected override EventStoreDbContainerBuilder Clone(IContainerConfiguration resourceConfiguration) {
+        return Merge(DockerResourceConfiguration, new EventStoreDbConfiguration(resourceConfiguration));
+    }
+
+    /// <inheritdoc />
+    protected override EventStoreDbContainerBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) {
+        return Merge(DockerResourceConfiguration, new EventStoreDbConfiguration(resourceConfiguration));
+    }
+
+    /// <inheritdoc />
+    protected override EventStoreDbContainerBuilder Merge(EventStoreDbConfiguration oldValue, EventStoreDbConfiguration newValue) {
+        return new EventStoreDbContainerBuilder(new EventStoreDbConfiguration(oldValue, newValue));
+    }
+}
diff --git a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj
index 70779638d..fb1393775 100644
--- a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj
+++ b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj
@@ -5,6 +5,7 @@
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.Logging" />
+        <PackageReference Include="Testcontainers.EventStoreDb" />
         <PackageReference Include="xunit.abstractions" />
         <PackageReference Include="Xunit.Extensions.Logging" />
     </ItemGroup>
