update runtime with file asset cdn information

adding core definition bits for out of band assets
also adding a "loadInBandAssets" flag

- [x] get a test in

Diffs=
af873d55a update runtime with file asset cdn information (#6040)

Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
diff --git a/.rive_head b/.rive_head
index 21826a4..8c4cf81 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-382a48cf8c79664cc8d5308f2f0a4a504d33b2be
+af873d55a4c80c7ba5982186eed4ceb0a0fe579f
diff --git a/dev/defs/assets/file_asset.json b/dev/defs/assets/file_asset.json
index 5db878a..3173b81 100644
--- a/dev/defs/assets/file_asset.json
+++ b/dev/defs/assets/file_asset.json
@@ -25,6 +25,36 @@
       },
       "description": "Size of the asset in bytes",
       "runtime": false
+    },
+    "exportTypeValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 358,
+        "string": "exporttypevalue"
+      },
+      "description": "How to export the asset: embedded, referenced or cdn",
+      "runtime": false
+    },
+    "cdnUuid": {
+      "type": "Bytes",
+      "encoded": true,
+      "key": {
+        "int": 359,
+        "string": "cdnuuid"
+      },
+      "description": "The cdn uuid if it exists",
+      "coop": false
+    },
+    "cdnBaseUrl": {
+      "type": "String",
+      "initialValue": "'https://public.rive.app/cdn/uuid'",
+      "key": {
+        "int": 362,
+        "string": "cdnbaseurl"
+      },
+      "description": "Set the base url of our cdn.",
+      "coop": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/event.json b/dev/defs/event.json
index 3cc5000..99db764 100644
--- a/dev/defs/event.json
+++ b/dev/defs/event.json
@@ -6,6 +6,24 @@
   },
   "extends": "container_component.json",
   "properties": {
+    "x": {
+      "type": "double",
+      "initialValue": "0",
+      "key": {
+        "int": 396,
+        "string": "x"
+      },
+      "runtime": false
+    },
+    "y": {
+      "type": "double",
+      "initialValue": "0",
+      "key": {
+        "int": 397,
+        "string": "y"
+      },
+      "runtime": false
+    },
     "trigger": {
       "type": "callback",
       "animates": true,
diff --git a/dev/defs/text/text_value_run.json b/dev/defs/text/text_value_run.json
index 41b2ac0..ecb8931 100644
--- a/dev/defs/text/text_value_run.json
+++ b/dev/defs/text/text_value_run.json
@@ -27,6 +27,16 @@
         "string": "text_value"
       },
       "description": "The text string value."
+    },
+    "fieldHeight": {
+      "type": "double",
+      "initialValue": "98.0",
+      "key": {
+        "int": 398,
+        "string": "fieldheight"
+      },
+      "runtime": false,
+      "coop": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/include/rive/assets/file_asset.hpp b/include/rive/assets/file_asset.hpp
index d506558..83113cc 100644
--- a/include/rive/assets/file_asset.hpp
+++ b/include/rive/assets/file_asset.hpp
@@ -9,12 +9,19 @@
 class Factory;
 class FileAsset : public FileAssetBase
 {
+private:
+    std::vector<uint8_t> m_cdnUuid;
+
 public:
+    Span<const uint8_t> cdnUuid() const;
+
+    void decodeCdnUuid(Span<const uint8_t> value) override;
+    void copyCdnUuid(const FileAssetBase& object) override;
     virtual bool decode(Span<const uint8_t>, Factory*) = 0;
-    virtual std::string fileExtension() = 0;
+    virtual std::string fileExtension() const = 0;
     StatusCode import(ImportStack& importStack) override;
 
-    std::string uniqueFilename();
+    std::string uniqueFilename() const;
 };
 } // namespace rive
 
