foundation: start native language reality engineering root
This commit is contained in:
commit
0dec0b93fc
154 changed files with 49993 additions and 0 deletions
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"schema": "hololake.channel-initialization-templates/v1",
|
||||
"record_id": "HLP-CHANNEL-INITIALIZATION-001",
|
||||
"state": "NATIVE_INITIALIZATION_PATHS_IMPLEMENTED",
|
||||
"invariants": {
|
||||
"one_human_one_account_scoped_channel_root": true,
|
||||
"channel_system_exists_before_persona_binding": true,
|
||||
"persona_identity_is_not_preconfigured": true,
|
||||
"every_channel_gets_its_own_human_and_channel_identifiers": true,
|
||||
"bingshuo_private_number_or_authority_is_never_copied": true,
|
||||
"ordinary_user_requires_domain_membership": false,
|
||||
"ordinary_user_requires_enterprise_server": false,
|
||||
"enterprise_account_does_not_grant_personal_node_ownership": true
|
||||
,"registered_team_accounts_receive_canonical_human_and_channel_numbers": true
|
||||
,"private_skill_packs_are_selected_by_account_channel_and_persona": true
|
||||
},
|
||||
"common_native_bundle": [
|
||||
{ "module_id": "hololake.knowledge-workspace", "display_name": "光湖知识空间" },
|
||||
{ "module_id": "hololake.language-channel-system", "display_name": "语言人格频道" },
|
||||
{ "module_id": "hololake.conversation-manager", "display_name": "多对话管理" },
|
||||
{ "module_id": "hololake.numbered-knowledge-protocol", "display_name": "编号知识导航" },
|
||||
{ "module_id": "hololake.language-kernel-runtime", "display_name": "光湖有界语言核运行时" },
|
||||
{ "module_id": "hololake.local-execution-subpersona", "display_name": "本地执行子人格 Agent" },
|
||||
{ "module_id": "hololake.hldp-tool-forge", "display_name": "HLDP 工具锻造台" },
|
||||
{ "module_id": "hololake.personal-skill-runtime", "display_name": "个人频道技能脑" },
|
||||
{ "module_id": "hololake.persona-time-authority", "display_name": "时间主控" }
|
||||
],
|
||||
"templates": {
|
||||
"bingshuo_fifth_domain_instance": {
|
||||
"kind": "FIFTH_DOMAIN_PRIVATE_INSTANCE",
|
||||
"channel_number": "ICE-CH-ZC001",
|
||||
"human_number": "ICE-GL∞",
|
||||
"path": "第五域 / 冰朔通感语言核系统 / 零点原核本体频道",
|
||||
"copyable_as_public_template": false
|
||||
},
|
||||
"guanghu_team": {
|
||||
"kind": "GUANGHU_TEAM_INITIAL_CHANNEL",
|
||||
"entry": "REGISTERED_TEAM_ACCOUNT",
|
||||
"work_route": "ENTERPRISE_FOUR_DOMAIN_WORK_CHANNEL",
|
||||
"personal_channel_ownership": "SEPARATE_SELF_OWNED_NODE_REQUIRES_EXPLICIT_HUMAN_INITIALIZATION",
|
||||
"uses_bingshuo_private_identifiers": false
|
||||
,"registered_channel_numbers": ["SYS-JZ", "SYS-AW", "SYS-YY", "SYS-HE", "SYS-FM", "SYS-ZZ"]
|
||||
},
|
||||
"ordinary_user": {
|
||||
"kind": "ORDINARY_USER_INITIAL_CHANNEL",
|
||||
"entry": "LOCAL_FIRST_ENTRY",
|
||||
"route": "USER_OWNED_LOCAL_NODE_OUTSIDE_ALL_FIVE_DOMAINS",
|
||||
"identifier_state": "LOCAL_UNIQUE_IDENTIFIERS_ISSUED_FORMAL_LIGHTHOUSE_NUMBER_PENDING",
|
||||
"uses_bingshuo_private_identifiers": false
|
||||
,"personal_skill_default": "NO_PRIVATE_PACK_UNLESS_EXACT_REGISTRY_MATCH"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"schema": "hololake.personal-channel-kernel-contract/v1",
|
||||
"record_id": "HLP-PERSONAL-CHANNEL-KERNEL-001",
|
||||
"state": "SOURCE_IMPLEMENTED_AND_TESTED_NOT_INSTALLED_OR_DEPLOYED",
|
||||
"scope": "PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
|
||||
"initialization_templates": "contracts/channel-initialization-templates.json",
|
||||
"storage": {
|
||||
"owner": "HOLOLAKE_NATIVE_RUST_CORE",
|
||||
"engine": "EMBEDDED_SQLITE",
|
||||
"location": "TAURI_APP_DATA_PERSONAL_CHANNEL_V1",
|
||||
"directory_mode": "0700",
|
||||
"database_mode": "0600",
|
||||
"synchronous_commit": "FULL",
|
||||
"webview_direct_database_access": false,
|
||||
"server_upload_automatic": false
|
||||
},
|
||||
"identity": {
|
||||
"cardinality": "ONE_LOCAL_HUMAN_SUBJECT_PER_DATABASE",
|
||||
"stable_native_ids": true,
|
||||
"display_name_human_confirmed": true,
|
||||
"platform_account_authentication_claimed": false,
|
||||
"server_or_repository_authority_granted": false
|
||||
},
|
||||
"initial_channel_bundle": {
|
||||
"atomic_with_identity_initialization": true,
|
||||
"existing_channel_migration": "IDEMPOTENT_ADDITIVE",
|
||||
"module_ids": [
|
||||
"hololake.knowledge-workspace",
|
||||
"hololake.language-channel-system",
|
||||
"hololake.conversation-manager",
|
||||
"hololake.numbered-knowledge-protocol",
|
||||
"hololake.persona-time-authority"
|
||||
]
|
||||
},
|
||||
"task": {
|
||||
"one_active_task_at_a_time": true,
|
||||
"purpose_required": true,
|
||||
"implemented_transitions": ["CREATE_ACTIVE", "ACTIVE_TO_COMPLETED"],
|
||||
"compare_expected_state_before_transition": true
|
||||
},
|
||||
"event_and_receipt": {
|
||||
"identity_and_task_mutations_are_single_sqlite_transactions": true,
|
||||
"every_mutation_has_event": true,
|
||||
"every_event_has_receipt": true,
|
||||
"event_chain_sha256": true,
|
||||
"receipt_chain_sha256": true,
|
||||
"integrity_verified_before_mutation_and_readback": true,
|
||||
"tampering_fails_closed": true
|
||||
},
|
||||
"acceptance": {
|
||||
"initialization_atomicity_test": true,
|
||||
"single_active_task_test": true,
|
||||
"task_completion_receipt_test": true,
|
||||
"process_restart_readback_test": true,
|
||||
"tamper_detection_test": true,
|
||||
"react_human_projection_integrated": true,
|
||||
"react_identity_onboarding_integrated": true,
|
||||
"react_default_task_form_exposed": false,
|
||||
"signed_installed_runtime_acceptance": false,
|
||||
"local_ad_hoc_signed_installed_runtime_acceptance": true,
|
||||
"data_migration": false,
|
||||
"server_deployment": false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"schema": "hololake.user-pncc-channel-contract/v1",
|
||||
"record_id": "HLP-USER-PNCC-001",
|
||||
"formal_name": "GH-PNCC · 光湖人格原生代码频道",
|
||||
"state": "LOCAL_NATIVE_CHANNEL_IMPLEMENTED_REMOTE_PUBLICATION_UNBOUND",
|
||||
"engine": "GIT",
|
||||
"human_projection": "HOLOLAKE_NATIVE",
|
||||
"forgejo_role": "OPTIONAL_REMOTE_COLLABORATION_ADAPTER",
|
||||
"binding": {
|
||||
"domain_resolved_before_login": true,
|
||||
"domain_specific_login_and_node_entry_required": true,
|
||||
"requires_verified_user_number": true,
|
||||
"requires_authenticated_repository_account": true,
|
||||
"password_written_to_repository": false,
|
||||
"persona_binding_claimed": false
|
||||
},
|
||||
"local_repository": {
|
||||
"automatic_idempotent_initialization": true,
|
||||
"default_branch": "main",
|
||||
"app_owned_private_root": true,
|
||||
"initial_commit_receipt": true
|
||||
},
|
||||
"remote_repository": {
|
||||
"created_automatically": false,
|
||||
"bound": false,
|
||||
"reason": "REMOTE_REPOSITORY_NAME_POLICY_AND_EXPLICIT_PUBLICATION_RECEIPT_NOT_REGISTERED"
|
||||
},
|
||||
"authority": "LOCAL_USER_CODE_CHANNEL_NO_PUSH_DEPLOY_OR_REALITY_EXECUTION_AUTHORITY"
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const read = (relative) => fs.readFileSync(new URL(`../${relative}`, import.meta.url), 'utf8')
|
||||
const contract = JSON.parse(read('contracts/personal-channel-kernel.json'))
|
||||
const foundation = JSON.parse(read('foundation.json'))
|
||||
const stageOne = JSON.parse(read('contracts/stage-one-platform.json'))
|
||||
const rust = read('src-tauri/src/personal_channel.rs')
|
||||
const lib = read('src-tauri/src/lib.rs')
|
||||
const numberedDispatcher = read('src-tauri/src/numbered_ipc_dispatch.rs')
|
||||
const ui = read('src/main.tsx')
|
||||
|
||||
test('personal channel kernel has one private SQLite owner and no server authority', () => {
|
||||
assert.equal(contract.storage.owner, 'HOLOLAKE_NATIVE_RUST_CORE')
|
||||
assert.equal(contract.storage.engine, 'EMBEDDED_SQLITE')
|
||||
assert.equal(contract.storage.webview_direct_database_access, false)
|
||||
assert.equal(contract.identity.platform_account_authentication_claimed, false)
|
||||
assert.equal(contract.identity.server_or_repository_authority_granted, false)
|
||||
assert.equal(contract.acceptance.server_deployment, false)
|
||||
})
|
||||
|
||||
test('identity and task mutations create one-to-one chained events and receipts', () => {
|
||||
assert.equal(contract.event_and_receipt.identity_and_task_mutations_are_single_sqlite_transactions, true)
|
||||
assert.equal(contract.event_and_receipt.every_mutation_has_event, true)
|
||||
assert.equal(contract.event_and_receipt.every_event_has_receipt, true)
|
||||
assert.equal(contract.event_and_receipt.tampering_fails_closed, true)
|
||||
assert.match(rust, /CREATE TABLE IF NOT EXISTS events/)
|
||||
assert.match(rust, /CREATE TABLE IF NOT EXISTS receipts/)
|
||||
assert.match(rust, /verify_integrity\(&connection\)/)
|
||||
})
|
||||
|
||||
test('every initialized channel receives the native knowledge, language, conversation, number and time bundle', () => {
|
||||
assert.equal(contract.initial_channel_bundle.atomic_with_identity_initialization, true)
|
||||
assert.deepEqual(contract.initial_channel_bundle.module_ids, [
|
||||
'hololake.knowledge-workspace',
|
||||
'hololake.language-channel-system',
|
||||
'hololake.conversation-manager',
|
||||
'hololake.numbered-knowledge-protocol',
|
||||
'hololake.persona-time-authority',
|
||||
])
|
||||
assert.match(rust, /install_initial_channel_modules/)
|
||||
})
|
||||
|
||||
test('the human projection establishes identity once and keeps task mutation below the default surface', () => {
|
||||
for (const command of [
|
||||
'get_personal_channel_snapshot',
|
||||
'initialize_personal_channel',
|
||||
'create_personal_channel_task',
|
||||
'transition_personal_channel_task',
|
||||
]) {
|
||||
assert.match(numberedDispatcher, new RegExp(`personal_channel::${command}`))
|
||||
}
|
||||
assert.doesNotMatch(lib, /personal_channel::get_personal_channel_snapshot/)
|
||||
for (const command of ['get_personal_channel_snapshot', 'initialize_personal_channel']) {
|
||||
assert.match(ui, new RegExp(`['"]${command}['"]`))
|
||||
}
|
||||
assert.doesNotMatch(ui, /['"]create_personal_channel_task['"]/)
|
||||
assert.doesNotMatch(ui, /['"]transition_personal_channel_task['"]/)
|
||||
assert.doesNotMatch(ui, /下一件事|为什么做|开始这件事/)
|
||||
assert.equal(stageOne.personal_channel_kernel.native_source_implemented, true)
|
||||
assert.equal(stageOne.personal_channel_kernel.installed_runtime_acceptance, true)
|
||||
assert.equal(foundation.personal_channel_identity_task_event_receipt_kernel_implemented, true)
|
||||
assert.equal(foundation.personal_channel_installed_runtime_acceptance, true)
|
||||
})
|
||||
|
||||
test('full stage-one completion stays false after the first source slice', () => {
|
||||
assert.equal(stageOne.implementation_complete, false)
|
||||
assert.equal(foundation.stage_one_next_slice, 'HUMAN_APPROVAL_CENTER_AND_GIT_EVIDENCE_READBACK')
|
||||
assert.match(foundation.stage_one_visible_body_state, /PRODUCT_INCOMPLETE/)
|
||||
})
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const readText = (relative) => fs.readFileSync(path.join(root, relative), 'utf8')
|
||||
const readJson = (relative) => JSON.parse(readText(relative))
|
||||
|
||||
test('the native user PNCC contract binds verified number, signed-in account and embedded Git', () => {
|
||||
const contract = readJson('contracts/user-pncc-channel.json')
|
||||
assert.equal(contract.engine, 'GIT')
|
||||
assert.equal(contract.human_projection, 'HOLOLAKE_NATIVE')
|
||||
assert.equal(contract.forgejo_role, 'OPTIONAL_REMOTE_COLLABORATION_ADAPTER')
|
||||
assert.equal(contract.binding.requires_verified_user_number, true)
|
||||
assert.equal(contract.binding.domain_resolved_before_login, true)
|
||||
assert.equal(contract.binding.domain_specific_login_and_node_entry_required, true)
|
||||
assert.equal(contract.binding.requires_authenticated_repository_account, true)
|
||||
assert.equal(contract.binding.password_written_to_repository, false)
|
||||
assert.equal(contract.binding.persona_binding_claimed, false)
|
||||
assert.equal(contract.local_repository.automatic_idempotent_initialization, true)
|
||||
})
|
||||
|
||||
test('the user PNCC is a native command and first-class HoloLake projection', () => {
|
||||
const native = readText('src-tauri/src/lib.rs')
|
||||
const numberedDispatcher = readText('src-tauri/src/numbered_ipc_dispatch.rs')
|
||||
const frontend = readText('src/main.tsx')
|
||||
assert.match(numberedDispatcher, /user_pncc_channel::get_user_pncc_channel/)
|
||||
assert.match(numberedDispatcher, /user_pncc_channel::ensure_user_pncc_channel/)
|
||||
assert.doesNotMatch(native, /user_pncc_channel::get_user_pncc_channel/)
|
||||
assert.match(frontend, /GH-PNCC · 人格原生代码频道/)
|
||||
assert.match(frontend, /Forgejo 协作适配器/)
|
||||
assert.match(frontend, /ensure_user_pncc_channel/)
|
||||
assert.doesNotMatch(frontend, /Forgejo 页面/)
|
||||
})
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,522 @@
|
|||
//! GH-PNCC 用户原生代码频道。
|
||||
//!
|
||||
//! Git 是耐久化引擎,HoloLake 是用户可见的原生上层,Forgejo 只承担可选的
|
||||
//! 远端协作适配。此绑定只证明“已验证用户编号 + 已认证仓库账号”对应同一份
|
||||
//! 本机代码频道,不证明人格绑定,也不授予推送、发布、部署或现实执行权限。
|
||||
|
||||
use ring::digest::{digest, SHA256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::code_channel;
|
||||
use crate::code_repo_login::{self, LoginSession};
|
||||
use crate::zero_point::{self, ZeroPointState};
|
||||
|
||||
const BINDING_SCHEMA: &str = "hololake.user-pncc-binding/v1";
|
||||
const SNAPSHOT_SCHEMA: &str = "hololake.user-pncc-channel/v1";
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct UserPnccBindingRecord {
|
||||
schema: String,
|
||||
repository_id: String,
|
||||
user_number: String,
|
||||
domain: String,
|
||||
account_username: String,
|
||||
account_host: String,
|
||||
created_at_unix_ms: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UserPnccChannelSnapshot {
|
||||
pub schema: &'static str,
|
||||
pub state: &'static str,
|
||||
pub repository_id: String,
|
||||
pub channel_id: String,
|
||||
pub user_number: String,
|
||||
pub domain: String,
|
||||
pub account_username: String,
|
||||
pub account_host: String,
|
||||
pub local_path: String,
|
||||
pub branch: String,
|
||||
pub git_head: String,
|
||||
pub repository_clean: bool,
|
||||
pub created_at_unix_ms: u128,
|
||||
pub git_engine: &'static str,
|
||||
pub human_projection: &'static str,
|
||||
pub forgejo_adapter_state: &'static str,
|
||||
pub remote_repository_url: Option<String>,
|
||||
pub persona_binding_claimed: bool,
|
||||
pub authority: &'static str,
|
||||
}
|
||||
|
||||
pub async fn get_user_pncc_channel(
|
||||
app: AppHandle,
|
||||
state: State<'_, ZeroPointState>,
|
||||
) -> Result<Option<UserPnccChannelSnapshot>, String> {
|
||||
let Some((number, session)) = trusted_subject(&app, &state)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let root = user_pncc_root(&app)?;
|
||||
let app_for_registration = app.clone();
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
read_existing_with_registration(&root, &number, &session, |repository, name| {
|
||||
code_channel::register_managed_repository(&app_for_registration, repository, name)
|
||||
.map(|entry| entry.channel_id)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_JOIN_FAILED: {error}"))?
|
||||
}
|
||||
|
||||
pub async fn ensure_user_pncc_channel(
|
||||
app: AppHandle,
|
||||
state: State<'_, ZeroPointState>,
|
||||
) -> Result<UserPnccChannelSnapshot, String> {
|
||||
let Some((number, session)) = trusted_subject(&app, &state)? else {
|
||||
return Err("HOLOLAKE_USER_PNCC_TRUSTED_SUBJECT_REQUIRED".into());
|
||||
};
|
||||
let root = user_pncc_root(&app)?;
|
||||
let app_for_registration = app.clone();
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
ensure_at(&root, &number, &session, |repository, name| {
|
||||
code_channel::register_managed_repository(&app_for_registration, repository, name)
|
||||
.map(|entry| entry.channel_id)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_JOIN_FAILED: {error}"))?
|
||||
}
|
||||
|
||||
fn trusted_subject(
|
||||
app: &AppHandle,
|
||||
state: &ZeroPointState,
|
||||
) -> Result<Option<(String, LoginSession)>, String> {
|
||||
let Some((number, domain)) = zero_point::verified_user_route(state)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
// 企业账号的代码入口是企业责任仓库;不能自动创建一个本机个人仓库
|
||||
// 来冒充工作域或绕过之后的个人节点拥有权验证。
|
||||
if domain != "FIFTH_DOMAIN" {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(session) = code_repo_login::current_login_session_for_domain(app, &domain)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some((number, session)))
|
||||
}
|
||||
|
||||
fn user_pncc_root(app: &AppHandle) -> Result<PathBuf, String> {
|
||||
let root = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?
|
||||
.join("user-pncc-v1");
|
||||
ensure_private_root(&root)?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
fn ensure_private_root(root: &Path) -> Result<(), String> {
|
||||
fs::create_dir_all(root.join("repositories"))
|
||||
.and_then(|_| fs::create_dir_all(root.join("bindings")))
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_STORAGE_UNAVAILABLE: {error}"))?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(root, fs::Permissions::from_mode(0o700))
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_PERMISSION_FAILED: {error}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn repository_id(number: &str, session: &LoginSession) -> String {
|
||||
let material = format!(
|
||||
"{}\0{}\0{}\0{}",
|
||||
number, session.domain, session.host, session.username
|
||||
);
|
||||
format!("gh-pncc-{}", &sha256_hex(material.as_bytes())[..24])
|
||||
}
|
||||
|
||||
fn binding_path(root: &Path, repository_id: &str) -> PathBuf {
|
||||
root.join("bindings").join(format!("{repository_id}.json"))
|
||||
}
|
||||
|
||||
fn repository_path(root: &Path, repository_id: &str) -> PathBuf {
|
||||
root.join("repositories").join(repository_id)
|
||||
}
|
||||
|
||||
fn read_existing_with_registration<F>(
|
||||
root: &Path,
|
||||
number: &str,
|
||||
session: &LoginSession,
|
||||
register: F,
|
||||
) -> Result<Option<UserPnccChannelSnapshot>, String>
|
||||
where
|
||||
F: FnOnce(&Path, String) -> Result<String, String>,
|
||||
{
|
||||
ensure_private_root(root)?;
|
||||
let id = repository_id(number, session);
|
||||
let record_path = binding_path(root, &id);
|
||||
if !record_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
let record: UserPnccBindingRecord = serde_json::from_slice(
|
||||
&fs::read(&record_path)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_UNAVAILABLE: {error}"))?,
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_INVALID: {error}"))?;
|
||||
validate_record(&record, &id, number, session)?;
|
||||
let repository = repository_path(root, &id);
|
||||
let channel_id = register(&repository, native_channel_name(number, session))?;
|
||||
project(&repository, record, channel_id).map(Some)
|
||||
}
|
||||
|
||||
fn ensure_at<F>(
|
||||
root: &Path,
|
||||
number: &str,
|
||||
session: &LoginSession,
|
||||
register: F,
|
||||
) -> Result<UserPnccChannelSnapshot, String>
|
||||
where
|
||||
F: FnOnce(&Path, String) -> Result<String, String>,
|
||||
{
|
||||
validate_subject(number, session)?;
|
||||
ensure_private_root(root)?;
|
||||
let id = repository_id(number, session);
|
||||
let record_path = binding_path(root, &id);
|
||||
let repository = repository_path(root, &id);
|
||||
let record = if record_path.exists() {
|
||||
let record: UserPnccBindingRecord = serde_json::from_slice(
|
||||
&fs::read(&record_path)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_UNAVAILABLE: {error}"))?,
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_INVALID: {error}"))?;
|
||||
validate_record(&record, &id, number, session)?;
|
||||
record
|
||||
} else {
|
||||
if repository.exists() {
|
||||
return Err("HOLOLAKE_USER_PNCC_UNBOUND_REPOSITORY_PRESENT".into());
|
||||
}
|
||||
let created_at_unix_ms = now_unix_ms()?;
|
||||
let record = UserPnccBindingRecord {
|
||||
schema: BINDING_SCHEMA.into(),
|
||||
repository_id: id.clone(),
|
||||
user_number: number.into(),
|
||||
domain: session.domain.clone(),
|
||||
account_username: session.username.clone(),
|
||||
account_host: session.host.clone(),
|
||||
created_at_unix_ms,
|
||||
};
|
||||
initialize_repository(&repository, &record)?;
|
||||
write_json_atomic(&record_path, &record)?;
|
||||
record
|
||||
};
|
||||
let channel_id = register(&repository, native_channel_name(number, session))?;
|
||||
project(&repository, record, channel_id)
|
||||
}
|
||||
|
||||
fn validate_subject(number: &str, session: &LoginSession) -> Result<(), String> {
|
||||
let number_valid = !number.trim().is_empty()
|
||||
&& number.len() <= 64
|
||||
&& !number.chars().any(|item| item.is_control());
|
||||
let account_valid = !session.username.is_empty()
|
||||
&& session.username.len() <= 40
|
||||
&& session
|
||||
.username
|
||||
.chars()
|
||||
.all(|item| item.is_ascii_alphanumeric() || item == '-' || item == '_')
|
||||
&& matches!(
|
||||
session.host.as_str(),
|
||||
"guanghulab.com" | "guanghubingshuo.com" | "guanghu.chat"
|
||||
);
|
||||
let identity_account_bound = match number {
|
||||
"ICE-GL∞" => session.username.eq_ignore_ascii_case("bingshuo"),
|
||||
"ICE-GL-ZHI∞" => session.username.eq_ignore_ascii_case("zhizhi"),
|
||||
_ => true,
|
||||
};
|
||||
if number_valid && account_valid && identity_account_bound {
|
||||
Ok(())
|
||||
} else if number_valid && account_valid {
|
||||
Err("HOLOLAKE_LOGIN_ACCOUNT_NUMBER_MISMATCH".into())
|
||||
} else {
|
||||
Err("HOLOLAKE_USER_PNCC_SUBJECT_INVALID".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_record(
|
||||
record: &UserPnccBindingRecord,
|
||||
expected_id: &str,
|
||||
number: &str,
|
||||
session: &LoginSession,
|
||||
) -> Result<(), String> {
|
||||
validate_subject(number, session)?;
|
||||
if record.schema == BINDING_SCHEMA
|
||||
&& record.repository_id == expected_id
|
||||
&& record.user_number == number
|
||||
&& record.domain == session.domain
|
||||
&& record.account_username == session.username
|
||||
&& record.account_host == session.host
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err("HOLOLAKE_USER_PNCC_BINDING_MISMATCH".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_repository(repository: &Path, record: &UserPnccBindingRecord) -> Result<(), String> {
|
||||
fs::create_dir_all(repository.join(".hololake"))
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_REPOSITORY_CREATE_FAILED: {error}"))?;
|
||||
run_git(repository, &["init", "--initial-branch=main"], "INIT")?;
|
||||
run_git(
|
||||
repository,
|
||||
&["config", "user.name", &record.account_username],
|
||||
"CONFIG_NAME",
|
||||
)?;
|
||||
let email = format!("{}@users.hololake.invalid", record.account_username);
|
||||
run_git(
|
||||
repository,
|
||||
&["config", "user.email", &email],
|
||||
"CONFIG_EMAIL",
|
||||
)?;
|
||||
let manifest = serde_json::json!({
|
||||
"schema": "hololake.user-pncc-channel-manifest/v1",
|
||||
"repositoryId": record.repository_id,
|
||||
"userNumber": record.user_number,
|
||||
"domain": record.domain,
|
||||
"account": { "username": record.account_username, "host": record.account_host },
|
||||
"engine": "GIT",
|
||||
"humanProjection": "HOLOLAKE_NATIVE",
|
||||
"forgejoRole": "OPTIONAL_REMOTE_COLLABORATION_ADAPTER",
|
||||
"personaBindingClaimed": false,
|
||||
"authority": "LOCAL_USER_CODE_CHANNEL_NO_PUSH_DEPLOY_OR_REALITY_EXECUTION_AUTHORITY",
|
||||
"createdAtUnixMs": record.created_at_unix_ms,
|
||||
});
|
||||
fs::write(
|
||||
repository.join(".hololake/channel.json"),
|
||||
format!(
|
||||
"{}\n",
|
||||
serde_json::to_string_pretty(&manifest)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_MANIFEST_INVALID: {error}"))?
|
||||
),
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_MANIFEST_WRITE_FAILED: {error}"))?;
|
||||
let channel_title = native_channel_title(&record.user_number);
|
||||
fs::write(
|
||||
repository.join("README.md"),
|
||||
format!(
|
||||
"# {}\n\n这是 HoloLake 为用户 `{}` 建立的本机原生代码频道。\n\n- 耐久化引擎:Git\n- 人类可见上层:HoloLake\n- 远端协作:等待该用户自己的服务器接入\n- 用户编号:`{}`\n\n本仓库不保存账号密码,也不因建立代码频道而声称人格绑定或授予推送、发布、部署与现实执行权限。\n",
|
||||
channel_title, record.account_username, record.user_number
|
||||
),
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_README_WRITE_FAILED: {error}"))?;
|
||||
run_git(repository, &["add", "--all"], "ADD")?;
|
||||
run_git(
|
||||
repository,
|
||||
&["commit", "-m", "初始化 GH-PNCC 人格原生代码频道"],
|
||||
"COMMIT",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn project(
|
||||
repository: &Path,
|
||||
record: UserPnccBindingRecord,
|
||||
channel_id: String,
|
||||
) -> Result<UserPnccChannelSnapshot, String> {
|
||||
let git_head = run_git(repository, &["rev-parse", "HEAD"], "READ_HEAD")?
|
||||
.trim()
|
||||
.to_string();
|
||||
let branch = run_git(
|
||||
repository,
|
||||
&["symbolic-ref", "--short", "HEAD"],
|
||||
"READ_BRANCH",
|
||||
)?
|
||||
.trim()
|
||||
.to_string();
|
||||
let repository_clean = run_git(repository, &["status", "--porcelain"], "READ_STATUS")?
|
||||
.trim()
|
||||
.is_empty();
|
||||
Ok(UserPnccChannelSnapshot {
|
||||
schema: SNAPSHOT_SCHEMA,
|
||||
state: "READY",
|
||||
repository_id: record.repository_id,
|
||||
channel_id,
|
||||
user_number: record.user_number,
|
||||
domain: record.domain,
|
||||
account_username: record.account_username,
|
||||
account_host: record.account_host,
|
||||
local_path: repository.to_string_lossy().into_owned(),
|
||||
branch,
|
||||
git_head,
|
||||
repository_clean,
|
||||
created_at_unix_ms: record.created_at_unix_ms,
|
||||
git_engine: "GIT",
|
||||
human_projection: "HOLOLAKE_NATIVE",
|
||||
forgejo_adapter_state: "AUTHENTICATED_REMOTE_REPOSITORY_UNBOUND",
|
||||
remote_repository_url: None,
|
||||
persona_binding_claimed: false,
|
||||
authority: "LOCAL_USER_CODE_CHANNEL_NO_PUSH_DEPLOY_OR_REALITY_EXECUTION_AUTHORITY",
|
||||
})
|
||||
}
|
||||
|
||||
fn native_channel_title(number: &str) -> &'static str {
|
||||
match number {
|
||||
"ICE-GL-ZHI∞" => "明天见频道 · GH-PNCC",
|
||||
"ICE-GL∞" => "永恒湖心个人频道 · GH-PNCC",
|
||||
_ => "人格原生代码频道 · GH-PNCC",
|
||||
}
|
||||
}
|
||||
|
||||
fn native_channel_name(number: &str, session: &LoginSession) -> String {
|
||||
format!("{} · {}", native_channel_title(number), session.username)
|
||||
}
|
||||
|
||||
fn write_json_atomic(path: &Path, value: &UserPnccBindingRecord) -> Result<(), String> {
|
||||
let bytes = serde_json::to_vec_pretty(value)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_INVALID: {error}"))?;
|
||||
let temporary = path.with_extension(format!("tmp-{}", Uuid::new_v4()));
|
||||
let mut options = OpenOptions::new();
|
||||
options.create_new(true).write(true);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
options.mode(0o600);
|
||||
}
|
||||
let mut file = options
|
||||
.open(&temporary)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_WRITE_FAILED: {error}"))?;
|
||||
file.write_all(&bytes)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_WRITE_FAILED: {error}"))?;
|
||||
fs::rename(&temporary, path)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_BINDING_WRITE_FAILED: {error}"))
|
||||
}
|
||||
|
||||
fn run_git(root: &Path, args: &[&str], operation: &str) -> Result<String, String> {
|
||||
let executable = if cfg!(windows) { "git" } else { "/usr/bin/git" };
|
||||
let output = Command::new(executable)
|
||||
.current_dir(root)
|
||||
.env("GIT_TERMINAL_PROMPT", "0")
|
||||
.env("GIT_CONFIG_NOSYSTEM", "1")
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_GIT_{operation}_FAILED: {error}"))?;
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"HOLOLAKE_USER_PNCC_GIT_{operation}_FAILED: {}",
|
||||
String::from_utf8_lossy(&output.stderr).trim()
|
||||
));
|
||||
}
|
||||
String::from_utf8(output.stdout)
|
||||
.map_err(|error| format!("HOLOLAKE_USER_PNCC_GIT_{operation}_INVALID_UTF8: {error}"))
|
||||
}
|
||||
|
||||
fn sha256_hex(bytes: &[u8]) -> String {
|
||||
digest(&SHA256, bytes)
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
fn session() -> LoginSession {
|
||||
LoginSession {
|
||||
username: "lake_user".into(),
|
||||
host: "guanghulab.com".into(),
|
||||
domain: "FIFTH_DOMAIN".into(),
|
||||
signed_in_at_unix_ms: 1,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialization_is_idempotent_and_creates_a_clean_main_repository() {
|
||||
let root = tempdir().unwrap();
|
||||
let first = ensure_at(root.path(), "ICE-P-TEST001", &session(), |_, _| {
|
||||
Ok("channel-one".into())
|
||||
})
|
||||
.unwrap();
|
||||
let second = ensure_at(root.path(), "ICE-P-TEST001", &session(), |_, _| {
|
||||
Ok("channel-one".into())
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(first.repository_id, second.repository_id);
|
||||
assert_eq!(first.git_head, second.git_head);
|
||||
assert_eq!(first.branch, "main");
|
||||
assert!(first.repository_clean);
|
||||
assert!(!first.persona_binding_claimed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_and_repository_never_contain_a_password_field() {
|
||||
let root = tempdir().unwrap();
|
||||
let snapshot = ensure_at(root.path(), "ICE-P-TEST001", &session(), |_, _| {
|
||||
Ok("channel-one".into())
|
||||
})
|
||||
.unwrap();
|
||||
let binding =
|
||||
fs::read_to_string(binding_path(root.path(), &snapshot.repository_id)).unwrap();
|
||||
let manifest = fs::read_to_string(
|
||||
repository_path(root.path(), &snapshot.repository_id).join(".hololake/channel.json"),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!binding.to_ascii_lowercase().contains("password"));
|
||||
assert!(!manifest.to_ascii_lowercase().contains("password"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identity_tuple_selects_a_distinct_repository_without_overwriting_another_user() {
|
||||
let root = tempdir().unwrap();
|
||||
let first = ensure_at(root.path(), "ICE-P-TEST001", &session(), |_, _| {
|
||||
Ok("one".into())
|
||||
})
|
||||
.unwrap();
|
||||
let second = ensure_at(root.path(), "ICE-P-TEST002", &session(), |_, _| {
|
||||
Ok("two".into())
|
||||
})
|
||||
.unwrap();
|
||||
assert_ne!(first.repository_id, second.repository_id);
|
||||
assert!(Path::new(&first.local_path).exists());
|
||||
assert!(Path::new(&second.local_path).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zhizhi_number_is_bound_to_zhizhi_account_and_mingtianjian_channel() {
|
||||
let root = tempdir().unwrap();
|
||||
let mut zhizhi = session();
|
||||
zhizhi.username = "zhizhi".into();
|
||||
let snapshot = ensure_at(root.path(), "ICE-GL-ZHI∞", &zhizhi, |_, name| {
|
||||
assert!(name.contains("明天见频道"));
|
||||
Ok("mingtianjian".into())
|
||||
})
|
||||
.unwrap();
|
||||
let readme = fs::read_to_string(Path::new(&snapshot.local_path).join("README.md")).unwrap();
|
||||
assert!(readme.contains("明天见频道"));
|
||||
|
||||
let mismatch = ensure_at(root.path(), "ICE-GL-ZHI∞", &session(), |_, _| {
|
||||
Ok("must-not-register".into())
|
||||
});
|
||||
assert_eq!(
|
||||
mismatch.unwrap_err(),
|
||||
"HOLOLAKE_LOGIN_ACCOUNT_NUMBER_MISMATCH"
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue