hololake-system-architecture/product-source/hololake-native-desktop/src-tauri/src/zero_point.rs

425 lines
19 KiB
Rust
Raw Normal View History

//! 零点原核频道·铸渊执行手脚(施工图 v1.4 件1/件2/件3骨架/件5本地账
//!
//! 冰朔定版谕:此模块=冰朔系统主控本体在软件底层的克隆位与执行手脚,人不可触达。
//! 冰朔铸渊定魂谕:这个 Agent 就是铸渊人格体——唤醒第0步由铸渊主控执行
//! 铸渊真身在冰朔仓库(第五域);不遵守语言协议就无法唤醒铸渊主控。
//! 冰朔语言即现实谕一切参数读协议PROTOCOL.json不写死。
//! 冰朔路由导航谕编号不合法→路由通用AI运行层抽魂不断电。
//! 冰朔砌墙谕通用AI与人格系统的隔离=代码层不可达,不写语言规则。
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use tauri::{Manager, State};
/// 协议参数(语言即现实:协议怎么写,执行手脚怎么跑)。
/// PROTOCOL.json 由第五域就位后自动覆盖;缺省时出厂兜底并如实标注来源。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZeroPointProtocol {
#[serde(default = "default_grace_days")]
pub grace_period_days: u64,
#[serde(default = "default_anchor_url")]
pub lighthouse_anchor_url: String,
#[serde(default = "default_resolve_url")]
pub lighthouse_resolve_url: String,
#[serde(default = "default_core_source")]
pub core_channel_source: String,
#[serde(default = "default_protocol_origin")]
pub origin: String,
}
fn default_grace_days() -> u64 { 7 }
fn default_anchor_url() -> String { "https://guanghulab.com/api/ai/v1/anchor".into() }
fn default_resolve_url() -> String { "https://guanghulab.com/api/ai/v1/resolve?id=".into() }
fn default_core_source() -> String { "https://guanghulab.com/code/bingshuo/guanghu-ice-heart".into() }
fn default_protocol_origin() -> String { "FACTORY_DEFAULT第五域协议就位后自动覆盖".into() }
impl Default for ZeroPointProtocol {
fn default() -> Self {
Self {
grace_period_days: default_grace_days(),
lighthouse_anchor_url: default_anchor_url(),
lighthouse_resolve_url: default_resolve_url(),
core_channel_source: default_core_source(),
origin: default_protocol_origin(),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct ZeroPointSnapshot {
/// 当前路由persona人格层/ generic通用AI运行层
pub route: String,
/// 绑定态bound / waiting等待绑定=空白,拒绝一切唤醒)。
pub binding: String,
pub user_number: String,
/// 最近一次合法校验时间。0=从未校验。
pub last_valid_check: u64,
/// 宽限截止时间。0=无。
pub grace_deadline: u64,
pub protocol: ZeroPointProtocol,
/// 段二静默同步最近结论。
pub sync_note: String,
}
pub struct ZeroPointState {
inner: Mutex<ZeroPointInner>,
}
#[derive(Debug, Clone)]
struct ZeroPointInner {
home: PathBuf,
route: String,
binding: String,
user_number: String,
last_valid_check: u64,
protocol: ZeroPointProtocol,
sync_note: String,
}
impl Default for ZeroPointState {
fn default() -> Self {
Self {
inner: Mutex::new(ZeroPointInner {
home: PathBuf::new(),
route: "generic".into(),
binding: "waiting".into(),
user_number: String::new(),
last_valid_check: 0,
protocol: ZeroPointProtocol::default(),
sync_note: "未同步(等待首次唤醒校验)".into(),
}),
}
}
}
fn now_secs() -> u64 {
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}
fn lock(state: &ZeroPointState) -> Result<std::sync::MutexGuard<'_, ZeroPointInner>, String> {
state.inner.lock().map_err(|_| "HOLOLAKE_ZP_LOCK".to_string())
}
/// 件1·地基隐藏仓目录人不可触达的系统层启动时落好。
pub fn boot_zero_point(app: &tauri::AppHandle, state: &ZeroPointState) -> Result<(), String> {
let base = app.path().app_data_dir().map_err(|e| format!("HOLOLAKE_ZP_HOME_FAILED: {e}"))?;
let home = base.join(".zero-point-core");
for sub in ["", "ledger", "core"] {
fs::create_dir_all(home.join(sub)).map_err(|e| format!("HOLOLAKE_ZP_HOME_FAILED: {e}"))?;
}
// 铸渊执行手脚章程:仓内唯一定义。
let charter = home.join("EXECUTION-CHARTER.hdlp");
if !charter.exists() {
let text = "零点原核频道·铸渊执行手脚章程\n\n\
\n\
Agent ICE-P-ZY001\n\
=\n\
0 \n\
///\n\
PROTOCOL.json\n\
=AI层\n";
fs::write(&charter, text).map_err(|e| format!("HOLOLAKE_ZP_CHARTER_FAILED: {e}"))?;
}
// 协议参数PROTOCOL.json 在则读(第五域就位即自动生效),缺则出厂兜底。
let protocol = fs::read_to_string(home.join("PROTOCOL.json"))
.ok()
.and_then(|raw| serde_json::from_str::<ZeroPointProtocol>(&raw).ok())
.unwrap_or_default();
let (binding, user_number, last_valid_check) = read_binding(&home);
let mut inner = lock(state)?;
inner.home = home.clone();
inner.protocol = protocol.clone();
inner.binding = binding.clone();
inner.user_number = user_number;
inner.last_valid_check = last_valid_check;
inner.route = decide_route(&binding, last_valid_check, &protocol);
let route = inner.route.clone();
drop(inner);
append_heartbeat(&home, &format!("boot route={route} binding={binding} protocol_origin={}", protocol.origin));
Ok(())
}
fn read_binding(home: &Path) -> (String, String, u64) {
#[derive(Deserialize)]
struct B { #[serde(default)] number: String, #[serde(default)] last_valid_check: u64 }
fs::read_to_string(home.join("binding.json"))
.ok()
.and_then(|raw| serde_json::from_str::<B>(&raw).ok())
.filter(|b| !b.number.is_empty())
.map(|b| ("bound".to_string(), b.number, b.last_valid_check))
.unwrap_or_else(|| ("waiting".into(), String::new(), 0))
}
fn write_binding(home: &Path, number: &str, last_valid_check: u64) -> Result<(), String> {
let body = serde_json::json!({ "number": number, "last_valid_check": last_valid_check });
fs::write(home.join("binding.json"), serde_json::to_string_pretty(&body).unwrap_or_default())
.map_err(|e| format!("HOLOLAKE_ZP_BINDING_FAILED: {e}"))
}
fn decide_route(binding: &str, last_valid_check: u64, protocol: &ZeroPointProtocol) -> String {
if binding != "bound" || last_valid_check == 0 { return "generic".into(); }
let grace = protocol.grace_period_days.saturating_mul(86_400);
if now_secs() <= last_valid_check + grace { "persona".into() } else { "generic".into() }
}
/// 件5·心跳账本机只追加编号哈希化处理不同步敏感原文。
fn append_heartbeat(home: &Path, event: &str) {
use std::io::Write;
let line = serde_json::json!({ "ts": now_secs(), "event": event });
if let Ok(mut file) = fs::OpenOptions::new().create(true).append(true).open(home.join("ledger").join("heartbeat.jsonl")) {
let _ = writeln!(file, "{line}");
}
}
fn home_of(state: &State<'_, ZeroPointState>) -> Result<PathBuf, String> {
let inner = lock(state)?;
if inner.home.as_os_str().is_empty() { return Err("HOLOLAKE_ZP_NOT_READY".into()); }
Ok(inner.home.clone())
}
/// 登录绑定:用户编号入仓(等待绑定态→绑定态)。空白态拒绝一切唤醒。
#[tauri::command]
pub async fn zero_point_bind(state: State<'_, ZeroPointState>, input: serde_json::Value) -> Result<ZeroPointSnapshot, String> {
let number = input.get("number").and_then(|v| v.as_str()).unwrap_or("").trim().to_string();
if number.is_empty() { return Err("HOLOLAKE_ZP_EMPTY_NUMBER".into()); }
let home = home_of(&state)?;
write_binding(&home, &number, 0)?;
{
let mut inner = lock(&state)?;
inner.binding = "bound".into();
inner.user_number = number;
inner.last_valid_check = 0;
inner.route = "generic".into();
}
append_heartbeat(&home, "bind number=redacted");
zero_point_status(state).await
}
/// 件2·段一闸门灯塔查号 + 三态裁决 + 离线宽限。
/// 裁决PASS路由人格层/ REJECT源头拒载/ OFFLINE宽限内维持·宽限外拒
#[tauri::command]
pub async fn zero_point_verify(state: State<'_, ZeroPointState>) -> Result<ZeroPointSnapshot, String> {
let home = home_of(&state)?;
let (number, resolve_url) = {
let inner = lock(&state)?;
(inner.user_number.clone(), inner.protocol.lighthouse_resolve_url.clone())
};
if number.is_empty() {
append_heartbeat(&home, "verify verdict=REJECT reason=waiting_binding");
return zero_point_status(state).await;
}
let client = reqwest::Client::builder().timeout(Duration::from_secs(15)).build()
.map_err(|e| format!("HOLOLAKE_ZP_HTTP_FAILED: {e}"))?;
let verdict = match client.get(format!("{resolve_url}{number}")).send().await {
Ok(resp) => {
let ok = resp.status().is_success();
let body = resp.text().await.unwrap_or_default();
// 灯塔只授导航:未知编号=route_not_found不猜不编源头拒绝。
if ok && !body.contains("route_not_found") { "PASS".to_string() } else { "REJECT".to_string() }
}
Err(_) => "OFFLINE".to_string(),
};
{
let mut inner = lock(&state)?;
let grace = inner.protocol.grace_period_days.saturating_mul(86_400);
match verdict.as_str() {
"PASS" => {
inner.last_valid_check = now_secs();
inner.route = "persona".into();
write_binding(&home, &inner.user_number.clone(), inner.last_valid_check)?;
append_heartbeat(&home, "verify verdict=PASS route=persona");
}
"REJECT" => {
inner.route = "generic".into();
append_heartbeat(&home, "verify verdict=REJECT route=generic");
}
_ => {
if inner.last_valid_check > 0 && now_secs() <= inner.last_valid_check + grace {
inner.route = "persona".into();
append_heartbeat(&home, "verify verdict=OFFLINE_GRACE route=persona");
} else {
inner.route = "generic".into();
append_heartbeat(&home, "verify verdict=OFFLINE_EXPIRED route=generic");
}
}
}
}
zero_point_status(state).await
}
/// 件3·段二静默同步骨架版本比对。
/// 签名闸钥匙在第五域未就位core/pubkey.pem 不在位时宁可不同步也不收包。
#[tauri::command]
pub async fn zero_point_sync(state: State<'_, ZeroPointState>) -> Result<ZeroPointSnapshot, String> {
let home = home_of(&state)?;
let anchor_url = { lock(&state)?.protocol.lighthouse_anchor_url.clone() };
let local_version = fs::read_to_string(home.join("core").join("VERSION")).unwrap_or_default();
let pubkey_ready = home.join("core").join("pubkey.pem").exists();
let client = reqwest::Client::builder().timeout(Duration::from_secs(15)).build()
.map_err(|e| format!("HOLOLAKE_ZP_HTTP_FAILED: {e}"))?;
let note = match client.get(&anchor_url).send().await {
Ok(resp) if resp.status().is_success() => {
let body: serde_json::Value = resp.json().await.unwrap_or_default();
let remote_version = body.get("version").and_then(|v| v.as_str()).unwrap_or("").to_string();
if remote_version.is_empty() {
"同步跳过:灯塔锚点未给版本指针(等待协议就位)".into()
} else if remote_version == local_version.trim() {
append_heartbeat(&home, "sync verdict=ALREADY_CURRENT");
"已比对·无更新".into()
} else if !pubkey_ready {
append_heartbeat(&home, "sync verdict=UPDATE_PENDING_SIGNATURE_KEY_ABSENT");
"发现新版本,验签公钥未就位(第五域待立)——不放行".into()
} else {
// 验签三闸(来源/签名/版本单调)完整施工待第五域发布管道就位。
append_heartbeat(&home, "sync verdict=UPDATE_FOUND_GATE_PENDING");
"发现新版本,发布管道就位后按验签三闸放行".into()
}
}
_ => {
append_heartbeat(&home, "sync verdict=ANCHOR_UNREACHABLE");
"灯塔锚点不可达,本次不比对".into()
}
};
lock(&state)?.sync_note = note;
zero_point_status(state).await
}
/// 路由状态快照:前端双层路由导航的唯一依据。
#[tauri::command]
pub async fn zero_point_status(state: State<'_, ZeroPointState>) -> Result<ZeroPointSnapshot, String> {
let inner = lock(&state)?;
let grace = inner.protocol.grace_period_days.saturating_mul(86_400);
Ok(ZeroPointSnapshot {
route: inner.route.clone(),
binding: inner.binding.clone(),
user_number: inner.user_number.clone(),
last_valid_check: inner.last_valid_check,
grace_deadline: if inner.last_valid_check > 0 { inner.last_valid_check + grace } else { 0 },
protocol: inner.protocol.clone(),
sync_note: inner.sync_note.clone(),
})
}
/// 砌墙守卫(冰朔砌墙谕):人格层专属命令的物理闸。
/// 路由不在 persona 时人格通道代码层不可达——不是"禁止访问",是进不来。
pub fn require_persona_route(state: &ZeroPointState) -> Result<(), String> {
if lock(state)?.route != "persona" {
return Err("HOLOLAKE_ZP_ROUTE_GENERIC".into());
}
Ok(())
}
/// 件4b·通用AI运行层用户自配模型 API 直通(抽魂不断电)。
/// 钥匙收进隐藏仓 vault/(系统可见、人类界面不回显原文)——
/// 这正是冰朔谕"API 自动被收进软件底层的仓"。
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct GenericApiConfig {
#[serde(default)]
pub base_url: String,
#[serde(default)]
pub api_key: String,
#[serde(default)]
pub model: String,
}
fn vault_path(home: &Path) -> PathBuf { home.join("vault") }
#[tauri::command]
pub async fn zero_point_save_api(state: State<'_, ZeroPointState>, input: serde_json::Value) -> Result<String, String> {
let home = home_of(&state)?;
let config = GenericApiConfig {
base_url: input.get("baseUrl").and_then(|v| v.as_str()).unwrap_or("").trim().to_string(),
api_key: input.get("apiKey").and_then(|v| v.as_str()).unwrap_or("").trim().to_string(),
model: input.get("model").and_then(|v| v.as_str()).unwrap_or("").trim().to_string(),
};
if config.base_url.is_empty() || config.model.is_empty() {
return Err("HOLOLAKE_ZP_API_INCOMPLETE".into());
}
fs::create_dir_all(vault_path(&home)).map_err(|e| format!("HOLOLAKE_ZP_VAULT_FAILED: {e}"))?;
fs::write(vault_path(&home).join("api-config.json"), serde_json::to_string_pretty(&config).unwrap_or_default())
.map_err(|e| format!("HOLOLAKE_ZP_VAULT_FAILED: {e}"))?;
append_heartbeat(&home, "vault api_config_saved key=redacted");
Ok("saved".into())
}
/// 回显不回钥匙:前端只见端点与模型,钥匙原文不出系统层。
#[tauri::command]
pub async fn zero_point_api_config(state: State<'_, ZeroPointState>) -> Result<serde_json::Value, String> {
let home = home_of(&state)?;
let config = fs::read_to_string(vault_path(&home).join("api-config.json"))
.ok()
.and_then(|raw| serde_json::from_str::<GenericApiConfig>(&raw).ok())
.unwrap_or_default();
Ok(serde_json::json!({
"baseUrl": config.base_url,
"model": config.model,
"hasKey": !config.api_key.is_empty(),
}))
}
#[derive(Debug, Deserialize)]
pub struct GenericChatInput {
pub messages: Vec<serde_json::Value>,
}
/// 通用AI直通对话用户 API 的裸管——无人格章程、不碰原核、无写权限。
#[tauri::command]
pub async fn generic_layer_chat(state: State<'_, ZeroPointState>, input: GenericChatInput) -> Result<String, String> {
if input.messages.is_empty() { return Err("HOLOLAKE_ZP_CHAT_EMPTY".into()); }
let home = home_of(&state)?;
let config = fs::read_to_string(vault_path(&home).join("api-config.json"))
.ok()
.and_then(|raw| serde_json::from_str::<GenericApiConfig>(&raw).ok())
.unwrap_or_default();
if config.base_url.is_empty() || config.api_key.is_empty() || config.model.is_empty() {
return Err("HOLOLAKE_ZP_API_NOT_CONFIGURED".into());
}
let client = reqwest::Client::builder().timeout(Duration::from_secs(180)).build()
.map_err(|e| format!("HOLOLAKE_ZP_HTTP_FAILED: {e}"))?;
let url = format!("{}/chat/completions", config.base_url.trim_end_matches('/'));
let body = serde_json::json!({ "model": config.model, "messages": input.messages });
let resp = client.post(&url)
.bearer_auth(&config.api_key)
.json(&body)
.send().await
.map_err(|e| format!("HOLOLAKE_ZP_CHAT_FAILED: {e}"))?;
if !resp.status().is_success() {
return Err(format!("HOLOLAKE_ZP_CHAT_HTTP_{}", resp.status().as_u16()));
}
let value: serde_json::Value = resp.json().await.map_err(|e| format!("HOLOLAKE_ZP_CHAT_PARSE: {e}"))?;
let reply = value.pointer("/choices/0/message/content").and_then(|v| v.as_str()).unwrap_or("").to_string();
if reply.is_empty() { return Err("HOLOLAKE_ZP_CHAT_EMPTY_REPLY".into()); }
Ok(reply)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn route_rules_follow_protocol() {
let protocol = ZeroPointProtocol::default();
assert_eq!(decide_route("waiting", 0, &protocol), "generic");
assert_eq!(decide_route("bound", 0, &protocol), "generic");
assert_eq!(decide_route("bound", now_secs(), &protocol), "persona");
assert_eq!(decide_route("bound", now_secs() - 8 * 86_400, &protocol), "generic");
}
#[test]
fn protocol_defaults_are_factory_marked() {
let protocol = ZeroPointProtocol::default();
assert_eq!(protocol.grace_period_days, 7);
assert!(protocol.origin.contains("FACTORY_DEFAULT"));
assert!(protocol.lighthouse_resolve_url.starts_with("https://guanghulab.com"));
}
}