diff --git a/include/rive/assets/font_asset.hpp b/include/rive/assets/font_asset.hpp
index effffc3..e39fb72 100644
--- a/include/rive/assets/font_asset.hpp
+++ b/include/rive/assets/font_asset.hpp
@@ -10,7 +10,7 @@
 {
 public:
     bool decode(Span<const uint8_t>, Factory*) override;
-    std::string fileExtension() override;
+    std::string fileExtension() const override;
     const rcp<Font> font() const { return m_font; }
     void font(rcp<Font> font);
 
diff --git a/include/rive/assets/image_asset.hpp b/include/rive/assets/image_asset.hpp
index 6d941fc..4791b94 100644
--- a/include/rive/assets/image_asset.hpp
+++ b/include/rive/assets/image_asset.hpp
@@ -20,7 +20,7 @@
     std::size_t decodedByteSize = 0;
 #endif
     bool decode(Span<const uint8_t>, Factory*) override;
-    std::string fileExtension() override;
+    std::string fileExtension() const override;
     RenderImage* renderImage() const { return m_RenderImage.get(); }
     void renderImage(std::unique_ptr<RenderImage> renderImage);
 };
diff --git a/include/rive/file.hpp b/include/rive/file.hpp
index 3f79648..b1b75b5 100644
--- a/include/rive/file.hpp
+++ b/include/rive/file.hpp
@@ -4,7 +4,7 @@
 #include "rive/artboard.hpp"
 #include "rive/backboard.hpp"
 #include "rive/factory.hpp"
-#include "rive/file_asset_resolver.hpp"
+#include "rive/file_asset_loader.hpp"
 #include <vector>
 #include <set>
 
@@ -55,11 +55,11 @@
 
     Factory* m_Factory;
 
-    /// The helper used to resolve assets when they're not provided in-band
+    /// The helper used to load assets when they're not provided in-band
     /// with the file.
-    FileAssetResolver* m_AssetResolver;
+    FileAssetLoader* m_AssetLoader;
 
-    File(Factory*, FileAssetResolver*);
+    File(Factory*, FileAssetLoader*);
 
 public:
     ~File();
@@ -68,13 +68,13 @@
     /// Imports a Rive file from a binary buffer.
     /// @param data the raw date of the file.
     /// @param result is an optional status result.
-    /// @param assetResolver is an optional helper to resolve assets which
+    /// @param assetLoader is an optional helper to load assets which
     /// cannot be found in-band.
     /// @returns a pointer to the file, or null on failure.
     static std::unique_ptr<File> import(Span<const uint8_t> data,
                                         Factory*,
                                         ImportResult* result = nullptr,
-                                        FileAssetResolver* assetResolver = nullptr);
+                                        FileAssetLoader* assetLoader = nullptr);
 
     /// @returns the file's backboard. All files have exactly one backboard.
     Backboard* backboard() const { return m_Backboard.get(); }
diff --git a/include/rive/file_asset_loader.hpp b/include/rive/file_asset_loader.hpp
new file mode 100644
index 0000000..201d5a3
--- /dev/null
+++ b/include/rive/file_asset_loader.hpp
@@ -0,0 +1,32 @@
+#ifndef _RIVE_FILE_ASSET_RESOLVER_HPP_
+#define _RIVE_FILE_ASSET_RESOLVER_HPP_
+
+#include <cstdint>
+#include <vector>
+
+namespace rive
+{
+class FileAsset;
+class FileAssetLoader
+{
+public:
+    virtual ~FileAssetLoader() {}
+
+    /// The return value sets the intention for handling loading the contents
+    /// of the given asset. When no asset loader commits to handling the contents 
+    /// we will load assets in band if provided. 
+    /// 
+    /// @param asset describes the asset that Rive is looking for the
+    /// contents of.
+    virtual bool willLoadContents(FileAsset& asset) {
+        return true;
+    }
+
+    /// Load the contents of the given asset
+    ///
+    /// @param asset describes the asset that Rive is looking for the
+    /// contents of.
+    virtual void loadContents(FileAsset& asset) = 0;
+};
+} // namespace rive
+#endif
diff --git a/include/rive/file_asset_resolver.hpp b/include/rive/file_asset_resolver.hpp
deleted file mode 100644
index 9f8b7bf..0000000
--- a/include/rive/file_asset_resolver.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _RIVE_FILE_ASSET_RESOLVER_HPP_
-#define _RIVE_FILE_ASSET_RESOLVER_HPP_
-
-#include <cstdint>
-#include <vector>
-
-namespace rive
-{
-class FileAsset;
-class FileAssetResolver
-{
-public:
-    virtual ~FileAssetResolver() {}
-
-    /// Expected to be overridden to find asset contents when not provided
-    /// in band.
-    /// @param asset describes the asset that Rive is looking for the
-    /// contents of.
-    virtual void loadContents(FileAsset& asset) = 0;
-};
-} // namespace rive
-#endif
diff --git a/include/rive/generated/assets/file_asset_base.hpp b/include/rive/generated/assets/file_asset_base.hpp
index 4856c62..664b4b7 100644
--- a/include/rive/generated/assets/file_asset_base.hpp
+++ b/include/rive/generated/assets/file_asset_base.hpp
@@ -1,7 +1,11 @@
 #ifndef _RIVE_FILE_ASSET_BASE_HPP_
 #define _RIVE_FILE_ASSET_BASE_HPP_
