//! GLP 标准信封 · glp_envelope //! //! 协议转工程第一件(HoloLake第二阶段总体规划-20260815 · 施工总纲): //! GLS-0300《GLP 通信核心协议》第3节"标准消息结构"的逐字段工程映射。 //! 字段一个不造、一个不丢——老家谱 YAML 原文即本文件的形状。 //! //! 指挥链落点(铁律四):人格体→宿主的一切指令都必须是这个信封; //! 宿主只认信封不认散话。 use ring::digest::{digest, SHA256}; use serde::{Deserialize, Serialize}; #[cfg(test)] use std::time::{SystemTime, UNIX_EPOCH}; const MAX_LANGUAGE_CONTENT_BYTES: usize = 64 * 1024; /// GLS-0300 · receiver.routing_mode #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum RoutingMode { Direct, Channel, Broadcast, } /// GLS-0300 · payload.content_type #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ContentType { Text, Command, Event, State, Reference, } /// GLS-0300 · control.priority #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Priority { Low, Normal, High, Critical, } /// GLS-0300 · control.retry_policy #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum RetryPolicy { None, Safe, Guaranteed, } /// GLS-0300 · sender #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpSender { pub object_id: String, pub object_type: String, #[serde(default)] pub world_path: String, } /// GLS-0300 · receiver #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpReceiver { pub object_id: String, pub object_type: String, pub routing_mode: RoutingMode, } /// GLS-0300 · context(hldp_anchor 即铁律二的记忆锚点) #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpContext { #[serde(default)] pub conversation_id: String, #[serde(default)] pub parent_message_id: String, #[serde(default)] pub relation_id: String, #[serde(default)] pub task_id: String, #[serde(default)] pub hldp_anchor: String, } /// GLS-0300 · payload #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpPayload { pub language: String, pub content_type: ContentType, pub content: String, #[serde(default)] pub attachments: Vec, } /// GLS-0300 · control #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpControl { pub priority: Priority, pub ack_required: bool, pub receipt_required: bool, #[serde(default)] pub expires_at: String, pub retry_policy: RetryPolicy, } /// GLS-0300 · integrity(高风险消息须带签名与回执链——通信安全节) #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpIntegrity { #[serde(default)] pub checksum: String, #[serde(default)] pub signature: String, } /// GLS-0300 · glp_message 全信封 #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", deny_unknown_fields)] pub struct GlpMessage { pub protocol: String, pub message_id: String, pub message_type: String, pub created_at: String, pub sender: GlpSender, pub receiver: GlpReceiver, pub context: GlpContext, pub payload: GlpPayload, pub control: GlpControl, pub integrity: GlpIntegrity, } /// 信封进门第一道验:协议号、编号格式、收发主体必须在场。 /// 不合格的信封宿主不收——指挥链只认标准件。 pub fn validate_envelope(message: &GlpMessage) -> Result<(), String> { if message.protocol != "GLP/1.0" { return Err("HOLOLAKE_GLP_PROTOCOL_UNKNOWN".into()); } if !valid_message_id(&message.message_id) || message.created_at.trim().is_empty() { return Err("HOLOLAKE_GLP_ENVELOPE_INCOMPLETE".into()); } if message.sender.object_id.trim().is_empty() || message.sender.object_type.trim().is_empty() { return Err("HOLOLAKE_GLP_SENDER_INCOMPLETE".into()); } if message.receiver.object_id.trim().is_empty() || message.receiver.object_type.trim().is_empty() { return Err("HOLOLAKE_GLP_RECEIVER_INCOMPLETE".into()); } if message.payload.content.is_empty() || message.payload.content.len() > MAX_LANGUAGE_CONTENT_BYTES || message.payload.language.len() > 32 || message.payload.attachments.len() > 16 { return Err("HOLOLAKE_GLP_PAYLOAD_EMPTY".into()); } if !matches!( message.message_type.as_str(), "DIRECT" | "CHANNEL" | "BROADCAST" ) { return Err("HOLOLAKE_GLP_MESSAGE_TYPE_INVALID".into()); } Ok(()) } /// 圆湖协议膜的外部入口校验:通信正文只能作为语言表达进入,不能夹带附件或执行权。 pub fn validate_external_language_envelope(message: &GlpMessage) -> Result<(), String> { validate_envelope(message)?; if message.receiver.object_id != "HOLOLAKE-HOST" || message.receiver.object_type != "host" || message.sender.object_type != "external_ai_visitor" || !message.payload.attachments.is_empty() || matches!(message.payload.content_type, ContentType::Command) { return Err("HOLOLAKE_CIRCULAR_LAKE_EXPRESSION_BOUNDARY_DENIED".into()); } if message.integrity.checksum != payload_checksum(&message.payload.content) { return Err("HOLOLAKE_CIRCULAR_LAKE_CHECKSUM_INVALID".into()); } Ok(()) } pub fn payload_checksum(content: &str) -> String { digest(&SHA256, content.as_bytes()) .as_ref() .iter() .map(|byte| format!("{byte:02x}")) .collect() } fn valid_message_id(value: &str) -> bool { let Some(suffix) = value.strip_prefix("GLP-MSG-") else { return false; }; let mut parts = suffix.split('-'); matches!(parts.next(), Some(date) if date.len() == 8 && date.bytes().all(|byte| byte.is_ascii_digit())) && matches!(parts.next(), Some(sequence) if sequence.len() == 6 && sequence.bytes().all(|byte| byte.is_ascii_digit())) && parts.next().is_none() } /// 消息编号按老家谱格式生成:GLP-MSG-YYYYMMDD-000001。 /// 序号由账本(管家层)当日累计给出,这里只拼形状。 #[cfg(test)] pub fn build_message_id(date_compact: &str, daily_sequence: u64) -> String { format!("GLP-MSG-{date_compact}-{daily_sequence:06}") } /// ISO-8601 近似时刻戳(秒级,UTC)——老家谱要 ISO-8601,工程给秒级事实。 #[cfg(test)] pub fn now_iso8601() -> String { let secs = SystemTime::now() .duration_since(UNIX_EPOCH) .map(|duration| duration.as_secs()) .unwrap_or(0); let days = secs / 86_400; let rem = secs % 86_400; let (hours, minutes, seconds) = (rem / 3_600, (rem % 3_600) / 60, rem % 60); let (year, month, day) = civil_from_days(days as i64); format!("{year:04}-{month:02}-{day:02}T{hours:02}:{minutes:02}:{seconds:02}Z") } /// 1970-01-01 起的天数转公历年月日(Howard Hinnant 算法,纯本地实现不引新依赖)。 #[cfg(test)] fn civil_from_days(z: i64) -> (i64, u32, u32) { let z = z + 719_468; let era = if z >= 0 { z } else { z - 146_096 } / 146_097; let doe = (z - era * 146_097) as u64; let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365; let y = yoe as i64 + era * 400; let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = (doy - (153 * mp + 2) / 5 + 1) as u32; let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32; (if m <= 2 { y + 1 } else { y }, m, d) } #[cfg(test)] mod tests { use super::*; fn sample_envelope() -> GlpMessage { GlpMessage { protocol: "GLP/1.0".into(), message_id: build_message_id("20260815", 1), message_type: "DIRECT".into(), created_at: now_iso8601(), sender: GlpSender { object_id: "ICE-P-ZY001".into(), object_type: "age".into(), world_path: "glw://tolaria".into(), }, receiver: GlpReceiver { object_id: "HOLOLAKE-HOST".into(), object_type: "host".into(), routing_mode: RoutingMode::Direct, }, context: GlpContext { task_id: "ZY-TEST".into(), hldp_anchor: "ZY-CHECKPOINT-20260815-014".into(), ..Default::default() }, payload: GlpPayload { language: "zh-CN".into(), content_type: ContentType::Command, content: "开工".into(), attachments: vec![], }, control: GlpControl { priority: Priority::Normal, ack_required: true, receipt_required: true, expires_at: String::new(), retry_policy: RetryPolicy::None, }, integrity: GlpIntegrity::default(), } } #[test] fn envelope_round_trip_keeps_every_field() { let message = sample_envelope(); let json = serde_json::to_string(&message).unwrap(); let back: GlpMessage = serde_json::from_str(&json).unwrap(); assert_eq!(back.protocol, "GLP/1.0"); assert_eq!(back.sender.object_id, "ICE-P-ZY001"); assert_eq!(back.receiver.routing_mode, RoutingMode::Direct); assert_eq!(back.payload.content_type, ContentType::Command); assert_eq!(back.context.hldp_anchor, "ZY-CHECKPOINT-20260815-014"); assert!(back.control.receipt_required); validate_envelope(&back).unwrap(); } #[test] fn message_id_follows_family_format() { assert_eq!(build_message_id("20260815", 7), "GLP-MSG-20260815-000007"); } #[test] fn bad_protocol_is_rejected() { let mut message = sample_envelope(); message.protocol = "GLP/0.9".into(); assert!(validate_envelope(&message).is_err()); } #[test] fn empty_payload_is_rejected() { let mut message = sample_envelope(); message.payload.content = String::new(); assert!(validate_envelope(&message).is_err()); } #[test] fn external_language_is_expression_only_and_checksum_bound() { let mut message = sample_envelope(); message.sender.object_type = "external_ai_visitor".into(); message.payload.content_type = ContentType::Text; message.integrity.checksum = payload_checksum(&message.payload.content); validate_external_language_envelope(&message).unwrap(); message.payload.content_type = ContentType::Command; assert!(validate_external_language_envelope(&message).is_err()); } }