public sealed class ContentHasher
{
    public async Task<string> ComputeSha256Async(Stream stream, CancellationToken cancellationToken)
    {
        if (!stream.CanSeek)
        {
            throw new NotSupportedException("Stream must be seekable for content hashing.");
        }

        var originalPosition = stream.Position;
        try
        {
            stream.Position = 0;
            using var sha = SHA256.Create();
            var hash = await sha.ComputeHashAsync(stream, cancellationToken).ConfigureAwait(false);
            return Convert.ToHexString(hash);
        }
        finally
        {
            stream.Position = originalPosition;
        }
    }
}