+#include <string>
 #include "rive/assets/asset.hpp"
+#include "rive/core/field_types/core_bytes_type.hpp"
+#include "rive/core/field_types/core_string_type.hpp"
 #include "rive/core/field_types/core_uint_type.hpp"
+#include "rive/span.hpp"
 namespace rive
 {
 class FileAssetBase : public Asset
@@ -29,9 +33,12 @@
     uint16_t coreType() const override { return typeKey; }
 
     static const uint16_t assetIdPropertyKey = 204;
+    static const uint16_t cdnUuidPropertyKey = 359;
+    static const uint16_t cdnBaseUrlPropertyKey = 362;
 
 private:
     uint32_t m_AssetId = 0;
+    std::string m_CdnBaseUrl = "https://public.rive.app/cdn/uuid";
 
 public:
     inline uint32_t assetId() const { return m_AssetId; }
@@ -45,9 +52,25 @@
         assetIdChanged();
     }
 
+    virtual void decodeCdnUuid(Span<const uint8_t> value) = 0;
+    virtual void copyCdnUuid(const FileAssetBase& object) = 0;
+
+    inline const std::string& cdnBaseUrl() const { return m_CdnBaseUrl; }
+    void cdnBaseUrl(std::string value)
+    {
+        if (m_CdnBaseUrl == value)
+        {
+            return;
+        }
+        m_CdnBaseUrl = value;
+        cdnBaseUrlChanged();
+    }
+
     void copy(const FileAssetBase& object)
     {
         m_AssetId = object.m_AssetId;
+        copyCdnUuid(object);
+        m_CdnBaseUrl = object.m_CdnBaseUrl;
         Asset::copy(object);
     }
 
@@ -58,12 +81,20 @@
             case assetIdPropertyKey:
                 m_AssetId = CoreUintType::deserialize(reader);
                 return true;
+            case cdnUuidPropertyKey:
+                decodeCdnUuid(CoreBytesType::deserialize(reader));
+                return true;
+            case cdnBaseUrlPropertyKey:
+                m_CdnBaseUrl = CoreStringType::deserialize(reader);
+                return true;
         }
         return Asset::deserialize(propertyKey, reader);
     }
 
 protected:
     virtual void assetIdChanged() {}
+    virtual void cdnUuidChanged() {}
+    virtual void cdnBaseUrlChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index f062ab5..78cdee6 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -395,6 +395,9 @@
             case AssetBase::namePropertyKey:
                 object->as<AssetBase>()->name(value);
                 break;
+            case FileAssetBase::cdnBaseUrlPropertyKey:
+                object->as<FileAssetBase>()->cdnBaseUrl(value);
+                break;
         }
     }
     static void setUint(Core* object, int propertyKey, uint32_t value)
@@ -1179,6 +1182,8 @@
                 return object->as<CustomPropertyStringBase>()->propertyValue();
             case AssetBase::namePropertyKey:
                 return object->as<AssetBase>()->name();
+            case FileAssetBase::cdnBaseUrlPropertyKey:
+                return object->as<FileAssetBase>()->cdnBaseUrl();
         }
         return "";
     }
@@ -1708,6 +1713,7 @@
             case TextValueRunBase::textPropertyKey:
             case CustomPropertyStringBase::propertyValuePropertyKey:
             case AssetBase::namePropertyKey:
+            case FileAssetBase::cdnBaseUrlPropertyKey:
                 return CoreStringType::id;
             case ComponentBase::parentIdPropertyKey:
             case DrawTargetBase::drawableIdPropertyKey:
@@ -1957,6 +1963,7 @@
             case GradientStopBase::colorValuePropertyKey:
                 return CoreColorType::id;
             case MeshBase::triangleIndexBytesPropertyKey:
