diff --git a/PowerKit.Tests/MemoryReadStreamTests.cs b/PowerKit.Tests/MemoryReadStreamTests.cs
new file mode 100644
index 0000000..de6cc4b
--- /dev/null
+++ b/PowerKit.Tests/MemoryReadStreamTests.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using FluentAssertions;
+using Xunit;
+
+namespace PowerKit.Tests;
+
+public class MemoryReadStreamTests
+{
+ [Fact]
+ public void MemoryReadStream_MakesUnseekableStreamSeekable_Test()
+ {
+ // Arrange — wrap a MemoryStream in a non-seekable facade
+ var data = new byte[] { 1, 2, 3, 4, 5 };
+ using var source = new NonSeekableStream(new MemoryStream(data));
+ source.CanSeek.Should().BeFalse();
+
+ // Act
+ using var result = new MemoryReadStream(source);
+
+ // Assert — wrapper is always seekable and can re-read from the beginning
+ result.CanSeek.Should().BeTrue();
+
+ var partial = new byte[2];
+ result.ReadExactly(partial);
+
+ result.Seek(0, SeekOrigin.Begin);
+
+ var full = new byte[data.Length];
+ result.ReadExactly(full);
+ full.Should().Equal(data);
+ }
+}
diff --git a/PowerKit.Tests/MemoryWriteStreamTests.cs b/PowerKit.Tests/MemoryWriteStreamTests.cs
new file mode 100644
index 0000000..f866180
--- /dev/null
+++ b/PowerKit.Tests/MemoryWriteStreamTests.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using FluentAssertions;
+using Xunit;
+
+namespace PowerKit.Tests;
+
+public class MemoryWriteStreamTests
+{
+ [Fact]
+ public void MemoryWriteStream_MakesUnseekableStreamSeekable_Test()
+ {
+ // Arrange — wrap a MemoryStream in a non-seekable facade
+ var data = new byte[] { 1, 2, 3, 4, 5 };
+ var inner = new MemoryStream();
+ using var source = new NonSeekableStream(inner);
+ source.CanSeek.Should().BeFalse();
+
+ // Act — wrapper buffers writes in memory; Flush() commits them to the source
+ using var wrapper = new MemoryWriteStream(source);
+ wrapper.CanSeek.Should().BeTrue();
+
+ // Write all bytes then seek back and overwrite the first two
+ wrapper.Write(data, 0, data.Length);
+ wrapper.Seek(0, SeekOrigin.Begin);
+ wrapper.Write(new byte[] { 10, 20 }, 0, 2);
+
+ wrapper.Flush();
+
+ // Assert — overwritten prefix is reflected in the underlying stream
+ inner.ToArray().Should().Equal(new byte[] { 10, 20, 3, 4, 5 });
+ }
+}
diff --git a/PowerKit.Tests/NonSeekableStream.cs b/PowerKit.Tests/NonSeekableStream.cs
new file mode 100644
index 0000000..78f9d12
--- /dev/null
+++ b/PowerKit.Tests/NonSeekableStream.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+
+namespace PowerKit.Tests;
+
+internal sealed class NonSeekableStream(Stream inner) : Stream
+{
+ public override bool CanRead => inner.CanRead;
+ public override bool CanSeek => false;
+ public override bool CanWrite => inner.CanWrite;
+ public override long Length => throw new NotSupportedException();
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ public override void Flush() => inner.Flush();
+
+ public override int Read(byte[] buffer, int offset, int count) =>
+ inner.Read(buffer, offset, count);
+
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
+
+ public override void SetLength(long value) => throw new NotSupportedException();
+
+ public override void Write(byte[] buffer, int offset, int count) =>
+ inner.Write(buffer, offset, count);
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ inner.Dispose();
+ base.Dispose(disposing);
+ }
+}
diff --git a/PowerKit/MemoryReadStream.cs b/PowerKit/MemoryReadStream.cs
new file mode 100644
index 0000000..f2ebbde
--- /dev/null
+++ b/PowerKit/MemoryReadStream.cs
@@ -0,0 +1,74 @@
+using System.IO;
+
+namespace PowerKit;
+
+///
+/// A wrapper that lazily loads a readable stream into an in-memory buffer,
+/// making it fully seekable regardless of whether the underlying stream supports seeking.
+///
+///
+///
+/// The underlying stream is read into memory on the first access to
+/// , , , or .
+/// Subsequent operations work directly against the in-memory buffer.
+///
+///
+/// Content is buffered from the source stream's current position when the buffer is first
+/// initialized. The buffer position always starts at 0.
+///
+///
+public sealed class MemoryReadStream(Stream source) : Stream
+{
+ private MemoryStream? _buffer;
+
+ private MemoryStream EnsureBuffer()
+ {
+ if (_buffer is not null)
+ return _buffer;
+
+ var capacity = source.CanSeek ? (int)(source.Length - source.Position) : 0;
+ _buffer = new MemoryStream(capacity);
+ source.CopyTo(_buffer);
+ _buffer.Position = 0;
+
+ return _buffer;
+ }
+
+ ///
+ public override bool CanRead => true;
+
+ ///
+ public override bool CanSeek => true;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => EnsureBuffer().Length;
+
+ ///
+ public override long Position
+ {
+ get => EnsureBuffer().Position;
+ set => EnsureBuffer().Position = value;
+ }
+
+ ///
+ public override void Flush() => _buffer?.Flush();
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count) =>
+ EnsureBuffer().Read(buffer, offset, count);
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin) =>
+ EnsureBuffer().Seek(offset, origin);
+
+ ///
+ public override void SetLength(long value) =>
+ throw new System.NotSupportedException("Stream does not support writing.");
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count) =>
+ throw new System.NotSupportedException("Stream does not support writing.");
+}
diff --git a/PowerKit/MemoryWriteStream.cs b/PowerKit/MemoryWriteStream.cs
new file mode 100644
index 0000000..134991d
--- /dev/null
+++ b/PowerKit/MemoryWriteStream.cs
@@ -0,0 +1,64 @@
+using System.IO;
+
+namespace PowerKit;
+
+///
+/// A wrapper that buffers all writes in memory and flushes them to the
+/// underlying stream on .
+///
+///
+///
+/// Writes go to an in-memory buffer and do not touch the underlying stream until
+/// is called. This makes the wrapper always seekable and allows writes to
+/// be reordered freely before the final flush.
+///
+///
+/// On , the entire in-memory buffer is written to the underlying stream
+/// starting at its current position.
+///
+///
+public sealed class MemoryWriteStream(Stream source) : Stream
+{
+ private readonly MemoryStream _buffer = new();
+
+ ///
+ public override bool CanRead => false;
+
+ ///
+ public override bool CanSeek => true;
+
+ ///
+ public override bool CanWrite => true;
+
+ ///
+ public override long Length => _buffer.Length;
+
+ ///
+ public override long Position
+ {
+ get => _buffer.Position;
+ set => _buffer.Position = value;
+ }
+
+ ///
+ public override void Flush()
+ {
+ _buffer.Position = 0;
+ _buffer.CopyTo(source);
+ source.Flush();
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count) =>
+ throw new System.NotSupportedException("Stream does not support reading.");
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin) => _buffer.Seek(offset, origin);
+
+ ///
+ public override void SetLength(long value) => _buffer.SetLength(value);
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count) =>
+ _buffer.Write(buffer, offset, count);
+}