# ===================================================================== # Node Events — published by Node Agent → Render Orchestrator # Routing: flatrender.events (topic) — key: node.*.v1 # ===================================================================== events: # ------------------------------------------------------------------- # node.online.v1 — node agent starts up # ------------------------------------------------------------------- node.online.v1: routing_key: node.online.v1 payload: type: object required: [node_id, region, node_agent_version, current_ae_version] properties: node_id: { type: string, format: uuid } node_ip: { type: string, format: ipv4 } region: { type: string } node_agent_version: { type: string } current_ae_version: { type: string } available_ae_versions: type: array items: { type: string } ram_gb: { type: integer } cpu_cores: { type: integer } cache_used_gb: { type: integer } cached_template_md5s: type: array items: { type: string } # ------------------------------------------------------------------- # node.offline.v1 — graceful shutdown OR detected missed heartbeats # ------------------------------------------------------------------- node.offline.v1: routing_key: node.offline.v1 payload: type: object required: [node_id, reason] properties: node_id: { type: string, format: uuid } reason: { type: string, enum: [Shutdown, HeartbeatLost, Maintenance, Disabled] } last_heartbeat_at: { type: string, format: date-time } current_job_id: { type: string, format: uuid, nullable: true } # ------------------------------------------------------------------- # node.heartbeat.v1 — every 5s (NOT broadcast on topic exchange, # sent direct to orchestrator HTTP endpoint OR a dedicated stream) # Documented here for completeness. # ------------------------------------------------------------------- node.heartbeat.v1: routing_key: node.heartbeat.v1 transport: HTTP POST /v1/internal/nodes/{node_id}/heartbeat payload: type: object required: [node_id, status, recorded_at] properties: node_id: { type: string, format: uuid } status: { type: string, enum: [Ready, Busy, Crashed, Updating] } recorded_at: { type: string, format: date-time } cpu_pct: { type: integer, minimum: 0, maximum: 100 } ram_available_mb: { type: integer } ae_running: { type: boolean } current_job_id: { type: string, format: uuid, nullable: true } current_frame_job_id: { type: string, format: uuid, nullable: true } current_frame: { type: integer, nullable: true } cache_used_gb: { type: integer } # ------------------------------------------------------------------- # node.crashed.v1 — AfterFX crashed mid-render # ------------------------------------------------------------------- node.crashed.v1: routing_key: node.crashed.v1 description: AE process exited unexpectedly while rendering. payload: type: object required: [node_id, crashed_at] properties: node_id: { type: string, format: uuid } render_job_id: { type: string, format: uuid, nullable: true } frame_job_id: { type: string, format: uuid, nullable: true } crashed_at: { type: string, format: date-time } last_known_frame: { type: integer, nullable: true } crash_signal: { type: string, nullable: true } ae_version: { type: string } error_log_tail: { type: string, description: "Last ~50 lines of AE log" } log_file_url: { type: string, nullable: true } auto_recovery_started: { type: boolean } # ------------------------------------------------------------------- # node.cache.updated.v1 — template cache changed (download or evict) # ------------------------------------------------------------------- node.cache.updated.v1: routing_key: node.cache.updated.v1 payload: type: object required: [node_id, action, project_id, aep_file_md5] properties: node_id: { type: string, format: uuid } action: { type: string, enum: [Downloaded, Evicted, Verified, Failed] } project_id: { type: string, format: uuid } aep_file_md5: { type: string } file_size_bytes: { type: integer, format: int64 } cache_used_gb: { type: integer, description: "Total cache size after action" } duration_ms: { type: integer, nullable: true } error_message: { type: string, nullable: true } # ------------------------------------------------------------------- # node.frame.completed.v1 — single frame done (high-frequency) # NOT on topic; sent via direct push to orchestrator. # ------------------------------------------------------------------- node.frame.completed.v1: routing_key: node.frame.completed.v1 transport: HTTP POST /v1/internal/render/jobs/{job_id}/frames payload: type: object required: [render_job_id, frame_job_id, frame_number] properties: render_job_id: { type: string, format: uuid } frame_job_id: { type: string, format: uuid } frame_number: { type: integer } file_size_bytes: { type: integer } completed_at: { type: string, format: date-time }