+            case FileAssetBase::cdnUuidPropertyKey:
             case FileAssetContentsBase::bytesPropertyKey:
                 return CoreBytesType::id;
             default:
diff --git a/include/rive/importers/file_asset_importer.hpp b/include/rive/importers/file_asset_importer.hpp
index 7d80612..c32cff3 100644
--- a/include/rive/importers/file_asset_importer.hpp
+++ b/include/rive/importers/file_asset_importer.hpp
@@ -9,22 +9,21 @@
 {
 class FileAsset;
 class FileAssetContents;
-class FileAssetResolver;
+class FileAssetLoader;
 class Factory;
 
 class FileAssetImporter : public ImportStackObject
 {
 private:
-    bool m_LoadedContents = false;
     FileAsset* m_FileAsset;
-    FileAssetResolver* m_FileAssetResolver;
+    FileAssetLoader* m_FileAssetLoader;
     Factory* m_Factory;
     // we will delete this when we go out of scope
     std::unique_ptr<FileAssetContents> m_Content;
 
 public:
-    FileAssetImporter(FileAsset*, FileAssetResolver*, Factory*);
-    void loadContents(std::unique_ptr<FileAssetContents> contents);
+    FileAssetImporter(FileAsset*, FileAssetLoader*, Factory*);
+    void onFileAssetContents(std::unique_ptr<FileAssetContents> contents);
     StatusCode resolve() override;
 };
 } // namespace rive
diff --git a/include/rive/relative_local_asset_resolver.hpp b/include/rive/relative_local_asset_loader.hpp
similarity index 79%
rename from include/rive/relative_local_asset_resolver.hpp
rename to include/rive/relative_local_asset_loader.hpp
index fb4a74e..17b84a0 100644
--- a/include/rive/relative_local_asset_resolver.hpp
+++ b/include/rive/relative_local_asset_loader.hpp
@@ -1,7 +1,7 @@
 #ifndef _RIVE_RELATIVE_LOCAL_ASSET_RESOLVER_HPP_
 #define _RIVE_RELATIVE_LOCAL_ASSET_RESOLVER_HPP_
 
-#include "rive/file_asset_resolver.hpp"
+#include "rive/file_asset_loader.hpp"
 #include "rive/assets/file_asset.hpp"
 #include <cstdio>
 #include <string>
@@ -11,16 +11,16 @@
 class FileAsset;
 class Factory;
 
-/// An implementation of FileAssetResolver which finds the assets in a local
+/// An implementation of FileAssetLoader which finds the assets in a local
 /// path relative to the original .riv file looking for them.
-class RelativeLocalAssetResolver : public FileAssetResolver
+class RelativeLocalAssetLoader : public FileAssetLoader
 {
 private:
     std::string m_Path;
     Factory* m_Factory;
 
 public:
-    RelativeLocalAssetResolver(std::string filename, Factory* factory) : m_Factory(factory)
+    RelativeLocalAssetLoader(std::string filename, Factory* factory) : m_Factory(factory)
     {
         std::size_t finalSlash = filename.rfind('/');
 
diff --git a/src/assets/file_asset.cpp b/src/assets/file_asset.cpp
index 65f3872..744622a 100644
--- a/src/assets/file_asset.cpp
+++ b/src/assets/file_asset.cpp
@@ -16,7 +16,7 @@
     return Super::import(importStack);
 }
 
-std::string FileAsset::uniqueFilename()
+std::string FileAsset::uniqueFilename() const
 {
     // remove final extension
     std::string uniqueFilename = name();
@@ -28,3 +28,16 @@
     }
     return uniqueFilename + "-" + std::to_string(assetId()) + "." + fileExtension();
 }
+
+void FileAsset::copyCdnUuid(const FileAssetBase& object)
+{
+    // Should never be called.
+    assert(false);
+}
+
+void FileAsset::decodeCdnUuid(Span<const uint8_t> value)
+{
+    m_cdnUuid = std::vector<uint8_t>(value.begin(), value.end());
+}
+
+Span<const uint8_t> FileAsset::cdnUuid() const { return m_cdnUuid; }
diff --git a/src/assets/file_asset_contents.cpp b/src/assets/file_asset_contents.cpp
index c1e869e..829b709 100644
--- a/src/assets/file_asset_contents.cpp
+++ b/src/assets/file_asset_contents.cpp
@@ -12,7 +12,7 @@
     {
         return StatusCode::MissingObject;
     }
