ZY-TAKEOVER-SNAPSHOT 20260815: 铸渊接管前现场冻结(Codex 未提交改动全部入册)· 冰朔面谕接管+可回退

This commit is contained in:
冰朔 2026-08-15 15:02:35 +08:00
commit ffed065841
22 changed files with 4293 additions and 567 deletions

View file

@ -0,0 +1,929 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use serde::{Deserialize, Serialize};
use std::cmp::Reverse;
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::path::{Component, Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::{AppHandle, Manager};
use tauri_plugin_dialog::DialogExt;
use url::Url;
use uuid::Uuid;
const SNAPSHOT_SCHEMA: &str = "hololake.code-channel/v1";
const REGISTRY_SCHEMA: &str = "hololake.code-channel-registry/v1";
const ALLOWED_HOSTS: &[&str] = &["guanghulab.com", "guanghubingshuo.com"];
const MAX_TREE_ENTRIES: usize = 1_000;
const MAX_CODE_FILE_BYTES: u64 = 2 * 1024 * 1024;
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CloneCodeChannelInput {
pub url: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CodeChannelEntry {
pub channel_id: String,
pub name: String,
pub source_kind: String,
pub local_path: String,
pub remote_url: Option<String>,
pub git_head: String,
pub branch: String,
pub repository_clean: bool,
pub registered_at_unix_ms: u128,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeChannelSnapshot {
pub schema: &'static str,
pub state: &'static str,
pub storage_root: String,
pub channels: Vec<CodeChannelEntry>,
pub authority: &'static str,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BrowseCodeChannelInput {
pub channel_id: String,
pub path: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeTreeEntry {
pub path: String,
pub name: String,
pub kind: &'static str,
pub size_bytes: u64,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeTreeSnapshot {
pub schema: &'static str,
pub channel_id: String,
pub path: String,
pub entries: Vec<CodeTreeEntry>,
pub truncated: bool,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ReadCodeChannelFileInput {
pub channel_id: String,
pub path: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeFileProjection {
pub schema: &'static str,
pub channel_id: String,
pub path: String,
pub format: String,
pub source: String,
pub human_markdown: String,
pub size_bytes: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct CodeChannelRegistry {
schema: String,
channels: Vec<CodeChannelEntry>,
}
#[derive(Clone, Debug)]
struct ValidatedCloneUrl {
normalized: String,
owner: String,
repository: String,
}
#[tauri::command]
pub async fn get_code_channel_snapshot(app: AppHandle) -> Result<CodeChannelSnapshot, String> {
let root = code_channel_root(&app)?;
tauri::async_runtime::spawn_blocking(move || snapshot_at(&root))
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn clone_code_channel(
app: AppHandle,
input: CloneCodeChannelInput,
) -> Result<CodeChannelSnapshot, String> {
let root = code_channel_root(&app)?;
tauri::async_runtime::spawn_blocking(move || clone_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn select_local_code_channel(
app: AppHandle,
) -> Result<Option<CodeChannelSnapshot>, String> {
let picker = app.clone();
let selected = tauri::async_runtime::spawn_blocking(move || {
picker
.dialog()
.file()
.set_title("选择本地 Git 代码频道")
.blocking_pick_folder()
})
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PICKER_JOIN_FAILED: {error}"))?;
let Some(selected) = selected else {
return Ok(None);
};
let selected = selected
.into_path()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PICKER_PATH_INVALID: {error}"))?;
let root = code_channel_root(&app)?;
tauri::async_runtime::spawn_blocking(move || register_local_at(&root, &selected).map(Some))
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn browse_code_channel(
app: AppHandle,
input: BrowseCodeChannelInput,
) -> Result<CodeTreeSnapshot, String> {
let root = code_channel_root(&app)?;
tauri::async_runtime::spawn_blocking(move || browse_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn read_code_channel_file(
app: AppHandle,
input: ReadCodeChannelFileInput,
) -> Result<CodeFileProjection, String> {
let root = code_channel_root(&app)?;
tauri::async_runtime::spawn_blocking(move || read_code_file_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))?
}
fn code_channel_root(app: &AppHandle) -> Result<PathBuf, String> {
let root = app
.path()
.app_data_dir()
.map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?
.join("code-channel-v1");
ensure_root(&root)?;
Ok(root)
}
fn ensure_root(root: &Path) -> Result<(), String> {
fs::create_dir_all(root.join("repositories"))
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_STORAGE_UNAVAILABLE: {error}"))?;
fs::set_permissions(root, fs::Permissions::from_mode(0o700))
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PERMISSION_FAILED: {error}"))?;
Ok(())
}
fn snapshot_at(root: &Path) -> Result<CodeChannelSnapshot, String> {
ensure_root(root)?;
let mut registry = read_registry(root)?;
for channel in &mut registry.channels {
if let Ok(inspection) = inspect_repository(Path::new(&channel.local_path)) {
channel.git_head = inspection.git_head;
channel.branch = inspection.branch;
channel.repository_clean = inspection.repository_clean;
}
}
registry
.channels
.sort_by_key(|entry| Reverse(entry.registered_at_unix_ms));
Ok(CodeChannelSnapshot {
schema: SNAPSHOT_SCHEMA,
state: "READY",
storage_root: root.to_string_lossy().into_owned(),
channels: registry.channels,
authority: "LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY",
})
}
fn browse_at(root: &Path, input: BrowseCodeChannelInput) -> Result<CodeTreeSnapshot, String> {
let (channel, repository) = registered_repository(root, &input.channel_id)?;
let relative = input.path.unwrap_or_default();
let directory = safe_repository_path(&repository, &relative)?;
if !directory.is_dir() {
return Err("HOLOLAKE_CODE_CHANNEL_DIRECTORY_INVALID".into());
}
let mut entries = fs::read_dir(&directory)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?
.collect::<Result<Vec<_>, _>>()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?;
entries.sort_by_key(|entry| {
let is_file = entry.file_type().map(|kind| kind.is_file()).unwrap_or(true);
(is_file, entry.file_name())
});
let mut projected = Vec::new();
for entry in entries {
if projected.len() >= MAX_TREE_ENTRIES {
break;
}
let name = entry.file_name().to_string_lossy().into_owned();
if name == ".git" || name == ".DS_Store" || name.starts_with("._") {
continue;
}
let kind = entry
.file_type()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?;
if kind.is_symlink() || (!kind.is_dir() && !kind.is_file()) {
continue;
}
let entry_path = entry.path();
let relative_path = relative_posix(&repository, &entry_path)?;
let size_bytes = if kind.is_file() {
entry
.metadata()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?
.len()
} else {
0
};
projected.push(CodeTreeEntry {
path: relative_path,
name,
kind: if kind.is_dir() { "directory" } else { "file" },
size_bytes,
});
}
Ok(CodeTreeSnapshot {
schema: "hololake.code-channel-tree/v1",
channel_id: channel.channel_id,
path: relative,
truncated: projected.len() >= MAX_TREE_ENTRIES,
entries: projected,
})
}
fn read_code_file_at(
root: &Path,
input: ReadCodeChannelFileInput,
) -> Result<CodeFileProjection, String> {
let (channel, repository) = registered_repository(root, &input.channel_id)?;
let file = safe_repository_path(&repository, &input.path)?;
let metadata = fs::metadata(&file)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_FILE_UNAVAILABLE: {error}"))?;
if !metadata.is_file() || metadata.len() > MAX_CODE_FILE_BYTES || !is_supported_text_file(&file)
{
return Err("HOLOLAKE_CODE_CHANNEL_FILE_UNSUPPORTED".into());
}
let source = fs::read_to_string(&file)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_FILE_INVALID_UTF8: {error}"))?;
let format = file_format(&file);
let human_markdown = human_projection(&input.path, &format, &source);
Ok(CodeFileProjection {
schema: "hololake.code-channel-file-projection/v1",
channel_id: channel.channel_id,
path: input.path,
format,
source,
human_markdown,
size_bytes: metadata.len(),
})
}
fn registered_repository(
root: &Path,
channel_id: &str,
) -> Result<(CodeChannelEntry, PathBuf), String> {
let registry = read_registry(root)?;
let channel = registry
.channels
.into_iter()
.find(|entry| entry.channel_id == channel_id)
.ok_or("HOLOLAKE_CODE_CHANNEL_NOT_REGISTERED")?;
let repository = PathBuf::from(&channel.local_path)
.canonicalize()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REPOSITORY_UNAVAILABLE: {error}"))?;
if !repository.is_dir() {
return Err("HOLOLAKE_CODE_CHANNEL_REPOSITORY_UNAVAILABLE".into());
}
Ok((channel, repository))
}
fn safe_repository_path(root: &Path, relative: &str) -> Result<PathBuf, String> {
let relative_path = Path::new(relative);
if relative_path.is_absolute()
|| relative_path
.components()
.any(|component| !matches!(component, Component::Normal(_)))
{
if relative.is_empty() {
return Ok(root.to_path_buf());
}
return Err("HOLOLAKE_CODE_CHANNEL_PATH_INVALID".into());
}
if relative.is_empty() {
return Ok(root.to_path_buf());
}
let candidate = root.join(relative_path);
let metadata = fs::symlink_metadata(&candidate)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PATH_UNAVAILABLE: {error}"))?;
if metadata.file_type().is_symlink() {
return Err("HOLOLAKE_CODE_CHANNEL_SYMLINK_DENIED".into());
}
let canonical = candidate
.canonicalize()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PATH_UNAVAILABLE: {error}"))?;
if !canonical.starts_with(root) {
return Err("HOLOLAKE_CODE_CHANNEL_PATH_ESCAPE".into());
}
Ok(canonical)
}
fn relative_posix(root: &Path, path: &Path) -> Result<String, String> {
Ok(path
.strip_prefix(root)
.map_err(|_| "HOLOLAKE_CODE_CHANNEL_PATH_INVALID".to_string())?
.to_string_lossy()
.replace('\\', "/"))
}
fn is_supported_text_file(path: &Path) -> bool {
let name = path
.file_name()
.and_then(|value| value.to_str())
.unwrap_or("");
if ["README", "LICENSE", "Dockerfile", "Makefile", "Procfile"].contains(&name) {
return true;
}
matches!(
path.extension()
.and_then(|value| value.to_str())
.unwrap_or("")
.to_ascii_lowercase()
.as_str(),
"md" | "markdown"
| "txt"
| "json"
| "jsonl"
| "yaml"
| "yml"
| "toml"
| "hdlp"
| "rs"
| "ts"
| "tsx"
| "js"
| "jsx"
| "mjs"
| "cjs"
| "py"
| "sh"
| "zsh"
| "bash"
| "css"
| "scss"
| "html"
| "xml"
| "sql"
| "graphql"
| "go"
| "java"
| "kt"
| "swift"
| "c"
| "h"
| "cpp"
| "hpp"
| "proto"
| "ini"
| "conf"
| "env"
)
}
fn file_format(path: &Path) -> String {
path.extension()
.and_then(|value| value.to_str())
.filter(|value| !value.is_empty())
.unwrap_or("text")
.to_ascii_lowercase()
}
fn human_projection(path: &str, format: &str, source: &str) -> String {
if matches!(format, "md" | "markdown") {
return source.to_string();
}
let title = Path::new(path)
.file_name()
.and_then(|value| value.to_str())
.unwrap_or("代码文件");
if format == "json" {
if let Ok(value) = serde_json::from_str::<serde_json::Value>(source) {
return format!(
"# {title}\n\n> 来自代码频道的 JSON 结构,已转换为可读层级。\n\n{}",
json_as_markdown(&value, 2)
);
}
}
if matches!(format, "hdlp" | "yaml" | "yml" | "toml" | "ini" | "conf") {
let rows = source
.lines()
.filter_map(|line| {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
return None;
}
trimmed
.split_once(':')
.or_else(|| trimmed.split_once('='))
.map(|(key, value)| format!("| {} | {} |", key.trim(), value.trim()))
})
.take(120)
.collect::<Vec<_>>();
if !rows.is_empty() {
return format!(
"# {title}\n\n> 来自代码频道的结构化文件。原始机器字段已转成阅读表格。\n\n| 字段 | 内容 |\n| --- | --- |\n{}",
rows.join("\n")
);
}
}
format!(
"# {title}\n\n> 来自代码频道的 `{format}` 文件。下方保留原始内容,便于核验。\n\n```{format}\n{source}\n```"
)
}
fn json_as_markdown(value: &serde_json::Value, level: usize) -> String {
match value {
serde_json::Value::Object(map) => map
.iter()
.map(|(key, value)| match value {
serde_json::Value::Object(_) | serde_json::Value::Array(_) => format!(
"{} {key}\n\n{}",
"#".repeat(level.min(5)),
json_as_markdown(value, level + 1)
),
_ => format!("- **{key}**{}", scalar_json(value)),
})
.collect::<Vec<_>>()
.join("\n\n"),
serde_json::Value::Array(values) => values
.iter()
.enumerate()
.map(|(index, value)| match value {
serde_json::Value::Object(_) | serde_json::Value::Array(_) => {
format!("{}. {}", index + 1, json_as_markdown(value, level + 1))
}
_ => format!("- {}", scalar_json(value)),
})
.collect::<Vec<_>>()
.join("\n"),
_ => scalar_json(value),
}
}
fn scalar_json(value: &serde_json::Value) -> String {
value
.as_str()
.map(ToOwned::to_owned)
.unwrap_or_else(|| value.to_string())
}
fn clone_at(root: &Path, input: CloneCodeChannelInput) -> Result<CodeChannelSnapshot, String> {
ensure_root(root)?;
let validated = validate_clone_url(&input.url)?;
let destination = root.join("repositories").join(format!(
"{}--{}",
safe_segment(&validated.owner),
safe_segment(&validated.repository)
));
if destination.exists() {
return Err("HOLOLAKE_CODE_CHANNEL_DESTINATION_EXISTS".into());
}
let output = Command::new("/usr/bin/git")
.env("GIT_TERMINAL_PROMPT", "0")
.env("GIT_ASKPASS", "/usr/bin/false")
.env("SSH_ASKPASS", "/usr/bin/false")
.env("GIT_CONFIG_NOSYSTEM", "1")
.args([
"-c",
"credential.helper=",
"-c",
"core.askPass=",
"clone",
"--origin",
"origin",
"--no-tags",
])
.arg(&validated.normalized)
.arg(&destination)
.output()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_CLONE_FAILED: {error}"))?;
if !output.status.success() {
if destination.starts_with(root.join("repositories")) {
let _ = fs::remove_dir_all(&destination);
}
return Err(format!(
"HOLOLAKE_CODE_CHANNEL_CLONE_FAILED: {}",
String::from_utf8_lossy(&output.stderr).trim()
));
}
let entry = entry_for_repository(
&destination,
"CLONED_HTTPS",
Some(validated.normalized),
Some(format!("{}/{}", validated.owner, validated.repository)),
)?;
upsert_entry(root, entry)?;
snapshot_at(root)
}
fn register_local_at(root: &Path, selected: &Path) -> Result<CodeChannelSnapshot, String> {
ensure_root(root)?;
let canonical = selected
.canonicalize()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_LOCAL_UNAVAILABLE: {error}"))?;
if !canonical.is_dir() {
return Err("HOLOLAKE_CODE_CHANNEL_LOCAL_INVALID".into());
}
let remote = git_optional(&canonical, &["remote", "get-url", "origin"])
.and_then(|value| redact_remote_url(value.trim()));
let entry = entry_for_repository(&canonical, "LOCAL_GIT", remote, None)?;
upsert_entry(root, entry)?;
snapshot_at(root)
}
struct RepositoryInspection {
git_head: String,
branch: String,
repository_clean: bool,
}
fn inspect_repository(path: &Path) -> Result<RepositoryInspection, String> {
let inside = git(path, &["rev-parse", "--is-inside-work-tree"], "INSPECT")?;
if inside.trim() != "true" {
return Err("HOLOLAKE_CODE_CHANNEL_NOT_GIT".into());
}
let git_head = git(path, &["rev-parse", "HEAD"], "READ_HEAD")?
.trim()
.to_string();
let branch = git_optional(path, &["symbolic-ref", "--short", "HEAD"])
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| "DETACHED".into());
let repository_clean = git(path, &["status", "--porcelain"], "READ_STATUS")?
.trim()
.is_empty();
Ok(RepositoryInspection {
git_head,
branch,
repository_clean,
})
}
fn entry_for_repository(
path: &Path,
source_kind: &str,
remote_url: Option<String>,
explicit_name: Option<String>,
) -> Result<CodeChannelEntry, String> {
let inspection = inspect_repository(path)?;
let name = explicit_name.unwrap_or_else(|| {
path.file_name()
.and_then(|value| value.to_str())
.unwrap_or("代码频道")
.to_string()
});
Ok(CodeChannelEntry {
channel_id: format!("channel-{}", Uuid::new_v4()),
name,
source_kind: source_kind.into(),
local_path: path.to_string_lossy().into_owned(),
remote_url,
git_head: inspection.git_head,
branch: inspection.branch,
repository_clean: inspection.repository_clean,
registered_at_unix_ms: now_unix_ms()?,
})
}
fn validate_clone_url(raw: &str) -> Result<ValidatedCloneUrl, String> {
let raw = raw.trim();
if raw.len() > 2_048 {
return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into());
}
let mut parsed = Url::parse(raw).map_err(|_| "HOLOLAKE_CODE_CHANNEL_URL_INVALID")?;
if parsed.scheme() != "https"
|| !parsed.username().is_empty()
|| parsed.password().is_some()
|| parsed.query().is_some()
|| parsed.fragment().is_some()
|| parsed.port().is_some()
{
return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into());
}
let host = parsed
.host_str()
.ok_or("HOLOLAKE_CODE_CHANNEL_URL_INVALID")?
.to_lowercase();
if !ALLOWED_HOSTS.contains(&host.as_str()) {
return Err("HOLOLAKE_CODE_CHANNEL_HOST_NOT_TRUSTED".into());
}
let segments = parsed
.path_segments()
.ok_or("HOLOLAKE_CODE_CHANNEL_URL_INVALID")?
.filter(|segment| !segment.is_empty())
.collect::<Vec<_>>();
if segments.len() != 3 || segments[0] != "code" {
return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into());
}
let owner = segments[1].to_string();
let repository = segments[2]
.strip_suffix(".git")
.unwrap_or(segments[2])
.to_string();
if !valid_git_segment(&owner) || !valid_git_segment(&repository) {
return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into());
}
parsed.set_path(&format!("/code/{owner}/{repository}.git"));
Ok(ValidatedCloneUrl {
normalized: parsed.to_string(),
owner,
repository,
})
}
fn valid_git_segment(value: &str) -> bool {
!value.is_empty()
&& value.len() <= 100
&& value != "."
&& value != ".."
&& value
.chars()
.all(|character| character.is_ascii_alphanumeric() || "-_.".contains(character))
}
fn redact_remote_url(raw: &str) -> Option<String> {
let mut parsed = Url::parse(raw).ok()?;
if parsed.scheme() != "https" {
return None;
}
parsed.set_username("").ok()?;
parsed.set_password(None).ok()?;
parsed.set_query(None);
parsed.set_fragment(None);
Some(parsed.to_string())
}
fn safe_segment(value: &str) -> String {
value
.chars()
.map(|character| {
if character.is_ascii_alphanumeric() || "-_.".contains(character) {
character
} else {
'-'
}
})
.collect()
}
fn upsert_entry(root: &Path, entry: CodeChannelEntry) -> Result<(), String> {
let mut registry = read_registry(root)?;
registry
.channels
.retain(|existing| existing.local_path != entry.local_path);
registry.channels.push(entry);
write_registry(root, &registry)
}
fn read_registry(root: &Path) -> Result<CodeChannelRegistry, String> {
let path = root.join("channels.json");
if !path.exists() {
return Ok(CodeChannelRegistry {
schema: REGISTRY_SCHEMA.into(),
channels: Vec::new(),
});
}
let bytes = fs::read(&path)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_UNAVAILABLE: {error}"))?;
let registry: CodeChannelRegistry = serde_json::from_slice(&bytes)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_INVALID: {error}"))?;
if registry.schema != REGISTRY_SCHEMA {
return Err("HOLOLAKE_CODE_CHANNEL_REGISTRY_SCHEMA_INVALID".into());
}
Ok(registry)
}
fn write_registry(root: &Path, registry: &CodeChannelRegistry) -> Result<(), String> {
let bytes = serde_json::to_vec_pretty(registry)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_INVALID: {error}"))?;
let temporary = root.join(format!("channels.json.tmp-{}", Uuid::new_v4()));
let final_path = root.join("channels.json");
let mut file = OpenOptions::new()
.create_new(true)
.write(true)
.mode(0o600)
.open(&temporary)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?;
fs::rename(&temporary, &final_path)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?;
Ok(())
}
fn git(root: &Path, args: &[&str], operation: &str) -> Result<String, String> {
let output = Command::new("/usr/bin/git")
.current_dir(root)
.env("GIT_TERMINAL_PROMPT", "0")
.args(args)
.output()
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_GIT_{operation}_FAILED: {error}"))?;
if !output.status.success() {
return Err(format!(
"HOLOLAKE_CODE_CHANNEL_GIT_{operation}_FAILED: {}",
String::from_utf8_lossy(&output.stderr).trim()
));
}
String::from_utf8(output.stdout)
.map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_GIT_{operation}_INVALID_UTF8: {error}"))
}
fn git_optional(root: &Path, args: &[&str]) -> Option<String> {
let output = Command::new("/usr/bin/git")
.current_dir(root)
.env("GIT_TERMINAL_PROMPT", "0")
.args(args)
.output()
.ok()?;
output
.status
.success()
.then(|| String::from_utf8_lossy(&output.stdout).into_owned())
}
fn now_unix_ms() -> Result<u128, String> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis())
.map_err(|error| format!("HOLOLAKE_CLOCK_INVALID: {error}"))
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn clone_url_is_normalized_to_official_https_git_endpoint() {
let parsed =
validate_clone_url("https://guanghulab.com/code/bingshuo/guanghu-ice-heart").unwrap();
assert_eq!(
parsed.normalized,
"https://guanghulab.com/code/bingshuo/guanghu-ice-heart.git"
);
assert_eq!(parsed.owner, "bingshuo");
assert_eq!(parsed.repository, "guanghu-ice-heart");
}
#[test]
fn clone_url_rejects_credentials_queries_and_unknown_hosts() {
assert!(
validate_clone_url("https://user:secret@guanghulab.com/code/bingshuo/repo").is_err()
);
assert!(
validate_clone_url("https://guanghulab.com/code/bingshuo/repo?token=secret").is_err()
);
assert!(validate_clone_url("https://example.com/code/bingshuo/repo").is_err());
}
#[test]
fn remote_redaction_removes_credentials_and_fragments() {
assert_eq!(
redact_remote_url("https://user:secret@example.com/a/b.git?x=1#readme"),
Some("https://example.com/a/b.git".into())
);
assert_eq!(redact_remote_url("git@example.com:a/b.git"), None);
}
#[test]
fn local_repository_registration_is_persistent_and_deduplicated() {
let root = tempdir().unwrap();
let repository = tempdir().unwrap();
ensure_root(root.path()).unwrap();
git(
repository.path(),
&["init", "--initial-branch=main"],
"TEST_INIT",
)
.unwrap();
git(
repository.path(),
&["config", "user.name", "Test"],
"TEST_CONFIG",
)
.unwrap();
git(
repository.path(),
&["config", "user.email", "test@example.invalid"],
"TEST_CONFIG",
)
.unwrap();
fs::write(repository.path().join("README.md"), "# test\n").unwrap();
git(repository.path(), &["add", "README.md"], "TEST_ADD").unwrap();
git(
repository.path(),
&["commit", "-m", "initial"],
"TEST_COMMIT",
)
.unwrap();
register_local_at(root.path(), repository.path()).unwrap();
register_local_at(root.path(), repository.path()).unwrap();
let snapshot = snapshot_at(root.path()).unwrap();
assert_eq!(snapshot.channels.len(), 1);
assert_eq!(snapshot.channels[0].branch, "main");
assert!(snapshot.channels[0].repository_clean);
}
#[test]
fn registered_repository_can_be_browsed_and_projected_as_knowledge() {
let root = tempdir().unwrap();
let repository = tempdir().unwrap();
ensure_root(root.path()).unwrap();
git(
repository.path(),
&["init", "--initial-branch=main"],
"TEST_INIT",
)
.unwrap();
git(
repository.path(),
&["config", "user.name", "Test"],
"TEST_CONFIG",
)
.unwrap();
git(
repository.path(),
&["config", "user.email", "test@example.invalid"],
"TEST_CONFIG",
)
.unwrap();
fs::create_dir(repository.path().join("config")).unwrap();
fs::write(
repository.path().join("config/system.json"),
r#"{"name":"光湖","state":"ready","routes":["native","mcp"]}"#,
)
.unwrap();
git(repository.path(), &["add", "."], "TEST_ADD").unwrap();
git(
repository.path(),
&["commit", "-m", "initial"],
"TEST_COMMIT",
)
.unwrap();
let snapshot = register_local_at(root.path(), repository.path()).unwrap();
let channel_id = snapshot.channels[0].channel_id.clone();
let tree = browse_at(
root.path(),
BrowseCodeChannelInput {
channel_id: channel_id.clone(),
path: None,
},
)
.unwrap();
assert!(tree
.entries
.iter()
.any(|entry| entry.name == "config" && entry.kind == "directory"));
let projection = read_code_file_at(
root.path(),
ReadCodeChannelFileInput {
channel_id,
path: "config/system.json".into(),
},
)
.unwrap();
assert_eq!(projection.format, "json");
assert!(projection
.human_markdown
.contains("来自代码频道的 JSON 结构"));
assert!(projection.human_markdown.contains("**state**ready"));
}
#[test]
fn code_tree_denies_parent_escape() {
let repository = tempdir().unwrap();
assert!(safe_repository_path(repository.path(), "../secret").is_err());
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,11 @@
mod code_channel;
mod direct_local_broker;
mod direct_local_session;
mod dynamic_capability_routing;
mod home_status;
mod knowledge_base;
mod local_development_bridge;
mod personal_channel;
mod pncc_receipt_projection;
mod pncc_remote_git;
mod pncc_repository_binding;
@ -33,6 +36,20 @@ pub fn run() {
local_development_bridge::acquire_development_write_lane,
local_development_bridge::inspect_development_write_lane,
local_development_bridge::release_development_write_lane,
personal_channel::get_personal_channel_snapshot,
personal_channel::initialize_personal_channel,
personal_channel::create_personal_channel_task,
personal_channel::transition_personal_channel_task,
knowledge_base::get_knowledge_snapshot,
knowledge_base::read_knowledge_document,
knowledge_base::search_knowledge,
knowledge_base::save_knowledge_document,
knowledge_base::select_and_import_knowledge_folder,
code_channel::get_code_channel_snapshot,
code_channel::clone_code_channel,
code_channel::select_local_code_channel,
code_channel::browse_code_channel,
code_channel::read_code_channel_file,
pncc_repository_binding::inspect_mounted_pncc_repository,
pncc_repository_binding::select_pncc_repository_candidate,
pncc_repository_binding::confirm_pncc_repository_mount,

View file

@ -0,0 +1,853 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use ring::digest::{digest, SHA256};
use rusqlite::{params, Connection, OptionalExtension, Transaction, TransactionBehavior};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tauri::{AppHandle, Manager};
use uuid::Uuid;
const KERNEL_SCHEMA: &str = "hololake.personal-channel-kernel/v1";
const DATABASE_SCHEMA_VERSION: i64 = 1;
const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000";
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializePersonalChannelInput {
pub display_name: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreatePersonalChannelTaskInput {
pub title: String,
pub purpose: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransitionPersonalChannelTaskInput {
pub task_id: String,
pub expected_status: String,
pub next_status: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonalChannelIdentity {
pub human_subject_id: String,
pub display_name: String,
pub channel_id: String,
pub created_at_unix_ms: i64,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonalChannelTask {
pub task_id: String,
pub title: String,
pub purpose: String,
pub status: String,
pub created_at_unix_ms: i64,
pub updated_at_unix_ms: i64,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonalChannelEventProjection {
pub sequence: i64,
pub event_id: String,
pub kind: String,
pub task_id: Option<String>,
pub summary: String,
pub occurred_at_unix_ms: i64,
pub event_hash: String,
pub receipt_id: String,
pub receipt_hash: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonalChannelIntegrity {
pub state: &'static str,
pub schema_version: i64,
pub event_count: i64,
pub receipt_count: i64,
pub last_event_hash: String,
pub last_receipt_hash: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonalChannelSnapshot {
pub schema: &'static str,
pub state: &'static str,
pub identity: Option<PersonalChannelIdentity>,
pub current_task: Option<PersonalChannelTask>,
pub recent_events: Vec<PersonalChannelEventProjection>,
pub integrity: PersonalChannelIntegrity,
pub storage: &'static str,
pub authority: &'static str,
}
#[derive(Serialize)]
struct EventHashPayload<'a> {
schema: &'static str,
sequence: i64,
event_id: &'a str,
human_subject_id: &'a str,
kind: &'a str,
task_id: Option<&'a str>,
summary: &'a str,
occurred_at_unix_ms: i64,
}
#[tauri::command]
pub async fn get_personal_channel_snapshot(
app: AppHandle,
) -> Result<PersonalChannelSnapshot, String> {
let database = personal_channel_database(&app)?;
tauri::async_runtime::spawn_blocking(move || snapshot_at(&database))
.await
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn initialize_personal_channel(
app: AppHandle,
input: InitializePersonalChannelInput,
) -> Result<PersonalChannelSnapshot, String> {
let database = personal_channel_database(&app)?;
tauri::async_runtime::spawn_blocking(move || initialize_at(&database, input))
.await
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn create_personal_channel_task(
app: AppHandle,
input: CreatePersonalChannelTaskInput,
) -> Result<PersonalChannelSnapshot, String> {
let database = personal_channel_database(&app)?;
tauri::async_runtime::spawn_blocking(move || create_task_at(&database, input))
.await
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn transition_personal_channel_task(
app: AppHandle,
input: TransitionPersonalChannelTaskInput,
) -> Result<PersonalChannelSnapshot, String> {
let database = personal_channel_database(&app)?;
tauri::async_runtime::spawn_blocking(move || transition_task_at(&database, input))
.await
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))?
}
fn personal_channel_database(app: &AppHandle) -> Result<PathBuf, String> {
let app_data = app
.path()
.app_data_dir()
.map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?;
let root = app_data.join("personal-channel-v1");
create_private_directory(&root)?;
Ok(root.join("personal-channel.sqlite3"))
}
fn create_private_directory(path: &Path) -> Result<(), String> {
fs::create_dir_all(path)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_STORAGE_UNAVAILABLE: {error}"))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(path, fs::Permissions::from_mode(0o700)).map_err(|error| {
format!("HOLOLAKE_PERSONAL_CHANNEL_STORAGE_PERMISSION_FAILED: {error}")
})?;
}
Ok(())
}
fn open_database(path: &Path) -> Result<Connection, String> {
if let Some(parent) = path.parent() {
create_private_directory(parent)?;
}
let connection = Connection::open(path)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_DATABASE_UNAVAILABLE: {error}"))?;
connection
.busy_timeout(Duration::from_secs(5))
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_DATABASE_UNAVAILABLE: {error}"))?;
connection
.execute_batch(
"PRAGMA foreign_keys = ON;
PRAGMA journal_mode = DELETE;
PRAGMA synchronous = FULL;
PRAGMA trusted_schema = OFF;
CREATE TABLE IF NOT EXISTS kernel_meta (
key TEXT PRIMARY KEY NOT NULL,
value TEXT NOT NULL
);
INSERT OR IGNORE INTO kernel_meta(key, value) VALUES ('schema_version', '1');
CREATE TABLE IF NOT EXISTS identities (
singleton INTEGER PRIMARY KEY CHECK(singleton = 1),
human_subject_id TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
channel_id TEXT NOT NULL UNIQUE,
created_at_unix_ms INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
task_id TEXT PRIMARY KEY NOT NULL,
human_subject_id TEXT NOT NULL,
title TEXT NOT NULL,
purpose TEXT NOT NULL,
status TEXT NOT NULL CHECK(status IN ('ACTIVE', 'COMPLETED')),
created_at_unix_ms INTEGER NOT NULL,
updated_at_unix_ms INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS one_active_personal_task
ON tasks(status) WHERE status = 'ACTIVE';
CREATE TABLE IF NOT EXISTS events (
sequence INTEGER PRIMARY KEY NOT NULL,
event_id TEXT NOT NULL UNIQUE,
human_subject_id TEXT NOT NULL,
kind TEXT NOT NULL,
task_id TEXT,
summary TEXT NOT NULL,
occurred_at_unix_ms INTEGER NOT NULL,
previous_event_hash TEXT NOT NULL,
payload_sha256 TEXT NOT NULL,
event_hash TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS receipts (
sequence INTEGER PRIMARY KEY NOT NULL,
receipt_id TEXT NOT NULL UNIQUE,
event_sequence INTEGER NOT NULL UNIQUE REFERENCES events(sequence),
event_hash TEXT NOT NULL,
payload_sha256 TEXT NOT NULL,
previous_receipt_hash TEXT NOT NULL,
receipt_hash TEXT NOT NULL UNIQUE,
issued_at_unix_ms INTEGER NOT NULL
);",
)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_SCHEMA_INVALID: {error}"))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(path, fs::Permissions::from_mode(0o600)).map_err(|error| {
format!("HOLOLAKE_PERSONAL_CHANNEL_STORAGE_PERMISSION_FAILED: {error}")
})?;
}
let version: String = connection
.query_row(
"SELECT value FROM kernel_meta WHERE key = 'schema_version'",
[],
|row| row.get(0),
)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_SCHEMA_INVALID: {error}"))?;
if version != DATABASE_SCHEMA_VERSION.to_string() {
return Err("HOLOLAKE_PERSONAL_CHANNEL_SCHEMA_UNSUPPORTED".into());
}
Ok(connection)
}
fn initialize_at(
database: &Path,
input: InitializePersonalChannelInput,
) -> Result<PersonalChannelSnapshot, String> {
let display_name = validated_text(&input.display_name, 80, "DISPLAY_NAME")?;
let mut connection = open_database(database)?;
verify_integrity(&connection)?;
let transaction = connection
.transaction_with_behavior(TransactionBehavior::Immediate)
.map_err(database_write_error)?;
let already_initialized: bool = transaction
.query_row(
"SELECT EXISTS(SELECT 1 FROM identities WHERE singleton = 1)",
[],
|row| row.get(0),
)
.map_err(database_read_error)?;
if already_initialized {
return Err("HOLOLAKE_PERSONAL_CHANNEL_ALREADY_INITIALIZED".into());
}
let human_subject_id = format!("human-local-{}", Uuid::new_v4());
let channel_id = format!("channel-local-{}", Uuid::new_v4());
let created_at = now_unix_ms()?;
transaction
.execute(
"INSERT INTO identities(singleton, human_subject_id, display_name, channel_id, created_at_unix_ms)
VALUES(1, ?1, ?2, ?3, ?4)",
params![human_subject_id, display_name, channel_id, created_at],
)
.map_err(database_write_error)?;
append_event(
&transaction,
&human_subject_id,
"CHANNEL_INITIALIZED",
None,
&format!("{display_name} 建立了个人频道"),
created_at,
)?;
transaction.commit().map_err(database_write_error)?;
snapshot_at(database)
}
fn create_task_at(
database: &Path,
input: CreatePersonalChannelTaskInput,
) -> Result<PersonalChannelSnapshot, String> {
let title = validated_text(&input.title, 160, "TASK_TITLE")?;
let purpose = validated_text(&input.purpose, 1_000, "TASK_PURPOSE")?;
let mut connection = open_database(database)?;
verify_integrity(&connection)?;
let transaction = connection
.transaction_with_behavior(TransactionBehavior::Immediate)
.map_err(database_write_error)?;
let human_subject_id = require_identity_id(&transaction)?;
let active_exists: bool = transaction
.query_row(
"SELECT EXISTS(SELECT 1 FROM tasks WHERE status = 'ACTIVE')",
[],
|row| row.get(0),
)
.map_err(database_read_error)?;
if active_exists {
return Err("HOLOLAKE_PERSONAL_CHANNEL_ACTIVE_TASK_EXISTS".into());
}
let task_id = format!("task-local-{}", Uuid::new_v4());
let observed_at = now_unix_ms()?;
transaction
.execute(
"INSERT INTO tasks(task_id, human_subject_id, title, purpose, status, created_at_unix_ms, updated_at_unix_ms)
VALUES(?1, ?2, ?3, ?4, 'ACTIVE', ?5, ?5)",
params![task_id, human_subject_id, title, purpose, observed_at],
)
.map_err(database_write_error)?;
append_event(
&transaction,
&human_subject_id,
"TASK_STARTED",
Some(&task_id),
&format!("开始:{title}"),
observed_at,
)?;
transaction.commit().map_err(database_write_error)?;
snapshot_at(database)
}
fn transition_task_at(
database: &Path,
input: TransitionPersonalChannelTaskInput,
) -> Result<PersonalChannelSnapshot, String> {
validate_identifier(&input.task_id, "TASK")?;
if input.expected_status != "ACTIVE" || input.next_status != "COMPLETED" {
return Err("HOLOLAKE_PERSONAL_CHANNEL_TASK_TRANSITION_INVALID".into());
}
let mut connection = open_database(database)?;
verify_integrity(&connection)?;
let transaction = connection
.transaction_with_behavior(TransactionBehavior::Immediate)
.map_err(database_write_error)?;
let human_subject_id = require_identity_id(&transaction)?;
let task = transaction
.query_row(
"SELECT title, status FROM tasks WHERE task_id = ?1",
params![input.task_id],
|row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)),
)
.optional()
.map_err(database_read_error)?
.ok_or("HOLOLAKE_PERSONAL_CHANNEL_TASK_NOT_FOUND")?;
if task.1 != input.expected_status {
return Err("HOLOLAKE_PERSONAL_CHANNEL_TASK_STATE_CONFLICT".into());
}
let observed_at = now_unix_ms()?;
let changed = transaction
.execute(
"UPDATE tasks SET status = 'COMPLETED', updated_at_unix_ms = ?1
WHERE task_id = ?2 AND status = 'ACTIVE'",
params![observed_at, input.task_id],
)
.map_err(database_write_error)?;
if changed != 1 {
return Err("HOLOLAKE_PERSONAL_CHANNEL_TASK_STATE_CONFLICT".into());
}
append_event(
&transaction,
&human_subject_id,
"TASK_COMPLETED",
Some(&input.task_id),
&format!("完成:{}", task.0),
observed_at,
)?;
transaction.commit().map_err(database_write_error)?;
snapshot_at(database)
}
fn require_identity_id(transaction: &Transaction<'_>) -> Result<String, String> {
transaction
.query_row(
"SELECT human_subject_id FROM identities WHERE singleton = 1",
[],
|row| row.get(0),
)
.optional()
.map_err(database_read_error)?
.ok_or_else(|| "HOLOLAKE_PERSONAL_CHANNEL_NOT_INITIALIZED".into())
}
fn append_event(
transaction: &Transaction<'_>,
human_subject_id: &str,
kind: &str,
task_id: Option<&str>,
summary: &str,
occurred_at_unix_ms: i64,
) -> Result<(), String> {
let (last_sequence, previous_event_hash, previous_receipt_hash) = transaction
.query_row(
"SELECT e.sequence, e.event_hash, r.receipt_hash
FROM events e JOIN receipts r ON r.event_sequence = e.sequence
ORDER BY e.sequence DESC LIMIT 1",
[],
|row| {
Ok((
row.get::<_, i64>(0)?,
row.get::<_, String>(1)?,
row.get::<_, String>(2)?,
))
},
)
.optional()
.map_err(database_read_error)?
.unwrap_or((0, ZERO_HASH.into(), ZERO_HASH.into()));
let sequence = last_sequence + 1;
let event_id = format!("event-local-{}", Uuid::new_v4());
let payload = EventHashPayload {
schema: KERNEL_SCHEMA,
sequence,
event_id: &event_id,
human_subject_id,
kind,
task_id,
summary,
occurred_at_unix_ms,
};
let payload_bytes = serde_json::to_vec(&payload)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_EVENT_INVALID: {error}"))?;
let payload_sha256 = sha256_hex(&payload_bytes);
let event_hash =
sha256_hex(format!("event-chain/v1\n{previous_event_hash}\n{payload_sha256}").as_bytes());
let receipt_seed = sha256_hex(format!("receipt-id/v1\n{event_hash}").as_bytes());
let receipt_id = format!("HLR-{}", &receipt_seed[..24]);
let receipt_hash = sha256_hex(
format!(
"receipt-chain/v1\n{previous_receipt_hash}\n{receipt_id}\n{event_hash}\n{payload_sha256}"
)
.as_bytes(),
);
transaction
.execute(
"INSERT INTO events(sequence, event_id, human_subject_id, kind, task_id, summary,
occurred_at_unix_ms, previous_event_hash, payload_sha256, event_hash)
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
params![
sequence,
event_id,
human_subject_id,
kind,
task_id,
summary,
occurred_at_unix_ms,
previous_event_hash,
payload_sha256,
event_hash
],
)
.map_err(database_write_error)?;
transaction
.execute(
"INSERT INTO receipts(sequence, receipt_id, event_sequence, event_hash, payload_sha256,
previous_receipt_hash, receipt_hash, issued_at_unix_ms)
VALUES(?1, ?2, ?1, ?3, ?4, ?5, ?6, ?7)",
params![
sequence,
receipt_id,
event_hash,
payload_sha256,
previous_receipt_hash,
receipt_hash,
occurred_at_unix_ms
],
)
.map_err(database_write_error)?;
Ok(())
}
fn snapshot_at(database: &Path) -> Result<PersonalChannelSnapshot, String> {
let connection = open_database(database)?;
let integrity = verify_integrity(&connection)?;
let identity = connection
.query_row(
"SELECT human_subject_id, display_name, channel_id, created_at_unix_ms
FROM identities WHERE singleton = 1",
[],
|row| {
Ok(PersonalChannelIdentity {
human_subject_id: row.get(0)?,
display_name: row.get(1)?,
channel_id: row.get(2)?,
created_at_unix_ms: row.get(3)?,
})
},
)
.optional()
.map_err(database_read_error)?;
let current_task = connection
.query_row(
"SELECT task_id, title, purpose, status, created_at_unix_ms, updated_at_unix_ms
FROM tasks WHERE status = 'ACTIVE' LIMIT 1",
[],
|row| {
Ok(PersonalChannelTask {
task_id: row.get(0)?,
title: row.get(1)?,
purpose: row.get(2)?,
status: row.get(3)?,
created_at_unix_ms: row.get(4)?,
updated_at_unix_ms: row.get(5)?,
})
},
)
.optional()
.map_err(database_read_error)?;
let mut statement = connection
.prepare(
"SELECT e.sequence, e.event_id, e.kind, e.task_id, e.summary, e.occurred_at_unix_ms,
e.event_hash, r.receipt_id, r.receipt_hash
FROM events e JOIN receipts r ON r.event_sequence = e.sequence
ORDER BY e.sequence DESC LIMIT 12",
)
.map_err(database_read_error)?;
let recent_events = statement
.query_map([], |row| {
Ok(PersonalChannelEventProjection {
sequence: row.get(0)?,
event_id: row.get(1)?,
kind: row.get(2)?,
task_id: row.get(3)?,
summary: row.get(4)?,
occurred_at_unix_ms: row.get(5)?,
event_hash: row.get(6)?,
receipt_id: row.get(7)?,
receipt_hash: row.get(8)?,
})
})
.map_err(database_read_error)?
.collect::<Result<Vec<_>, _>>()
.map_err(database_read_error)?;
Ok(PersonalChannelSnapshot {
schema: KERNEL_SCHEMA,
state: if identity.is_some() {
"READY"
} else {
"UNINITIALIZED"
},
identity,
current_task,
recent_events,
integrity,
storage: "LOCAL_PRIVATE_SQLITE_SINGLE_HOLOLAKE_OWNER",
authority: "LOCAL_HUMAN_CONFIRMED_IDENTITY_NOT_SERVER_AUTHORITY",
})
}
fn verify_integrity(connection: &Connection) -> Result<PersonalChannelIntegrity, String> {
let schema_version: i64 = connection
.query_row(
"SELECT value FROM kernel_meta WHERE key = 'schema_version'",
[],
|row| row.get::<_, String>(0),
)
.map_err(database_read_error)?
.parse()
.map_err(|_| "HOLOLAKE_PERSONAL_CHANNEL_SCHEMA_INVALID".to_string())?;
if schema_version != DATABASE_SCHEMA_VERSION {
return Err("HOLOLAKE_PERSONAL_CHANNEL_SCHEMA_UNSUPPORTED".into());
}
let receipt_count: i64 = connection
.query_row("SELECT COUNT(*) FROM receipts", [], |row| row.get(0))
.map_err(database_read_error)?;
let mut statement = connection
.prepare(
"SELECT e.sequence, e.event_id, e.human_subject_id, e.kind, e.task_id, e.summary,
e.occurred_at_unix_ms, e.previous_event_hash, e.payload_sha256, e.event_hash,
r.receipt_id, r.event_hash, r.payload_sha256, r.previous_receipt_hash, r.receipt_hash
FROM events e LEFT JOIN receipts r ON r.event_sequence = e.sequence
ORDER BY e.sequence ASC",
)
.map_err(database_read_error)?;
let mut rows = statement.query([]).map_err(database_read_error)?;
let mut expected_sequence = 1_i64;
let mut previous_event_hash = ZERO_HASH.to_string();
let mut previous_receipt_hash = ZERO_HASH.to_string();
let mut event_count = 0_i64;
while let Some(row) = rows.next().map_err(database_read_error)? {
let sequence: i64 = row.get(0).map_err(database_read_error)?;
let event_id: String = row.get(1).map_err(database_read_error)?;
let human_subject_id: String = row.get(2).map_err(database_read_error)?;
let kind: String = row.get(3).map_err(database_read_error)?;
let task_id: Option<String> = row.get(4).map_err(database_read_error)?;
let summary: String = row.get(5).map_err(database_read_error)?;
let occurred_at: i64 = row.get(6).map_err(database_read_error)?;
let stored_previous_event: String = row.get(7).map_err(database_read_error)?;
let stored_payload: String = row.get(8).map_err(database_read_error)?;
let stored_event_hash: String = row.get(9).map_err(database_read_error)?;
let receipt_id: Option<String> = row.get(10).map_err(database_read_error)?;
let receipt_event_hash: Option<String> = row.get(11).map_err(database_read_error)?;
let receipt_payload: Option<String> = row.get(12).map_err(database_read_error)?;
let stored_previous_receipt: Option<String> = row.get(13).map_err(database_read_error)?;
let stored_receipt_hash: Option<String> = row.get(14).map_err(database_read_error)?;
if sequence != expected_sequence || stored_previous_event != previous_event_hash {
return Err("HOLOLAKE_PERSONAL_CHANNEL_INTEGRITY_FAILED".into());
}
let payload = EventHashPayload {
schema: KERNEL_SCHEMA,
sequence,
event_id: &event_id,
human_subject_id: &human_subject_id,
kind: &kind,
task_id: task_id.as_deref(),
summary: &summary,
occurred_at_unix_ms: occurred_at,
};
let payload_sha256 = sha256_hex(
&serde_json::to_vec(&payload)
.map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_EVENT_INVALID: {error}"))?,
);
let event_hash = sha256_hex(
format!("event-chain/v1\n{previous_event_hash}\n{payload_sha256}").as_bytes(),
);
let receipt_id = receipt_id.ok_or("HOLOLAKE_PERSONAL_CHANNEL_RECEIPT_MISSING")?;
let expected_receipt_id = format!(
"HLR-{}",
&sha256_hex(format!("receipt-id/v1\n{event_hash}").as_bytes())[..24]
);
let receipt_hash = sha256_hex(
format!(
"receipt-chain/v1\n{previous_receipt_hash}\n{receipt_id}\n{event_hash}\n{payload_sha256}"
)
.as_bytes(),
);
if stored_payload != payload_sha256
|| stored_event_hash != event_hash
|| receipt_id != expected_receipt_id
|| receipt_event_hash.as_deref() != Some(event_hash.as_str())
|| receipt_payload.as_deref() != Some(payload_sha256.as_str())
|| stored_previous_receipt.as_deref() != Some(previous_receipt_hash.as_str())
|| stored_receipt_hash.as_deref() != Some(receipt_hash.as_str())
{
return Err("HOLOLAKE_PERSONAL_CHANNEL_INTEGRITY_FAILED".into());
}
previous_event_hash = event_hash;
previous_receipt_hash = receipt_hash;
expected_sequence += 1;
event_count += 1;
}
if receipt_count != event_count {
return Err("HOLOLAKE_PERSONAL_CHANNEL_INTEGRITY_FAILED".into());
}
let identity_count: i64 = connection
.query_row("SELECT COUNT(*) FROM identities", [], |row| row.get(0))
.map_err(database_read_error)?;
if identity_count > 1 || (identity_count == 1 && event_count == 0) {
return Err("HOLOLAKE_PERSONAL_CHANNEL_INTEGRITY_FAILED".into());
}
Ok(PersonalChannelIntegrity {
state: "PASS_100",
schema_version,
event_count,
receipt_count,
last_event_hash: previous_event_hash,
last_receipt_hash: previous_receipt_hash,
})
}
fn validated_text(value: &str, maximum_chars: usize, kind: &str) -> Result<String, String> {
let trimmed = value.trim();
let count = trimmed.chars().count();
if count == 0
|| count > maximum_chars
|| trimmed.chars().any(|character| character.is_control())
{
return Err(format!("HOLOLAKE_PERSONAL_CHANNEL_{kind}_INVALID"));
}
Ok(trimmed.to_string())
}
fn validate_identifier(value: &str, kind: &str) -> Result<(), String> {
if value.is_empty()
|| value.len() > 128
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b':'))
{
return Err(format!("HOLOLAKE_PERSONAL_CHANNEL_{kind}_ID_INVALID"));
}
Ok(())
}
fn now_unix_ms() -> Result<i64, String> {
let millis = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}"))?
.as_millis();
i64::try_from(millis).map_err(|_| "HOLOLAKE_SYSTEM_CLOCK_INVALID".into())
}
fn sha256_hex(value: &[u8]) -> String {
digest(&SHA256, value)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
fn database_read_error(error: rusqlite::Error) -> String {
format!("HOLOLAKE_PERSONAL_CHANNEL_DATABASE_UNREADABLE: {error}")
}
fn database_write_error(error: rusqlite::Error) -> String {
format!("HOLOLAKE_PERSONAL_CHANNEL_DATABASE_WRITE_FAILED: {error}")
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn database(temp: &TempDir) -> PathBuf {
temp.path().join("personal-channel.sqlite3")
}
fn initialize(database: &Path) -> PersonalChannelSnapshot {
initialize_at(
database,
InitializePersonalChannelInput {
display_name: "冰朔".into(),
},
)
.unwrap()
}
#[test]
fn initialization_creates_identity_event_and_receipt_atomically() {
let temp = TempDir::new().unwrap();
let snapshot = initialize(&database(&temp));
assert_eq!(snapshot.state, "READY");
assert_eq!(snapshot.identity.unwrap().display_name, "冰朔");
assert_eq!(snapshot.integrity.event_count, 1);
assert_eq!(snapshot.integrity.receipt_count, 1);
assert_eq!(snapshot.recent_events[0].kind, "CHANNEL_INITIALIZED");
assert!(snapshot.recent_events[0].receipt_id.starts_with("HLR-"));
}
#[test]
fn restart_reads_the_same_identity_task_event_and_receipt_chain() {
let temp = TempDir::new().unwrap();
let database = database(&temp);
initialize(&database);
let created = create_task_at(
&database,
CreatePersonalChannelTaskInput {
title: "完成第一阶段闭环".into(),
purpose: "让身份、任务、事件与回执在重启后仍然可见".into(),
},
)
.unwrap();
let before_hash = created.integrity.last_receipt_hash.clone();
drop(created);
let after_restart = snapshot_at(&database).unwrap();
assert_eq!(
after_restart.current_task.unwrap().title,
"完成第一阶段闭环"
);
assert_eq!(after_restart.integrity.event_count, 2);
assert_eq!(after_restart.integrity.last_receipt_hash, before_hash);
}
#[test]
fn one_active_task_is_enforced_and_completion_is_receipted() {
let temp = TempDir::new().unwrap();
let database = database(&temp);
initialize(&database);
let created = create_task_at(
&database,
CreatePersonalChannelTaskInput {
title: "当前任务".into(),
purpose: "验证单一当前焦点".into(),
},
)
.unwrap();
assert_eq!(
create_task_at(
&database,
CreatePersonalChannelTaskInput {
title: "冲突任务".into(),
purpose: "不应被创建".into(),
},
)
.unwrap_err(),
"HOLOLAKE_PERSONAL_CHANNEL_ACTIVE_TASK_EXISTS"
);
let completed = transition_task_at(
&database,
TransitionPersonalChannelTaskInput {
task_id: created.current_task.unwrap().task_id,
expected_status: "ACTIVE".into(),
next_status: "COMPLETED".into(),
},
)
.unwrap();
assert!(completed.current_task.is_none());
assert_eq!(completed.recent_events[0].kind, "TASK_COMPLETED");
assert_eq!(completed.integrity.event_count, 3);
}
#[test]
fn changed_event_bytes_fail_closed_on_readback() {
let temp = TempDir::new().unwrap();
let database = database(&temp);
initialize(&database);
let connection = open_database(&database).unwrap();
connection
.execute(
"UPDATE events SET summary = '被篡改' WHERE sequence = 1",
[],
)
.unwrap();
assert_eq!(
snapshot_at(&database).unwrap_err(),
"HOLOLAKE_PERSONAL_CHANNEL_INTEGRITY_FAILED"
);
}
#[test]
fn local_identity_is_singleton_and_not_recreated() {
let temp = TempDir::new().unwrap();
let database = database(&temp);
initialize(&database);
assert_eq!(
initialize_at(
&database,
InitializePersonalChannelInput {
display_name: "另一个人".into(),
},
)
.unwrap_err(),
"HOLOLAKE_PERSONAL_CHANNEL_ALREADY_INITIALIZED"
);
}
}