diff --git a/SVSim.BattleNode/Protocol/IMsgBody.cs b/SVSim.BattleNode/Protocol/IMsgBody.cs
new file mode 100644
index 0000000..b1c7fb2
--- /dev/null
+++ b/SVSim.BattleNode/Protocol/IMsgBody.cs
@@ -0,0 +1,10 @@
+namespace SVSim.BattleNode.Protocol;
+
+///
+/// Marker for every type that can appear as .
+/// Implementers fall into two camps: typed records used on the outbound path
+/// (one per scripted frame shape) and used on the inbound
+/// path. The marker exists so the envelope can carry either without falling
+/// back to object.
+///
+public interface IMsgBody { }
diff --git a/SVSim.BattleNode/Protocol/RawBody.cs b/SVSim.BattleNode/Protocol/RawBody.cs
new file mode 100644
index 0000000..6d4748d
--- /dev/null
+++ b/SVSim.BattleNode/Protocol/RawBody.cs
@@ -0,0 +1,16 @@
+namespace SVSim.BattleNode.Protocol;
+
+///
+/// Wraps a parsed-dictionary body for the inbound path.
+/// returns this; flattens back to
+/// top-level keys when echoing.
+///
+public sealed class RawBody : IMsgBody
+{
+ public Dictionary Entries { get; }
+
+ public RawBody(Dictionary entries)
+ {
+ Entries = entries;
+ }
+}