-    fileAssetImporter->loadContents(std::unique_ptr<FileAssetContents>(this));
+    fileAssetImporter->onFileAssetContents(std::unique_ptr<FileAssetContents>(this));
 
     return Super::import(importStack);
 }
diff --git a/src/assets/font_asset.cpp b/src/assets/font_asset.cpp
index 9340704..8f41ffc 100644
--- a/src/assets/font_asset.cpp
+++ b/src/assets/font_asset.cpp
@@ -9,6 +9,6 @@
     m_font = factory->decodeFont(data);
     return m_font != nullptr;
 }
-std::string FontAsset::fileExtension() { return "ttf"; }
+std::string FontAsset::fileExtension() const { return "ttf"; }
 
 void FontAsset::font(rcp<Font> font) { m_font = std::move(font); }
\ No newline at end of file
diff --git a/src/assets/image_asset.cpp b/src/assets/image_asset.cpp
index e34f4c7..a513060 100644
--- a/src/assets/image_asset.cpp
+++ b/src/assets/image_asset.cpp
@@ -20,4 +20,4 @@
     m_RenderImage = std::move(renderImage);
 }
 
-std::string ImageAsset::fileExtension() { return "png"; }
+std::string ImageAsset::fileExtension() const { return "png"; }
diff --git a/src/file.cpp b/src/file.cpp
index 19c9a26..952dc0e 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -121,8 +121,8 @@
     return object;
 }
 
-File::File(Factory* factory, FileAssetResolver* assetResolver) :
-    m_Factory(factory), m_AssetResolver(assetResolver)
+File::File(Factory* factory, FileAssetLoader* assetLoader) :
+    m_Factory(factory), m_AssetLoader(assetLoader)
 {
     Counter::update(Counter::kFile, +1);
 
@@ -134,7 +134,7 @@
 std::unique_ptr<File> File::import(Span<const uint8_t> bytes,
                                    Factory* factory,
                                    ImportResult* result,
-                                   FileAssetResolver* assetResolver)
+                                   FileAssetLoader* assetLoader)
 {
     BinaryReader reader(bytes);
     RuntimeHeader header;
@@ -161,7 +161,8 @@
         }
         return nullptr;
     }
-    auto file = std::unique_ptr<File>(new File(factory, assetResolver));
+    auto file = std::unique_ptr<File>(new File(factory, assetLoader));
+
     auto readResult = file->read(reader, header);
     if (readResult != ImportResult::success)
     {
@@ -279,7 +280,7 @@
             case ImageAsset::typeKey:
             case FontAsset::typeKey:
                 stackObject =
-                    new FileAssetImporter(object->as<FileAsset>(), m_AssetResolver, m_Factory);
+                    new FileAssetImporter(object->as<FileAsset>(), m_AssetLoader, m_Factory);
                 stackType = FileAsset::typeKey;
                 break;
         }
diff --git a/src/importers/file_asset_importer.cpp b/src/importers/file_asset_importer.cpp
index dc4a214..f7d8ffd 100644
--- a/src/importers/file_asset_importer.cpp
+++ b/src/importers/file_asset_importer.cpp
@@ -1,38 +1,39 @@
 #include "rive/importers/file_asset_importer.hpp"
 #include "rive/assets/file_asset_contents.hpp"
 #include "rive/assets/file_asset.hpp"
-#include "rive/file_asset_resolver.hpp"
+#include "rive/file_asset_loader.hpp"
 #include "rive/span.hpp"
 #include <cstdint>
 
 using namespace rive;
 
 FileAssetImporter::FileAssetImporter(FileAsset* fileAsset,
-                                     FileAssetResolver* assetResolver,
+                                     FileAssetLoader* assetLoader,
                                      Factory* factory) :
-    m_FileAsset(fileAsset), m_FileAssetResolver(assetResolver), m_Factory(factory)
+    m_FileAsset(fileAsset), m_FileAssetLoader(assetLoader), m_Factory(factory)
 {}
 
