diff --git a/src/TeamsISO.Engine/Domain/EngineAlert.cs b/src/TeamsISO.Engine/Domain/EngineAlert.cs
new file mode 100644
index 0000000..b9189cd
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/EngineAlert.cs
@@ -0,0 +1,19 @@
+namespace TeamsISO.Engine.Domain;
+
+///
+/// Structured engine alerts for UI banner display and ops logging.
+///
+public abstract record EngineAlert(string Message)
+{
+ public sealed record NdiRuntimeMismatch(string DetectedVersion, string ExpectedVersion)
+ : EngineAlert($"NDI runtime version mismatch: detected {DetectedVersion}, expected {ExpectedVersion}.");
+
+ public sealed record OutputNameCollision(string Name)
+ : EngineAlert($"Another TeamsISO instance on the LAN is emitting an output named '{Name}'.");
+
+ public sealed record PipelineError(Guid ParticipantId, string Reason)
+ : EngineAlert($"Pipeline {ParticipantId} entered Error: {Reason}");
+
+ public sealed record ConfigSaveFailed(string Reason)
+ : EngineAlert($"Failed to save configuration: {Reason}");
+}
diff --git a/src/TeamsISO.Engine/Domain/EngineConfig.cs b/src/TeamsISO.Engine/Domain/EngineConfig.cs
new file mode 100644
index 0000000..6b37927
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/EngineConfig.cs
@@ -0,0 +1,9 @@
+namespace TeamsISO.Engine.Domain;
+
+public sealed record EngineConfig(
+ FrameProcessingSettings Global,
+ IReadOnlyList Assignments)
+{
+ public static readonly EngineConfig Default =
+ new(FrameProcessingSettings.Default, Array.Empty());
+}
diff --git a/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs b/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs
new file mode 100644
index 0000000..74c3cc8
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs
@@ -0,0 +1,34 @@
+namespace TeamsISO.Engine.Domain;
+
+public sealed record FrameProcessingSettings(
+ TargetFramerate Framerate,
+ TargetResolution Resolution,
+ AspectMode Aspect,
+ AudioMode Audio)
+{
+ public static readonly FrameProcessingSettings Default =
+ new(TargetFramerate.Fps30, TargetResolution.R1080p, AspectMode.Pillarbox, AudioMode.Auto);
+
+ /// Returns the framerate enum value as a numeric frames-per-second.
+ public double FramerateHz => Framerate switch
+ {
+ TargetFramerate.Fps23_976 => 24000.0 / 1001.0,
+ TargetFramerate.Fps24 => 24.0,
+ TargetFramerate.Fps25 => 25.0,
+ TargetFramerate.Fps29_97 => 30000.0 / 1001.0,
+ TargetFramerate.Fps30 => 30.0,
+ TargetFramerate.Fps50 => 50.0,
+ TargetFramerate.Fps59_94 => 60000.0 / 1001.0,
+ TargetFramerate.Fps60 => 60.0,
+ _ => throw new InvalidOperationException($"Unknown framerate: {Framerate}")
+ };
+
+ /// Returns the resolution as (width, height).
+ public (int Width, int Height) ResolutionSize => Resolution switch
+ {
+ TargetResolution.R720p => (1280, 720),
+ TargetResolution.R1080p => (1920, 1080),
+ TargetResolution.R4K => (3840, 2160),
+ _ => throw new InvalidOperationException($"Unknown resolution: {Resolution}")
+ };
+}
diff --git a/src/TeamsISO.Engine/Domain/IsoAssignment.cs b/src/TeamsISO.Engine/Domain/IsoAssignment.cs
new file mode 100644
index 0000000..a62e0cd
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/IsoAssignment.cs
@@ -0,0 +1,9 @@
+namespace TeamsISO.Engine.Domain;
+
+///
+/// Operator's intent for an ISO output. Persisted to config.json.
+///
+public sealed record IsoAssignment(
+ Guid ParticipantId,
+ bool IsEnabled,
+ string? CustomOutputName);
diff --git a/src/TeamsISO.Engine/Domain/IsoHealthStats.cs b/src/TeamsISO.Engine/Domain/IsoHealthStats.cs
new file mode 100644
index 0000000..84130a0
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/IsoHealthStats.cs
@@ -0,0 +1,15 @@
+namespace TeamsISO.Engine.Domain;
+
+public sealed record IsoHealthStats(
+ long FramesIn,
+ long FramesOut,
+ long FramesDropped,
+ long FramesDuplicated,
+ DateTimeOffset? LastFrameAt,
+ double IncomingFps,
+ int IncomingWidth,
+ int IncomingHeight)
+{
+ public static readonly IsoHealthStats Empty =
+ new(0, 0, 0, 0, null, 0, 0, 0);
+}
diff --git a/src/TeamsISO.Engine/Domain/IsoOutput.cs b/src/TeamsISO.Engine/Domain/IsoOutput.cs
new file mode 100644
index 0000000..4a660f4
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/IsoOutput.cs
@@ -0,0 +1,7 @@
+namespace TeamsISO.Engine.Domain;
+
+public sealed record IsoOutput(
+ Guid ParticipantId,
+ string EffectiveOutputName,
+ IsoHealthStats Stats,
+ IsoState State);
diff --git a/src/TeamsISO.Engine/Domain/Participant.cs b/src/TeamsISO.Engine/Domain/Participant.cs
new file mode 100644
index 0000000..2a38b67
--- /dev/null
+++ b/src/TeamsISO.Engine/Domain/Participant.cs
@@ -0,0 +1,12 @@
+namespace TeamsISO.Engine.Domain;
+
+///
+/// Operator-facing identity for a single human in the meeting.
+/// Id is engine-assigned and stable across the rename heuristic.
+///
+public sealed record Participant(
+ Guid Id,
+ string DisplayName,
+ NdiSource? CurrentSource,
+ DateTimeOffset FirstSeen,
+ DateTimeOffset LastSeen);