-void FileAssetImporter::loadContents(std::unique_ptr<FileAssetContents> contents)
+// if file asset contents are found when importing a rive file, store those for when we resolve 
+// the importer later
+void FileAssetImporter::onFileAssetContents(std::unique_ptr<FileAssetContents> contents)
 {
     // we should only ever be called once
     assert(!m_Content);
     m_Content = std::move(contents);
-
-    auto data = m_Content->bytes();
-    if (m_FileAsset->decode(data, m_Factory))
-    {
-        m_LoadedContents = true;
-    }
 }
 
 StatusCode FileAssetImporter::resolve()
 {
-    if (!m_LoadedContents && m_FileAssetResolver != nullptr)
+    // If we have a file asset loader that commits to loading the file asset, let it handle it
+    if (m_FileAssetLoader != nullptr && m_FileAssetLoader->willLoadContents(*m_FileAsset))
     {
-        // Contents weren't available in-band, or they couldn't be decoded. Try
-        // to find them out of band.
-        m_FileAssetResolver->loadContents(*m_FileAsset);
+        m_FileAssetLoader->loadContents(*m_FileAsset);
+    }
+    // If we do not, but we have found in band contents, load those
+    else if (m_Content != nullptr)
+    {
+        auto data = m_Content->bytes();
+        m_FileAsset->decode(data, m_Factory);
     }
 
     // Note that it's ok for an asset to not resolve (or to resolve async).
diff --git a/test/assets/hosted_font_file.riv b/test/assets/hosted_font_file.riv
new file mode 100644
index 0000000..ac0882f
--- /dev/null
+++ b/test/assets/hosted_font_file.riv
Binary files differ
diff --git a/test/assets/hosted_image_file.riv b/test/assets/hosted_image_file.riv
new file mode 100644
index 0000000..3c43288
--- /dev/null
+++ b/test/assets/hosted_image_file.riv
Binary files differ
diff --git a/test/assets/in_band_asset.riv b/test/assets/in_band_asset.riv
new file mode 100644
index 0000000..1aad7e4
--- /dev/null
+++ b/test/assets/in_band_asset.riv
Binary files differ
diff --git a/test/cdn_asset_test.cpp b/test/cdn_asset_test.cpp
new file mode 100644
index 0000000..32f9732
--- /dev/null
+++ b/test/cdn_asset_test.cpp
@@ -0,0 +1,52 @@
+#include <rive/bones/skin.hpp>
+#include <rive/bones/tendon.hpp>
+#include <rive/file.hpp>
+#include <rive/node.hpp>
+#include <rive/shapes/clipping_shape.hpp>
+#include <rive/shapes/path_vertex.hpp>
+#include <rive/shapes/points_path.hpp>
+#include <rive/shapes/rectangle.hpp>
+#include <rive/shapes/shape.hpp>
+#include <rive/assets/file_asset.hpp>
+#include <rive/assets/image_asset.hpp>
+#include <rive/assets/font_asset.hpp>
+#include "utils/no_op_factory.hpp"
+#include "rive_file_reader.hpp"
+#include <catch.hpp>
+#include <cstdio>
+
+TEST_CASE("Image assets with cdn information loads correctly", "[cdn]")
+{
+    auto file = ReadRiveFile("../../test/assets/hosted_image_file.riv");
+
+    auto assets = file->assets();
+    REQUIRE(assets.size() == 1);
+    auto firstAsset = assets[0];
+    REQUIRE(firstAsset->is<rive::ImageAsset>());
+
+    // this is a 16 byte uuid, any good ideas on how to manage this test?
+    // we could convert it to a string for the getter...
+    REQUIRE(firstAsset->cdnUuid().size() == 16);
+    REQUIRE(firstAsset->cdnBaseUrl() == "https://public.uat.rive.app/cdn/uuid");
+
+    REQUIRE(firstAsset->uniqueFilename() == "one-45008.png");
+    REQUIRE(firstAsset->fileExtension() == "png");
+}
+
+TEST_CASE("Font assets with cdn information loads correctly", "[cdn]")
+{
+    auto file = ReadRiveFile("../../test/assets/hosted_font_file.riv");
+
+    auto assets = file->assets();
+    REQUIRE(assets.size() == 1);
+    auto firstAsset = assets[0];
+    REQUIRE(firstAsset->is<rive::FontAsset>());
+
+    // this is a 16 byte uuid, any good ideas on how to manage this test?
+    // we could convert it to a string for the getter...
+    REQUIRE(firstAsset->cdnUuid().size() == 16);
+    REQUIRE(firstAsset->cdnBaseUrl() == "https://public.uat.rive.app/cdn/uuid");
+
+    REQUIRE(firstAsset->uniqueFilename() == "Inter-43276.ttf");
+    REQUIRE(firstAsset->fileExtension() == "ttf");
+}
diff --git a/test/image_asset_test.cpp b/test/image_asset_test.cpp
index 2ec140e..d6b4c80 100644
--- a/test/image_asset_test.cpp
+++ b/test/image_asset_test.cpp
@@ -4,7 +4,7 @@
 #include <rive/shapes/rectangle.hpp>
 #include <rive/shapes/image.hpp>
 #include <rive/assets/image_asset.hpp>
-#include <rive/relative_local_asset_resolver.hpp>
+#include <rive/relative_local_asset_loader.hpp>
 #include <utils/no_op_factory.hpp>
 #include <utils/no_op_renderer.hpp>
 #include "rive_file_reader.hpp"
@@ -49,9 +49,9 @@
     rive::NoOpFactory gEmptyFactory;
 
     std::string filename = "../../test/assets/out_of_band/walle.riv";
-    rive::RelativeLocalAssetResolver resolver(filename, &gEmptyFactory);
+    rive::RelativeLocalAssetLoader loader(filename, &gEmptyFactory);
 
-    auto file = ReadRiveFile(filename.c_str(), &gEmptyFactory, &resolver);
+    auto file = ReadRiveFile(filename.c_str(), &gEmptyFactory, &loader);
 
     auto node = file->artboard()->find("walle");
     REQUIRE(node != nullptr);
diff --git a/test/image_mesh_test.cpp b/test/image_mesh_test.cpp
index eb3beb5..5ba568e 100644
--- a/test/image_mesh_test.cpp
+++ b/test/image_mesh_test.cpp
@@ -5,7 +5,7 @@
 #include <rive/shapes/image.hpp>
 #include <rive/shapes/mesh.hpp>
 #include <rive/assets/image_asset.hpp>
-#include <rive/relative_local_asset_resolver.hpp>
+#include <rive/relative_local_asset_loader.hpp>
 #include <utils/no_op_renderer.hpp>
 #include "rive_file_reader.hpp"
 #include <catch.hpp>
diff --git a/test/in_band_asset_load_test.cpp b/test/in_band_asset_load_test.cpp
new file mode 100644
index 0000000..c4b9587
--- /dev/null
+++ b/test/in_band_asset_load_test.cpp
@@ -0,0 +1,86 @@
+#include <rive/bones/skin.hpp>
+#include <rive/bones/tendon.hpp>
+#include <rive/file.hpp>
+#include <rive/node.hpp>
+#include <rive/shapes/clipping_shape.hpp>
+#include <rive/shapes/path_vertex.hpp>
+#include <rive/shapes/points_path.hpp>
+#include <rive/shapes/rectangle.hpp>
+#include <rive/shapes/shape.hpp>
+#include <rive/assets/file_asset.hpp>
+#include <rive/assets/image_asset.hpp>
+#include <rive/assets/font_asset.hpp>
+#include <rive/assets/font_asset.hpp>
+#include "utils/no_op_factory.hpp"
+#include "rive_file_reader.hpp"
+#include <catch.hpp>
+#include <cstdio>
+
+TEST_CASE("Load asset with in-band image", "[asset]")
+{
+    auto file = ReadRiveFile("../../test/assets/in_band_asset.riv");
+
+    auto assets = file->assets();
+    REQUIRE(assets.size() == 1);
+    auto firstAsset = assets[0];
+    REQUIRE(firstAsset->is<rive::ImageAsset>());
+
+    // in band asset, no cdn uuid set
+    REQUIRE(firstAsset->cdnUuid().size() == 0);
+
+    // default value
+    REQUIRE(firstAsset->cdnBaseUrl() == "https://public.rive.app/cdn/uuid");
+
+    REQUIRE(firstAsset->uniqueFilename() == "1x1-45022.png");
+    REQUIRE(firstAsset->fileExtension() == "png");
+
+    // we load in band assets, so the decoded size >0
+    REQUIRE(firstAsset->as<rive::ImageAsset>()->decodedByteSize == 308);
+}
+
+class TestAssetLoader : public rive ::FileAssetLoader
+{
+
+public:
+    rive::FileAsset* attemptedAsset;
+
+    bool willLoadContents(rive::FileAsset& asset) override { return true; }
+    void loadContents(rive::FileAsset& asset) override { attemptedAsset = &asset; }
+};
+
+TEST_CASE("Load asset with in-band image, disabling loading in band assets", "[asset]")
+{
+    auto loader = TestAssetLoader();
+
+    // our Loader has not attempted to load any asset.
+    REQUIRE(loader.attemptedAsset == nullptr);
+
+    auto file = ReadRiveFile("../../test/assets/in_band_asset.riv", nullptr, &loader);
+
+    auto assets = file->assets();
+    REQUIRE(assets.size() == 1);
+    auto firstAsset = assets[0];
+    REQUIRE(firstAsset->is<rive::ImageAsset>());
+
+    // in band asset, no cdn uuid set
+    REQUIRE(firstAsset->cdnUuid().size() == 0);
+
+    // default value
+    REQUIRE(firstAsset->cdnBaseUrl() == "https://public.rive.app/cdn/uuid");
+
+    REQUIRE(firstAsset->uniqueFilename() == "1x1-45022.png");
+    REQUIRE(firstAsset->fileExtension() == "png");
+
+    // we do not load in band assets, so the decoded size is still 0
+    REQUIRE(firstAsset->as<rive::ImageAsset>()->decodedByteSize == 0);
+
+    // however our FileAssetLoader had a chance to load this asset.
+    // in band asset, no cdn uuid set
+    REQUIRE(loader.attemptedAsset->cdnUuid().size() == 0);
+
+    // default value
+    REQUIRE(loader.attemptedAsset->cdnBaseUrl() == "https://public.rive.app/cdn/uuid");
+
+    REQUIRE(loader.attemptedAsset->uniqueFilename() == "1x1-45022.png");
+    REQUIRE(loader.attemptedAsset->fileExtension() == "png");
+}
diff --git a/test/rive_file_reader.hpp b/test/rive_file_reader.hpp
index 9fe969e..761b198 100644
--- a/test/rive_file_reader.hpp
+++ b/test/rive_file_reader.hpp
@@ -11,7 +11,8 @@
 
 static inline std::unique_ptr<rive::File> ReadRiveFile(const char path[],
                                                        rive::Factory* factory = nullptr,
-                                                       rive::FileAssetResolver* resolver = nullptr)
+                                                       rive::FileAssetLoader* loader = nullptr,
+                                                       bool loadInBandAssets = true)
 {
     if (!factory)
     {
@@ -29,7 +30,7 @@
     fclose(fp);
 
     rive::ImportResult result;
-    auto file = rive::File::import(bytes, factory, &result, resolver);
+    auto file = rive::File::import(bytes, factory, &result, loader);
     REQUIRE(result == rive::ImportResult::success);
     REQUIRE(file.get() != nullptr);
     REQUIRE(file->artboard() != nullptr);
diff --git a/viewer/include/viewer/sample_tools/sample_atlas_packer.hpp b/viewer/include/viewer/sample_tools/sample_atlas_packer.hpp
index 8c1a34b..e9e3c09 100644
--- a/viewer/include/viewer/sample_tools/sample_atlas_packer.hpp
+++ b/viewer/include/viewer/sample_tools/sample_atlas_packer.hpp
@@ -2,7 +2,7 @@
 #define _RIVE_SAMPLE_ATLAS_PACKER_HPP_
 
 #include "rive/span.hpp"
-#include "rive/file_asset_resolver.hpp"
+#include "rive/file_asset_loader.hpp"
 #include "rive/math/mat2d.hpp"
 #include "rive/renderer.hpp"
 #include "rive/tess/sokol/sokol_tess_renderer.hpp"
@@ -65,7 +65,7 @@
     SampleAtlas* atlas(std::size_t index);
 };
 
-class SampleAtlasResolver : public FileAssetResolver
+class SampleAtlasLoader : public FileAssetLoader
 {
 private:
     SampleAtlasPacker* m_packer;