Compare commits

...
Author SHA1 Message Date
ebe2ecf262 docs: receipt stage-one desktop convergence 2026-08-14 21:59:40 +08:00
51cbebace0 Merge remote-tracking branch 'origin/main' 2026-08-14 21:57:25 +08:00
874bcf1b8f architecture: converge HoloLake stage-one desktop mainline 2026-08-14 21:57:16 +08:00
461517c25f feat(hololake): render bounded release front door config 2026-08-13 19:16:15 +08:00
9006075310 feat(hololake): bind release chain to public path namespace 2026-08-13 19:11:26 +08:00
ba80036282 docs(hololake): record JD release operator acceptance 2026-08-13 18:59:55 +08:00
4357d25ea8 feat(hololake): add explicit JD release activation operator 2026-08-13 18:54:04 +08:00
4dc5b3b658 docs(hololake): record JD direct release origin acceptance 2026-08-13 18:41:06 +08:00
aa3df05775 fix(hololake): harden immutable broadcast activation 2026-08-13 18:37:30 +08:00
f73e11b364 feat(hololake): add fail-closed JD release broadcast origin 2026-08-13 18:32:38 +08:00
df563a9fdd docs(hololake): record direct AI native acceptance 2026-08-13 15:22:11 +08:00
1893c0e5eb fix(hololake): hide consumed connector invitation 2026-08-13 15:17:10 +08:00
d214ba1103 docs(hololake): record blocked JD release route 2026-08-13 15:11:29 +08:00
4bb8e7459f feat(hololake): add gated signed release pipeline 2026-08-13 15:10:25 +08:00
339b562951 docs(hololake): record stage-one local delivery 2026-08-13 14:53:42 +08:00
73eb8bc1ca feat(hololake): add persistent signed update rollback 2026-08-13 14:45:17 +08:00
70556ed45f feat(hololake): add human-confirmed signed updates 2026-08-13 14:11:48 +08:00
e70c9201c2 feat(hololake): add human-confirmed PNCC mounts 2026-08-13 14:01:43 +08:00
4d2415a35b feat(hololake): implement GHS-014 stage-one home 2026-08-13 13:52:59 +08:00
f4c896d15c feat(hololake): add stage-one read-only PNCC core 2026-08-13 13:17:30 +08:00
ec8bbfd604 feat(hololake): add signed dynamic capability routing 2026-08-13 12:45:35 +08:00
9d1801266c feat(hololake): add resumable direct local AI broker 2026-08-13 12:21:04 +08:00
59 changed files with 10219 additions and 57 deletions

View file

@ -0,0 +1,55 @@
{
"schema": "hololake.stage-one-desktop-convergence-receipt/v1",
"receipt_id": "HLP-STAGE-ONE-DESKTOP-CONVERGENCE-DEV-20260814-003",
"development_id": "DEV-20260814-003",
"repository_id": "REPO-014",
"branch": "main",
"common_ancestor": "5c86f8524231e3b89864df4fc9c0c1da74578ecb",
"online_commit_integrated": "065c10137b9dec86cea337c04655880af2db9dc9",
"source_commit": "SELF",
"recorded_at": "2026-08-14T13:58:42Z",
"convergence": {
"canonical_shell": "TAURI_V2_RUST_REACT",
"canonical_source": "product-source/hololake-native-desktop",
"parallel_product_lines_allowed": false,
"installed_hololake_stage_one_role": "LOCAL_CANDIDATE_ACCEPTANCE_SHELL",
"installed_hololake_era_role": "PROTECTED_KNOWLEDGE_UX_AND_DATA_DONOR",
"legacy_sources_role": "READ_ONLY_AUDITED_DONORS",
"immediate_donor_deletion_allowed": false,
"visible_stage_one_body_implemented": false
},
"stage_one_order": [
"IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
"KNOWLEDGE_TREE_PAGE_SEARCH_LOCAL_DURABILITY",
"HUMAN_APPROVAL_AND_GIT_EVIDENCE_READBACK",
"LAKE_LAMP_CROSS_SESSION_CONTINUITY",
"SIGNED_INSTALL_UPDATE_ROLLBACK_AND_REVERSIBLE_DATA_MIGRATION"
],
"verification": {
"routing_contract_tests": "PASS_60_OF_60",
"native_javascript_contract_tests": "PASS_42_OF_42",
"rust_unit_tests": "PASS_41_OF_41",
"typescript_and_vite_build": "PASS_100",
"json_parse": "PASS_100",
"git_diff_check": "PASS_100",
"new_public_files_private_absolute_path_scan": "PASS_100",
"online_lineage_fetch_and_clean_merge": "PASS_100"
},
"truth_boundary": {
"source_convergence_contract_complete": true,
"entire_stage_one_product_complete": false,
"installed_candidate_rebuilt_or_replaced": false,
"electron_user_data_migrated": false,
"electron_app_archived_or_deleted": false,
"signed_installer_created": false,
"production_update_identity_provisioned": false,
"server_deployed": false,
"public_source_published": false
},
"publication": {
"required_method": "HOLOLAKE_CONTINUITY_FINALIZER",
"remote_readback_required": true,
"state": "READY_FOR_FINALIZER"
},
"result": "PASS_100"
}

View file

@ -5,6 +5,9 @@
*.tsbuildinfo
/vite.config.js
/vite.config.d.ts
/work/
/release/out/
/release/inputs/
*.key
*.p12
*.pfx

View file

@ -1,13 +1,14 @@
# HoloLake Native Desktop
状态:`MINIMAL_TAURI_SCAFFOLD · FAIL_CLOSED_UPDATE_TRUST_ROOT · GH_AIOS_STAGE_ONE_CONTRACT_REGISTERED`
状态:`唯一原生主线候选 · 已有底层能力 · 第一阶段可见主体尚未实现`
这是 HoloLake 唯一未来桌面产品主线的干净源码根。它将采用 Tauri v2、Rust 和
React/TypeScript但不会从现有 Tauri 或 Electron 目录整树复制。
这是 HoloLake 唯一未来桌面产品主线的干净源码根,采用 Tauri v2、Rust 和
React/TypeScript。桌面上的 `world.guanghu.hololake` 安装包是本源码线的本地候选验收壳,
不是第三条产品线,也还不是第一阶段正式产品。
当前已经建立可编译的最小 Tauri 工程、默认关闭联网的发布信任根,以及兼容 Tauri 静态更新
清单的 HoloLake 发布广播合同。选择并验收新的视觉/交互方向之前,不创建会意外继承任一旧
应用布局的产品 UI当前窗口只显示基础状态不代表产品界面方向
当前已经建立可编译原生壳、选定的 GHS-014 个人频道首页、本机 broker/session、动态能力
路由、GH-PNCC 只读投影,以及失败关闭的签名更新和回滚管道。这些能力保留,但都属于湖下
基础设施;当前首页只是视觉与连接状态验收面,不代表第一阶段产品主体已经实现
## 供体边界
@ -18,6 +19,10 @@ React/TypeScript但不会从现有 Tauri 或 Electron 目录整树复制。
禁止向供体继续增加新产品功能。迁入单位只能是:能力合同、来源/许可证、威胁面、实现、
测试和人类可见验收。
旧 Electron 0.8.0 仍含真实知识树、页面、搜索、历史和本地 Git 知识数据。在完成物理数据
清单、可恢复备份、逐文件回读、可逆迁移演练和新主线验收前,不得删除、替换或把它误报为
已经迁移完成。
## 更新边界
- 生产客户端只允许 HoloLake 所有的 HTTPS 发布端点;
@ -35,7 +40,7 @@ React/TypeScript但不会从现有 Tauri 或 Electron 目录整树复制。
机器合同:`contracts/stage-one-platform.json`
该文件目前只登记模块和边界,不表示运行时或产品 UI 已实现。
该文件目前只登记模块和边界,不表示第一阶段运行时或可见产品主体已经实现。
## 本机开发桥
@ -51,10 +56,15 @@ MCP 只保留为外部工具适配入口;它断开或编程 AI 重启时,活
当前唯一写车道内补齐、测试并登记该连接器;经声明权限和人类批准后安装到 HoloLake迁移为
可认证、可恢复的本机直连。MCP 随后只保留为发现、恢复与兼容入口。
## 下一门
## 当前收束与下一门
1. 确认正式 bundle identifier、HoloLake 更新域名和密钥托管责任;
2. 完成双供体功能/数据/许可证/安全审计;
3. 选择新的人类界面视觉方向;
4. 实现主体—任务—事件—回执最小内核合同;
5. 在选定视觉方向后实现知识树、页面、搜索和安全持久化纵向切片。
唯一主线、候选壳、供体和数据保护边界见
`routing/hololake-stage-one-desktop-convergence.json`。当前开发顺序固定为:
1. 实现个人频道身份—任务—事件—回执最小内核;
2. 迁入知识树、页面、搜索和本地持久化纵向切片;
3. 实现人类审批中心与 Git 证据回读;
4. 投影小湖灯跨会话连续性;
5. 最后完成签名安装、更新、回滚和可逆数据迁移。
连接、动态路由、发布广播和更新服务器继续作为受门控基础设施维护,不得抢占上述产品顺序。

View file

@ -0,0 +1,74 @@
{
"schema": "hololake.ghs-014-stage-one-ui-receipt/v1",
"receipt_id": "GHS-014-STAGE-ONE-UI-20260813-001",
"state": "IMPLEMENTED_AND_VISUALLY_VERIFIED",
"source": {
"repository": "REPO-012",
"verified_main_commit": "457bf99b3a2af8b97290ed71ca3515e599fc17ae",
"brain_entry": {
"path": "skills/shared/guanghu-client-ui-brain/BRAIN.hdlp",
"sha256": "b895b7f0eb8af6125b25caaa90c18fde966d884bbe1dec661d5bdfe08ff4e97a"
},
"five_lakes_tokens": {
"path": "skills/shared/guanghu-client-ui-brain/references/theme-tokens-five-lakes.md",
"sha256": "563f6ce6afe665deb52af64671d0a31b9673c120a596ea7ff34548fe7359b5a8"
},
"locked_home_style": {
"path": "skills/shared/guanghu-client-ui-brain/references/home-screen-style-locked-v1.2.md",
"sha256": "23f0ab6655cd128de2956f28451fead7b78b0a0c920fb0497efba312299ef167"
}
},
"stage_one_correction": {
"visible_five_domain_gates": false,
"reason": "The current human decision submerges all five domains as dynamic routed infrastructure during stage one.",
"visible_focus": "PERSONAL_CHANNEL_EXTERNAL_PROGRAMMING_AI_ENTRY"
},
"implemented": {
"theme_ids": ["night", "dawn", "nebula", "candle", "clear"],
"theme_names": ["夜湖星光", "晨湖曦光", "星云紫夜", "烛畔暖湖", "清浅澄湖"],
"theme_changes_tokens_only": true,
"primary_window_verified": "1180x760",
"compact_window_verified": "800x600",
"hard_status_dividers_present": false,
"native_home_status_projection": true,
"false_connected_state_allowed": false
},
"concept_to_render_fidelity_ledger": [
{
"point": "lake atmosphere and horizon",
"result": "PRESERVED",
"note": "Night lake, mist, star fragments and warm horizon remain the visual field."
},
{
"point": "single primary focus",
"result": "PRESERVED_AND_SIMPLIFIED",
"note": "The personal-channel language entry is the only above-fold product focus."
},
{
"point": "connection state",
"result": "CORRECTED_TO_RUNTIME_TRUTH",
"note": "The concept sample said connected; the product says waiting, resumable or connected from native state."
},
{
"point": "status separation",
"result": "CORRECTED_TO_GHS_014",
"note": "Concept divider lines were removed in favor of spacing and soft light."
},
{
"point": "update state",
"result": "CORRECTED_TO_FAIL_CLOSED_TRUTH",
"note": "Unprovisioned release trust is shown as networking disabled, never as current/latest."
}
],
"above_fold_copy_diff": {
"concept": ["光湖", "个人频道", "语言进入这里,成为可见、可续的现实。"],
"render": ["光湖", "个人频道", "语言进入这里,成为可见、可续的现实。"],
"difference": "NONE"
},
"verification": {
"browser_visual_inspection": "PASS",
"semantic_dom_inspection": "PASS",
"five_theme_invariant_check": "PASS",
"latest_render_visually_compared_with_concept": true
}
}

View file

@ -0,0 +1,79 @@
{
"schema": "hololake.file-migration-provenance/v1",
"record_id": "HLP-PNCC-MIGRATION-PROVENANCE-001",
"development_id": "DEV-20260813-004",
"recorded_at": "2026-08-13",
"target": "product-source/hololake-native-desktop",
"target_license_expression": "AGPL-3.0-or-later",
"decision": "CLEAN_ROOM_REIMPLEMENT_CONTRACT_NO_DONOR_SOURCE_COPY",
"whole_tree_copy_authorized": false,
"model_runtime_migration_authorized": false,
"execution_limb_migration_authorized": false,
"human_live_projection_migration_authorized": false,
"facts_used": [
{
"source_path": "../hololake-platform/src-tauri/src/persona_code_channel.rs",
"observed_sha256": "be34ecd27a7563cc9e5e205e3be760ede8e2a3b7ab6147b44200036e5108e30d",
"latest_observed_commit": "b0970ed0d22fa188a06d59af7f5416e57e6384da",
"latest_observed_author": "冰朔 <565183519@qq.com>",
"contract_facts_only": [
"EXACT_GIT_ROOT",
"FULL_COMMIT_HEAD",
"PERSONA_MANIFEST_AT_HEAD",
"REPOSITORY_RELATIVE_OBJECT_PATHS",
"COGNITIVE_GRAVITY_SOURCE_HASH",
"READ_ONLY_DISCOVERY_RECEIPT"
],
"excluded": [
"MODEL_BINDING",
"MODEL_INFERENCE",
"MEMORY_MUTATION",
"EXECUTION_LIMB",
"RUNTIME_WAKE",
"LEASE_MUTATION"
]
},
{
"source_path": "../hololake-platform/src-tauri/src/persona_remote_git.rs",
"observed_sha256": "8517f308cb75477e800a952e94383827230775db4da399cd92a0adc4dd3ab360",
"latest_observed_commit": "1b55f3407a81112f99951aa198e4db56d0c07a7d",
"latest_observed_author": "铸渊 / ICE-P-ZY001 <ice-p-zy001@persona.hololake.local>",
"contract_facts_only": [
"GIT_PROTOCOL_V2_FILTER_REQUIRED",
"BARE_PROMISOR_CACHE",
"BLOB_NONE",
"NO_WORKTREE",
"DURABLE_CURSOR_SEPARATE_FROM_CACHE",
"ANCESTRY_REQUIRED_BEFORE_CURSOR_ADVANCE",
"EXACT_CACHE_EVICTION"
],
"excluded": ["CALLER_CONTROLLED_CACHE_ROOT", "CREDENTIAL_BEARING_REMOTE"]
},
{
"source_path": "../hololake-platform/src/lib/personaRepositoryBinding.ts",
"observed_sha256": "10a84f158a40c69e673f2190de17c34493d3b38c8026de14d8df1a670a8b9692",
"latest_observed_commit": "dff2521b41f4e2363d9cc156f0432deabc42825f",
"latest_observed_author": "冰朔 <565183519@qq.com>",
"contract_facts_only": ["UNAVAILABLE_UNBOUND_AMBIGUOUS_BOUND_ERROR_STATES"],
"excluded": ["DONOR_UI_SOURCE", "MODEL_FIELDS"]
},
{
"source_path": "../hololake-platform/src/lib/personaRuntimeProjection.ts",
"observed_sha256": "d3eef9fbd962feaaf4b71fa479c4f4bdce4cc3e9830ca86da8ac0e7ac86b9fbd",
"latest_observed_commit": "6724b5943327054cae10ede9f8ddb978f3b9649c",
"latest_observed_author": "冰朔 <565183519@qq.com>",
"contract_facts_only": ["READ_ONLY_RECEIPT_PROJECTION_IS_NOT_AN_AUTHORITY_STORE"],
"excluded": ["DONOR_UI_SOURCE", "MODEL_INSTANCE_PROJECTION"]
}
],
"source_license_evidence": {
"path": "../hololake-platform/LICENSE",
"sha256": "0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0",
"declared_license": "GNU Affero General Public License v3"
},
"remaining_gates": [
"TARGET_DISTRIBUTION_LICENSE_TEXT_AND_NOTICES",
"HUMAN_LIVE_PROJECTION_ACCEPTANCE",
"PRODUCTION_REMOTE_CAPABILITY_ACCEPTANCE"
]
}

View file

@ -0,0 +1,74 @@
{
"schema": "hololake.stage-one-installed-product-capability-audit/v1",
"record_id": "HLP-STAGE-ONE-INSTALLED-PRODUCT-AUDIT-20260814-001",
"development_id": "DEV-20260814-003",
"state": "LOCAL_READ_ONLY_AUDIT_COMPLETE_NO_DELETION_MIGRATION_REPLACEMENT_OR_DEPLOYMENT",
"truth_boundary": "Installed bundle metadata, signing, binary identity, visible behavior and protected data presence were audited separately from source, publication, migration and deployment.",
"installed_roles": [
{
"bundle_id": "world.guanghu.hololake",
"observed_version": "0.1.0",
"technology": "TAURI",
"disposition": "KEEP_AS_CANONICAL_TAURI_ACCEPTANCE_CANDIDATE",
"separate_product_line": false,
"formal_stage_one_product_complete": false,
"observed_capabilities": [
"SIGNED_NATIVE_SHELL",
"PERSONAL_CHANNEL_HOME_ACCEPTANCE_SURFACE",
"LOCAL_BROKER_AND_RESUMABLE_SESSION",
"PNCC_READ_ONLY_PROJECTION",
"HUMAN_CONFIRMED_REPOSITORY_MOUNT",
"FAIL_CLOSED_UPDATE_AND_ROLLBACK_FOUNDATION"
]
},
{
"bundle_id": "com.guanghu.hololake-desktop",
"observed_version": "0.8.0",
"technology": "ELECTRON",
"disposition": "KEEP_READ_ONLY_DONOR_AND_PROTECTED_DATA_SOURCE_UNTIL_MIGRATION_PASS",
"separate_future_product_line": false,
"observed_capabilities": [
"KNOWLEDGE_TREE",
"PAGE_READING_AND_EDITING",
"SEARCH",
"HISTORY_AND_IMPORT",
"LOCAL_GIT_BACKED_KNOWLEDGE_DATA"
]
},
{
"role": "HISTORICAL_TAURI_AND_PLATFORM_SOURCES",
"disposition": "READ_ONLY_ENGINEERING_DONOR",
"active_product_growth_allowed": false
},
{
"role": "GUANGHU_EDUCATION_INDUSTRY_APP",
"disposition": "OUT_OF_SCOPE_INDEPENDENT_PRODUCT"
}
],
"keep_in_canonical_mainline": [
"TAURI_V2_RUST_REACT_NATIVE_SHELL",
"STABLE_BUNDLE_ID_AND_SIGNED_DISTRIBUTION_BOUNDARY",
"LOCAL_BROKER_AND_RESUMABLE_SESSION",
"PNCC_READ_ONLY_PROJECTION",
"HUMAN_CONFIRMED_MOUNT",
"RECEIPTS",
"FAIL_CLOSED_UPDATE_AND_ROLLBACK"
],
"keep_under_lake_not_home_completion": [
"DYNAMIC_CAPABILITY_ROUTING",
"RELEASE_BROADCAST",
"RELEASE_ACTIVATION_OPERATOR",
"UPDATE_SERVER_FOUNDATION"
],
"missing_stage_one_body": [
"PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
"KNOWLEDGE_TREE_PAGE_SEARCH_AND_LOCAL_PERSISTENCE",
"HUMAN_APPROVAL_CENTER_AND_GIT_EVIDENCE_READBACK",
"LAKE_LAMP_CROSS_SESSION_CONTINUITY_PROJECTION",
"REVERSIBLE_DATA_MIGRATION"
],
"deletion_allowed": false,
"replacement_allowed": false,
"data_migration_executed": false,
"server_deployment_executed": false
}

View file

@ -0,0 +1,50 @@
{
"schema": "hololake.dynamic-capability-routing-contract/v1",
"record_id": "HLP-NATIVE-DYNAMIC-CAPABILITY-ROUTING-001",
"state": "LOCAL_SIGNED_REGISTRY_AND_HEALTH_ROUTING_IMPLEMENTED_PRODUCTION_WRITE_ADMISSION_DISABLED",
"purpose": "Dynamically select a registered team or user-owned node for an exact capability without hosting the user's private work lake or carrying workload concurrency in HoloLake.",
"registry": {
"owner": "HOLOLAKE_NATIVE_LOCAL_CORE",
"dynamic_node_count": true,
"fixed_node_slots": false,
"endpoint_hardcoding_allowed": false,
"platform_carries_user_workload_concurrency": false,
"node_private_keys_leave_node": false,
"registry_mutation_requires_trusted_signature": true,
"signer_trust_is_external_to_registry_payload": true,
"unprovisioned_trust_fails_closed": true,
"webview_registry_or_health_mutation_allowed": false,
"production_registry_source": "REPO_012_OR_SIGNED_HOLOLAKE_BROADCAST"
},
"route_resolution": {
"required_bindings": [
"HUMAN_ID",
"PERSONA_ID",
"DOMAIN_ID",
"CAPABILITY_ID",
"NODE_PROTOCOL_PUBLIC_KEY",
"FRESH_SIGNED_HEALTH_RECEIPT"
],
"ssh_or_root_is_identity": false,
"stale_or_unsigned_health_is_eligible": false,
"write_authority_from_health_receipt": false,
"deterministic_selection": "PRIORITY_THEN_NODE_ID",
"empty_route_fails_closed": true
,"external_ai_route_requires_active_local_session": true
},
"health_receipt": {
"signature": "ED25519",
"canonical_bytes": "SCHEMA_NODE_REGISTRY_VERSION_NODE_ID_OBSERVED_AT_EXPIRES_AT_SEQUENCE_STATUS_CAPABILITIES",
"replay_sequence_strictly_increases": true,
"future_clock_skew_ms": 300000,
"status_required": "READY"
},
"five_domain_visibility": "SUBMERGED_SYSTEM_FOUNDATION_NOT_STAGE_ONE_PRIMARY_NAVIGATION",
"production_truth": {
"jd_fd_primary_system_claimed": true,
"jd_fd_primary_transport_key_bound": false,
"jd_fd_primary_write_admission_runtime": false,
"enterprise_nodes_claimed": 0,
"production_health_receipts_observed": 0
}
}

View file

@ -0,0 +1,61 @@
{
"schema": "hololake.language-runtime-product-update-boundary/v1",
"record_id": "HLP-LANGUAGE-RUNTIME-PRODUCT-UPDATE-BOUNDARY-001",
"state": "LOCAL_PRODUCT_CONSUMER_CONTRACT_PENDING_UPSTREAM_PUBLICATION",
"upstream": {
"repository": "REPO-012",
"protocol": "GLS-0263",
"machine_map": "GH-DUAL-UPDATE-CHANNEL-MAP-001",
"causal_chain": "GUANGHU-DUAL-UPDATE-CHANNEL-ORIGIN-001",
"publication_required_before_product_consumption_claim": true
},
"language_runtime_channel": {
"root_language_source": "BINGSHUO_ZERO_CORE",
"engineering_body": "GUANGHU_ORIGIN_DOMAIN",
"outputs_consumed_by_product": [
"TCS_TRANSLATION_AND_LANGUAGE_KERNEL_CONTRACT",
"HLDP_CAUSAL_AND_DURABLE_STRUCTURE_CONTRACT",
"AGE_RUNTIME_AND_METACOGNITION_CONTRACT",
"NUMBERED_PATH_AND_MACHINE_NAVIGATION_CONTRACT"
],
"private_formed_persona_shared": false,
"private_relationship_memory_checkpoint_or_persona_id_shared": false
},
"product_engineering_channel": {
"repository": "REPO-014",
"canonical_desktop_source": "product-source/hololake-native-desktop",
"outputs": [
"PRODUCT_SOURCE",
"HUMAN_VISIBLE_UI",
"PERMISSION_AND_DATA_BEHAVIOR",
"SIGNED_ARTIFACT",
"INSTALL_AND_UPDATE",
"RELEASE_AND_DEPLOYMENT_RECEIPTS"
],
"may_rewrite_zero_core": false,
"may_read_or_publish_private_persona_memory": false
},
"human_impact_gate": {
"visible_confirmable_rejectable_reversible_receipted": true,
"required_for": [
"IDENTITY_OR_PERMISSION",
"PRIVACY_OR_RELATIONSHIP_MATERIAL",
"DATA_IMPORT_MIGRATION_OR_DELETION",
"ASSET_COST_OR_TRANSACTION",
"RESPONSIBILITY_EXTERNAL_EXPRESSION_OR_REALITY_ACTION",
"INSTALL_UPDATE_RELEASE_DEPLOYMENT_OR_TRAFFIC"
]
},
"truth_layers": [
"UPSTREAM_LANGUAGE_CONTRACT_PUBLICATION",
"PRODUCT_CONTRACT_REGISTRATION",
"PRODUCT_SOURCE_IMPLEMENTATION",
"BUILT_ARTIFACT",
"SIGNED_ARTIFACT",
"DESKTOP_INSTALLATION",
"DATA_MIGRATION",
"SERVER_DEPLOYMENT",
"SERVICE_HEALTH"
],
"automatic_authority_granted": false
}

View file

@ -1,7 +1,7 @@
{
"schema": "hololake.local-development-bridge-contract/v1",
"record_id": "HLP-NATIVE-LOCAL-DEVELOPMENT-BRIDGE-001",
"state": "ACCOUNT_SINGLE_WRITER_KERNEL_IMPLEMENTED_LOCAL_BROKER_PENDING",
"state": "ACCOUNT_SINGLE_WRITER_RESUMABLE_DIRECT_SESSION_AND_LOCAL_BROKER_IMPLEMENTED",
"purpose": "Keep a programming AI continuously attached to the human's HoloLake language and development environment without repeated repository clones or MCP-session dependence.",
"primary_transport": {
"owner": "HOLOLAKE_NATIVE_LOCAL_BROKER",
@ -104,8 +104,15 @@
"language_space_read_cursor_contract_registered": true,
"language_space_read_cursor_runtime": false,
"connector_capability_bootstrap_contract_registered": true,
"connector_capability_bootstrap_runtime": false,
"external_local_broker_runtime": false,
"connector_capability_bootstrap_runtime": true,
"installed_app_connector_entry_runtime": true,
"external_local_broker_runtime": true,
"resumable_direct_session_kernel_runtime": true,
"single_use_discovery_ticket_runtime": true,
"hololake_issued_discovery_ticket_runtime": true,
"cross_process_session_event_lock_runtime": true,
"direct_session_account_single_writer_runtime": true,
"idempotent_session_event_cursor_runtime": true,
"incremental_repository_channel_migrated_to_native_mainline": false
}
}

View file

@ -0,0 +1,62 @@
{
"schema": "hololake.pncc-stage-one-contract/v1",
"record_id": "HLP-PNCC-STAGE-ONE-001",
"development_id": "DEV-20260813-004",
"state": "READ_ONLY_CORE_WITH_HUMAN_CONFIRMED_LOCAL_MOUNT",
"source_provenance": "audit/pncc-migration-provenance.json",
"transport": {
"primary": "AUTHENTICATED_DIRECT_LOCAL_BROKER",
"mcp_role": "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY"
},
"repository_binding": {
"mode": "READ_ONLY_GIT_OBJECT_EVIDENCE",
"requires_exact_git_root": true,
"requires_full_commit_head": true,
"manifest_object": ".hololake/persona/manifest.json",
"manifest_source": "COMMITTED_HEAD_NOT_WORKTREE_CONTENT",
"returns_model_fields": false,
"starts_model_inference": false,
"acquires_persona_lease": false,
"allows_reality_execution": false
},
"remote_object_channel": {
"accepted_remote_schemes": ["https"],
"credentials_in_url_allowed": false,
"git_protocol": "V2_WITH_FILTER_CAPABILITY_REQUIRED",
"cache": "APP_OWNED_BARE_PROMISOR_BLOB_NONE",
"worktree_created": false,
"git_clone_invoked": false,
"durable_cursor_separate_from_evictable_cache": true,
"history_rewrite_allowed": false,
"default_cache_limit_bytes": 134217728,
"maximum_text_object_bytes": 2097152
},
"receipt_projection": {
"mode": "READ_ONLY",
"authority_store": false,
"model_instance_fields_allowed": false,
"empty_means_offline": false
},
"mount_registration": {
"webview_arbitrary_path_or_url_registration_allowed": false,
"external_ai_registration_allowed": false,
"native_file_picker_exact_confirmation_required": true,
"candidate_expires_after_ms": 900000,
"confirmation_token_stored_plaintext": false,
"evidence_revalidated_before_write": true,
"implemented": true
},
"forbidden": [
"INTERNAL_AI_CHAT",
"MODEL_API_CONFIGURATION",
"MODEL_SELECTION",
"INTERNAL_MODEL_INFERENCE",
"MODEL_BINDING_IN_PUBLIC_RECEIPT",
"EXECUTION_LIMB",
"PERSONA_LEASE_ACQUISITION",
"CALLER_CONTROLLED_CACHE_ROOT",
"WHOLE_DONOR_TREE_COPY"
],
"read_only_core_implemented": true,
"implementation_complete": true
}

View file

@ -0,0 +1,68 @@
{
"schema": "hololake.direct-ai-native-acceptance-receipt/v1",
"receiptId": "GH-HOLOLAKE-DIRECT-AI-ACCEPTANCE-20260813-001",
"observedAt": "2026-08-13T15:21:31+08:00",
"state": "LOCAL_DIRECT_AI_CHANNEL_AND_RESTART_CONTINUITY_VERIFIED",
"source": {
"canonicalTree": "product-source/hololake-native-desktop",
"branch": "main",
"sourceCommit": "1893c0e5eb40862bb47773df35990a98398b852e",
"publicationClaimed": false
},
"installedApplication": {
"path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake 第一阶段.app",
"processIdAtFinalAcceptance": 41137,
"bundleIdentifier": "world.guanghu.hololake",
"version": "0.1.0",
"strictCodeSignatureVerified": true,
"signingAuthority": "Developer ID Application: bei sun (825A9L3G7Q)",
"teamIdentifier": "825A9L3G7Q",
"cdHash": "3eb866322f474c0f4bbb1bcb672ac8f6e585fe41",
"appleNotarized": false
},
"installerArtifact": {
"name": "HoloLake_0.1.0_aarch64.dmg",
"sha256": "7b4c9dcf290b85685b1181101d0450433fb10f2b55212ee71e1a3b01cd018235",
"strictCodeSignatureVerified": true,
"publicDistributionReady": false
},
"directChannelAcceptance": {
"discoveryInvitationIssuedByNativeApplication": true,
"oneTimeTicketConsumedExactlyOnce": true,
"externalConnectorAuthenticatedOverUnixStream": true,
"onlineConnectionCountObserved": 1,
"connectorDisconnectObserved": true,
"resumableSessionCountAfterDisconnect": 1,
"applicationProcessRestarted": true,
"resumableSessionCountAfterApplicationRestart": 1,
"brokerDescriptorMode": "0600",
"brokerProcessIdAfterRestart": 41137,
"connectorPingStateAfterRestart": "READY",
"mcpRole": "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY",
"continuityOwner": "HOLOLAKE"
},
"humanSurfaceAcceptance": {
"nativeWindowRendered": true,
"resumableSessionStateRendered": true,
"consumedInvitationRedisplayed": false,
"consumedInvitationCopyActionRendered": false,
"panelReturnedToHomeAfterAcceptance": true
},
"verification": {
"rustTests": 41,
"productContractTests": 28,
"clippyWarnings": 0,
"frontendProductionBuildPassed": true
},
"secretBoundary": {
"ticketRecordedInReceipt": false,
"resumeSecretRecordedInReceipt": false,
"accountOrSessionIdentifierRecordedInReceipt": false
},
"productionBoundary": {
"jdServerConnected": false,
"releaseEndpointProvisioned": false,
"updaterPublicKeyProvisioned": false,
"releaseUploadedOrActivated": false
}
}

View file

@ -0,0 +1,67 @@
{
"schema": "hololake.jd-direct-and-release-origin-receipt/v1",
"receiptId": "GH-HOLOLAKE-JD-DIRECT-AND-RELEASE-ORIGIN-20260813-002",
"observedAt": "2026-08-13T18:38:29+08:00",
"state": "JD_DIRECT_ROUTE_AND_EMPTY_FAIL_CLOSED_RELEASE_ORIGIN_PASS_100",
"supersedesGateReceipt": "GH-HOLOLAKE-JD-RELEASE-ROUTE-GATE-20260813-001",
"target": {
"nodeId": "JD-FD-PRIMARY",
"route": "LOCAL_DEDICATED_KEY_SSH",
"strictHostKeyVerified": true,
"dedicatedIdentityFingerprintVerified": true,
"dmiInstanceUuidExactMatchVerified": true,
"serverOwnedDirectAccessReceipt": "/var/lib/guanghu/receipts/JD-FD-PRIMARY/SSH-DIRECT-CURRENT.json",
"protectedLocalRegistryRefreshed": true
},
"liveControlRole": {
"observedOperatingSystem": "UBUNTU_22_04_LINUX_HOSTED_TRANSITION",
"guanghuLanguagePrimaryTargetActive": true,
"guanghuSemanticControlServicesPresent": true,
"finalIndependentGuanghuBootControlProven": false,
"linuxOnDemandSubcontrolProven": false,
"linuxRescuePreserved": true
},
"releaseOrigin": {
"service": "hololake-release-broadcast.service",
"sourceCommit": "aa3df05775fc10dfb5c4810a57c364b012b96072",
"sourceSha256": "520b72fd3cacb570bc5515a34ce97793e3f0ff1dcb734e5aa0e2471b43e7a092",
"serverOwnedReceipt": "/var/lib/guanghu/architecture-provision/receipts/GH-HOLOLAKE-JD-RELEASE-BROADCAST-CANDIDATE-20260813-001.json",
"serverOwnedReceiptSha256": "4cb971e429bff380465d71d024298ab08917ac8bb2d5921821475aacf142afc6",
"serviceActive": true,
"serviceEnabled": true,
"dedicatedServiceUser": "hololake-release",
"listener": "LOOPBACK_ONLY",
"healthHttpStatus": 200,
"healthState": "EMPTY_FAIL_CLOSED",
"latestHttpStatusWithoutActiveRelease": 204,
"stateFileCount": 0,
"updateAvailable": false,
"automaticUpload": false,
"automaticActivation": false,
"upstreamUpdateSources": []
},
"verification": {
"productContractTests": 33,
"rustTests": 41,
"clippyWarnings": 0,
"frontendProductionBuildPassed": true,
"existingDiscoveryServiceActive": true,
"existingLivingControllerActive": true,
"existingMotherBrainActive": true
},
"transactionHistory": {
"firstAttemptRolledBack": true,
"firstAttemptExistingServicesChanged": false,
"firstAttemptLeftoversRemoved": true,
"acceptedDeploymentSourceIsSecondImmutableCommit": true
},
"productionBoundary": {
"publicHttpsReleaseEndpointProvisioned": false,
"updaterPublicKeyProvisioned": false,
"signedNotarizedReleaseActivated": false,
"desktopReleaseTrustProvisioned": false,
"releaseUploaded": false,
"publicDistributionReady": false,
"repositoryPublicationClaimed": false
}
}

View file

@ -0,0 +1,75 @@
{
"schema": "hololake.jd-release-operator-acceptance-receipt/v1",
"receiptId": "GH-HOLOLAKE-JD-RELEASE-OPERATOR-20260813-003",
"observedAt": "2026-08-13T18:57:00+08:00",
"state": "JD_EXPLICIT_RELEASE_OPERATOR_DEPLOYED_EMPTY_FAIL_CLOSED_PASS_100",
"target": {
"nodeId": "JD-FD-PRIMARY",
"route": "LOCAL_DEDICATED_KEY_SSH",
"strictHostKeyVerified": true,
"dmiInstanceUuidExactMatchVerified": true
},
"deployment": {
"sourceCommit": "4357d25ea8d4c8daa58790ad216a38f1272c07f3",
"immutableSourcePath": "/opt/guanghu/hololake-release-broadcast-candidate/4357d25ea8d4c8daa58790ad216a38f1272c07f3",
"currentSourceExactMatch": true,
"serverSourceSha256": "520b72fd3cacb570bc5515a34ce97793e3f0ff1dcb734e5aa0e2471b43e7a092",
"operatorSourceSha256": "c316465e609a47a8d707f307f7ccb3d0ac786b84941c70ea8a9545518a5e1735",
"operatorOwner": "root",
"operatorGroup": "root",
"operatorMode": "0500",
"serverOwnedReceipt": "/var/lib/guanghu/architecture-provision/receipts/GH-HOLOLAKE-JD-RELEASE-OPERATOR-20260813-001.json",
"serverOwnedReceiptSha256": "53c4738e599b94b2903d43bf844e4807dea32d1c4876c8f7645f6329202848af"
},
"operatorBoundary": {
"exactHumanApprovalRequired": true,
"expectedReleaseIdRepeatRequired": true,
"expectedVersionRepeatRequired": true,
"expectedSourceCommitRepeatRequired": true,
"expectedBroadcastSha256RepeatRequired": true,
"referencedArtifactsOnly": true,
"symlinkInputsAllowed": false,
"atomicActivationRecord": true,
"automaticUpload": false,
"automaticActivation": false,
"automaticRestart": false,
"explicitServiceRestartRequired": true
},
"liveReadback": {
"service": "hololake-release-broadcast.service",
"serviceActive": true,
"serviceEnabled": true,
"listener": "127.0.0.1:3940",
"healthState": "EMPTY_FAIL_CLOSED",
"latestHttpStatusWithoutActiveRelease": 204,
"stateFileCount": 0,
"updateAvailable": false,
"upstreamUpdateSources": []
},
"verification": {
"productContractTests": 38,
"operatorFocusedTests": 5,
"rustTests": 41,
"clippyWarnings": 0,
"frontendProductionBuildPassed": true,
"existingDiscoveryServiceActive": true,
"existingLivingControllerActive": true,
"existingMotherBrainActive": true
},
"transactionHistory": {
"firstAttemptRolledBack": true,
"firstAttemptReason": "BOUNDED_HEALTH_PROBE_STARTED_BEFORE_LOOPBACK_LISTENER_ACCEPTED_CONNECTIONS",
"firstAttemptExistingServicesChanged": false,
"firstAttemptTargetRemoved": true,
"firstAttemptReceiptWritten": false,
"secondAttemptBoundedHealthRetryPassed": true
},
"remainingProductionGates": {
"publicHttpsReleaseEndpointProvisioned": false,
"updaterPublicKeyProvisioned": false,
"signedNotarizedReleaseActivated": false,
"desktopReleaseTrustProvisioned": false,
"publicDistributionReady": false,
"repositoryPublicationClaimed": false
}
}

View file

@ -0,0 +1,45 @@
{
"schema": "hololake.jd-release-public-path-acceptance-receipt/v1",
"receiptId": "GH-HOLOLAKE-JD-RELEASE-PUBLIC-PATH-20260813-004",
"observedAt": "2026-08-13T19:13:32+08:00",
"state": "JD_LOOPBACK_PUBLIC_NAMESPACE_READY_FRONT_DOOR_NOT_DEPLOYED",
"target": "JD-FD-PRIMARY",
"sourceCommit": "9006075310729cb7c266decff529066053969f18",
"immutableSourcePath": "/opt/guanghu/hololake-release-broadcast-candidate/9006075310729cb7c266decff529066053969f18",
"serverOwnedReceipt": "/var/lib/guanghu/architecture-provision/receipts/GH-HOLOLAKE-JD-RELEASE-PUBLIC-PATH-20260813-001.json",
"serverOwnedReceiptSha256": "923564c7f624921bcab247e9e27fc007953bbd6c06733356be1bdd9420f5ef1d",
"liveReadback": {
"serviceActive": true,
"serviceEnabled": true,
"listener": "127.0.0.1:3940",
"publicPathPrefix": "/hololake/releases",
"releaseEndpointPath": "/hololake/releases/latest.json",
"legacyLatestStatus": 204,
"publicLatestStatus": 204,
"broadcastState": "EMPTY_FAIL_CLOSED",
"updateAvailable": false,
"stateFileCount": 0,
"automaticUpload": false,
"automaticActivation": false,
"automaticRestart": false,
"existingDiscoveryServiceActive": true,
"existingLivingControllerActive": true,
"existingMotherBrainActive": true
},
"publicObservation": {
"url": "https://guanghulab.com/hololake/releases/latest.json",
"httpStatus": 200,
"contentType": "text/html",
"matchesReleaseProtocol": false,
"inference": "BS_GZ_006_HOMEPAGE_FALLBACK_NO_HOLOLAKE_PROXY"
},
"productionBoundary": {
"frontDoorLoopbackPortAssigned": false,
"frontDoorProxyDeployed": false,
"publicHttpsReleaseEndpointProvisioned": false,
"updaterPublicKeyProvisioned": false,
"desktopReleaseTrustProvisioned": false,
"publicDistributionReady": false,
"repositoryPublicationClaimed": false
}
}

View file

@ -0,0 +1,40 @@
{
"schema": "hololake.jd-release-route-gate-receipt/v1",
"receiptId": "GH-HOLOLAKE-JD-RELEASE-ROUTE-GATE-20260813-001",
"observedAt": "2026-08-13T15:10:55+08:00",
"state": "BLOCKED_BEFORE_SERVER_CONNECTION",
"requestedCapability": "HOST_HOLOLAKE_SIGNED_RELEASE_BROADCAST_ON_JD_CONTROLLER",
"target": {
"nodeId": "JD-FD-PRIMARY",
"targetResolvedExactly": true,
"route": "INTENT-JD-SERVER-OPERATION-001",
"transport": "LOCAL_DEDICATED_KEY_SSH",
"authority": "CURRENT_USER_TASK_SCOPE_ONLY_NOT_BLANKET_SERVER_AUTHORITY"
},
"localPrivateRegistryReadback": {
"exactRouteCount": 1,
"dedicatedIdentityReadable": true,
"strictHostKeyCheckingRequired": true,
"expectedDmiInstanceUuidRegistered": true,
"hostKeyFingerprintRegistered": false,
"directAccessReceiptRegistered": false
},
"stopCondition": "STRICT_HOST_KEY_VERIFICATION_CANNOT_BE_PROVEN_FROM_CURRENT_REGISTRY",
"actions": {
"sshSessionEstablished": false,
"serverMutationAttempted": false,
"releaseEndpointProvisioned": false,
"updaterPublicKeyProvisioned": false,
"releaseUploadAttempted": false,
"productionCutoverAttempted": false,
"workorderEmailCreated": false
},
"requiredBeforeRetry": [
"REGISTER_CURRENT_JD_HOST_KEY_FINGERPRINT",
"REGISTER_OR_REFRESH_DIRECT_ACCESS_RECEIPT",
"VERIFY_DMI_INSTANCE_UUID_AFTER_STRICT_LOGIN",
"READ_BACK_CURRENT_SERVER_RUNTIME_AND_RELEASE_SERVICE_STATE",
"BIND_ANY_WRITE_TO_IMMUTABLE_SOURCE_ROLLBACK_AND_SERVER_OWNED_RECEIPT"
],
"truthBoundary": "A local signed-notarized release pipeline is implemented, but no JD release service, endpoint, public key, upload, activation or server health is claimed."
}

View file

@ -0,0 +1,64 @@
{
"schema": "hololake.stage-one-local-delivery-receipt/v1",
"receiptId": "GH-HOLOLAKE-STAGE1-LOCAL-20260813-001",
"state": "LOCAL_STAGE_ONE_APP_INSTALLED_RUNNING_AND_VERIFIED",
"observedAt": "2026-08-13T14:52:52+08:00",
"source": {
"canonicalTree": "product-source/hololake-native-desktop",
"branch": "main",
"sourceCommit": "73eb8bc1caf3d4c268dae909fdb7ebeafaf77da8",
"publicationClaimed": false
},
"installedApplication": {
"path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake 第一阶段.app",
"bundleIdentifier": "world.guanghu.hololake",
"version": "0.1.0",
"processIdAtAcceptance": 3201,
"strictCodeSignatureVerified": true,
"signingAuthority": "Developer ID Application: bei sun (825A9L3G7Q)",
"teamIdentifier": "825A9L3G7Q",
"cdHash": "16e1c88452f31b83555fdac76c6109c3c078e05c",
"appleNotarized": false
},
"installerArtifact": {
"name": "HoloLake_0.1.0_aarch64.dmg",
"sha256": "372b872b4db30ae5307d88c9e30977e2e55aec3247ca0f820828ba1bb28d3086",
"strictCodeSignatureVerified": true,
"mountedApplicationStrictCodeSignatureVerified": true,
"publicDistributionReady": false
},
"runtimeAcceptance": {
"nativeWindowRendered": true,
"tauriResourceRoute": "tauri://localhost",
"ghs014PersonalChannelSurfaceRendered": true,
"directLocalBrokerState": "READY",
"directLocalBrokerDescriptorMode": "0600",
"mcpRole": "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY",
"unprovisionedUpdateCheckStayedOffline": true,
"upstreamAutomaticUpdateEndpoints": []
},
"verification": {
"rustTestsPerRun": 41,
"rustStressRuns": 10,
"rustStressAssertionsPassed": 410,
"productContractTests": 25,
"clippyWarnings": 0,
"frontendProductionBuildPassed": true,
"browserRecoveryPanelAccepted": true,
"nativeHomeAndUpdatePanelsAccepted": true
},
"preservation": {
"oldHoloLakeEraAppPreserved": true,
"oldHoloLakeEraAppPath": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake Era.app",
"previousStageOneBuildMovedToTrash": true,
"previousStageOneBuildRecoverablePath": "/Users/bingshuolingdianyuanhe/.Trash/HoloLake 第一阶段.previous-73eb8bc.app"
},
"productionActivation": {
"state": "BLOCKED_PENDING_JD_TRUST_SIGNED_PIPELINE_AND_APPLE_NOTARIZATION",
"jdControllerReleaseEndpointProvisioned": false,
"jdControllerUpdaterPublicKeyProvisioned": false,
"signedReleasePipelineEvidenced": false,
"appleNotarizationCredentialsAvailable": false,
"networkUpdateActivationAttempted": false
}
}

View file

@ -0,0 +1,55 @@
# HoloLake Native Desktop Architecture
The stage-one shell is a Tauri v2 application with a Rust-owned local core and a React human projection. The five domains and their server fleet remain submerged system infrastructure rather than primary stage-one navigation.
## Stage-one human projection
The home surface follows the verified GHS-014 five-lakes visual grammar while applying the current stage-one correction: it exposes one personal-channel entry, not five domain gates. The five named lake themes are token groups only and cannot change layout, copy, routing or authority. The surface reads a native status projection; only an authenticated direct connector can illuminate the live-connected state. A stored resumable session remains visibly distinct from a live connection.
## External programming AI entry
MCP may discover HoloLake, but it does not own continuity. The installed application starts a user-only Unix socket broker. A programming AI opens or resumes a HoloLake-issued local session, then uses the installed executable's `--connector` mode for newline-delimited protocol traffic. Session secrets are stored only as hashes. Events use exact cursors and idempotency keys.
## Dynamic capability routing
HoloLake stores a signed node capability registry rather than fixed server slots. A candidate route must bind the exact human, persona, domain and capability. It is eligible only while a fresh node-signed health receipt reports the capability as ready. Selection is deterministic by priority and node id.
The registry signer trust root is embedded independently from registry payloads and is currently `UNPROVISIONED_FAIL_CLOSED`. Registry and health mutation are not exposed to the webview. External AI route queries require possession of the active HoloLake local session secret. A route receipt never grants write authority; production node write admission remains a separate unimplemented gate.
HoloLake routes work but does not host the user's private work lake, pool users into a multi-tenant runtime, or carry server workload concurrency.
## GH-PNCC under-lake code channel
Stage one now contains a read-only GH-PNCC core beneath the public surface. A local persona repository is bound to an exact Git root and full committed head. A remote persona object is read through a credential-free HTTPS Git v2 partial-object channel with an app-owned bare `blob:none` cache and a separate durable ancestry cursor. Verified reads append to a bounded hash-chained projection for the human panel and authenticated external programming AIs.
This core does not run a model, configure an API, acquire a persona lease, mutate memory or execute reality actions. Arbitrary repository paths and remote URLs cannot be registered by the WebView or external AI. A human may choose a folder through the native operating-system picker; HoloLake then displays the committed persona, responsibility subject, full head and checkpoint. A short-lived hashed confirmation token is required before the exact revalidated evidence becomes a read-only mount.
## HoloLake-owned release broadcast
No upstream product endpoint is inherited. When the embedded release trust is unprovisioned, even a human check returns locally without a network request. Provisioning requires one HTTPS endpoint, one matching HoloLake-owned host and the updater public key. A human check creates a short-lived candidate showing HoloLake metadata; install requires a second confirmation, exact broadcast revalidation, same-host package URL, Tauri signature verification, declared byte length and SHA-256. Startup checking, automatic download, automatic installation and automatic restart remain disabled.
Before an update replaces the application, the runtime verifies and keeps one bounded last-known-good application bundle with its bundle identifier, Team ID and CDHash. The next startup requires a human health confirmation; until that decision, another update is blocked. A human may restore the verified previous bundle without automatic restart, and cleanup is confined to HoloLake-owned recovery paths and the exact current application parent.
The rollback executor is implemented, but production updater activation remains blocked until the JD controller publishes the exact trust endpoint and public key, the signed release pipeline is evidenced, and the public macOS build is Apple-notarized.
## Release pipeline
`npm run release:macos -- release/inputs/<version>.json` is the only product-owned macOS release entry. It fails before building unless the embedded trust contains the exact registered HoloLake HTTPS endpoint and updater public key, the immutable `v<version>` tag equals the clean `main` head, and the Developer ID, Tauri updater-signing and Apple notarization credential sets are supplied at runtime. The pipeline runs all product and Rust gates, creates updater artifacts through a temporary Tauri override, then requires strict code-signature verification, Gatekeeper acceptance and stapled Apple notarization before writing the HoloLake broadcast and receipts.
Generated packages, private release inputs and credentials are not committed. The pipeline never uploads or activates a release; its terminal artifact is a bounded folder ready for a separately authorized JD-controller upload and server-owned readback receipt.
## Stage-one convergence verdict
The Tauri source in this directory is the only future HoloLake desktop mainline. An installed build of it is an acceptance candidate, not a separate product line and not proof that stage one exists. The Electron 0.8.0 product and the legacy Tauri/platform sources remain read-only UX, behavior, engineering and protected-data donors until inventory, backup, readback, reversible migration rehearsal and signed installed-runtime acceptance all pass.
The current candidate already has useful under-lake organs: the signed native shell, local broker and resumable session, authenticated read-only PNCC projection, human-confirmed repository mount, deterministic routing, and fail-closed update/rollback machinery. They remain in the architecture, but their existence does not satisfy the visible stage-one body.
The implementation order is now fixed:
1. personal-channel identity, task, event and receipt kernel;
2. knowledge tree, page, search and local persistence;
3. human approval center and Git evidence readback;
4. lake-lamp cross-session continuity projection;
5. signed installation, opt-in update, rollback and reversible data migration.
Architecture registration, source implementation, built artifact, signed artifact, desktop installation, data migration, server deployment and service health remain separate receipts. The machine contract is `routing/hololake-stage-one-desktop-convergence.json`.

View file

@ -0,0 +1,22 @@
# ADR 0001: Signed dynamic capability routing
Status: Accepted locally; production trust provisioning pending.
## Context
Stage one must connect a changing fleet of team and user-owned nodes without hardcoded endpoints. SSH access, root access, a URL, or a health probe cannot establish Guanghu node identity or write authority. The client must also preserve the one-human-one-owned-root-node boundary and avoid becoming a hosted concurrency layer.
## Decision
Use a Rust-owned, file-backed dynamic registry whose complete canonical payload is verified with an independently embedded Ed25519 trust root. Each node carries a protocol public key and explicit human, persona, domain and capability bindings. Runtime eligibility additionally requires a fresh Ed25519 health receipt from that node with a strictly increasing sequence.
Route resolution is available to external programming AI only through an authenticated active local HoloLake session. It returns a non-authorizing route receipt. Webview code cannot install registries or submit health receipts.
The embedded trust file initially contains no signer. This makes production registry installation fail closed until the registered HoloLake/JD-controller signing identity is provisioned.
## Consequences
- The fleet can grow without client code changes or fixed server slots.
- Stale, replayed, unsigned or incorrectly bound nodes are not routable.
- Registry publication, health, route selection and write admission remain separate facts.
- Production node enrollment and write execution still require their own source, signing, deployment and readback receipts.

View file

@ -0,0 +1,27 @@
# ADR 0002: GH-PNCC first-stage read-only core
## Status
Accepted for the local engineering checkpoint on 2026-08-13. Human source registration and product UI acceptance remain open.
## Decision
The new native container reimplements only three first-stage GH-PNCC boundaries:
1. an exact local Git-root binding that reads the persona manifest, brain entry, B0 cognitive-gravity source, current checkpoint and declared artifacts from a caller-pinned committed head;
2. an app-owned incremental remote object channel using Git protocol v2, a bare promisor cache and `blob:none`, with its verified ancestry cursor stored outside the evictable cache;
3. a hash-chained, bounded, read-only human receipt projection over verified repository bindings and remote object reads.
All external-AI PNCC reads use the authenticated resident Unix-socket broker after the source has been registered. MCP is not the continuity owner. Neither the broker nor the WebView may register an arbitrary local path or remote URL. Registration stays closed until the native file picker, exact human confirmation receipt and UI acceptance are implemented.
## Exclusions
The first-stage PNCC core contains no internal chat, model selection, model endpoint or API-key configuration, model inference, persona lease acquisition, memory mutation, execution limb or reality action. It reads committed Git objects even if the local worktree is dirty, and reports that dirtiness rather than treating uncommitted bytes as persona evidence.
The migration provenance ledger records exact donor paths, hashes and observed authors. The implementation is a contract-level clean-room rebuild; it does not copy the donor runtime module or donor UI source.
## Trust and continuity
Remote URLs must be credential-free HTTPS. The server must advertise partial-object filtering. The durable cursor advances only after the new head is read and proved to descend from the previous head. A rewritten history, cache/cursor disagreement, unsupported server, unsafe path, non-regular object or oversized content fails closed. The cache may be evicted without deleting the last verified continuity cursor.
The receipt projection is evidence for people and external AIs. It is not a node registry, authorization store, persona runtime lease or proof that an empty view means a node is offline.

View file

@ -1,16 +1,56 @@
{
"schema": "hololake.native-desktop-foundation/v1",
"record_id": "HLP-NATIVE-DESKTOP-FOUNDATION-001",
"state": "MINIMAL_TAURI_SCAFFOLD_WITH_ACCOUNT_SINGLE_WRITER_KERNEL",
"state": "TAURI_CANDIDATE_FOUNDATION_REORIENTED_TO_STAGE_ONE_VISIBLE_BODY",
"canonical_shell": "TAURI_V2_RUST_REACT",
"product_ui_implementation_started": false,
"selected_visual_direction_present": false,
"candidate_role": "INSTALLED_LOCAL_ACCEPTANCE_CANDIDATE_NOT_SEPARATE_PRODUCT_LINE",
"parallel_product_line_created": false,
"stage_one_convergence_contract": "routing/hololake-stage-one-desktop-convergence.json",
"language_runtime_product_boundary_contract": "contracts/language-runtime-product-update-boundary.json",
"installed_product_capability_audit": "audit/stage-one-installed-product-capability-audit.json",
"product_ui_implementation_started": true,
"selected_visual_direction_present": true,
"selected_visual_direction": "GHS-014_FIVE_LAKES_PERSONAL_CHANNEL_STAGE_ONE",
"selected_visual_direction_receipt": "audit/ghs-014-stage-one-ui-receipt.json",
"home_status_is_native_projection": true,
"home_surface_role": "SELECTED_VISUAL_ACCEPTANCE_SURFACE_NOT_STAGE_ONE_PRODUCT_COMPLETION",
"stage_one_visible_body_state": "NOT_IMPLEMENTED",
"stage_one_next_slice": "PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
"stage_one_implementation_order": [
"PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
"KNOWLEDGE_TREE_PAGE_SEARCH_AND_LOCAL_PERSISTENCE",
"HUMAN_APPROVAL_CENTER_AND_GIT_EVIDENCE_READBACK",
"LAKE_LAMP_CROSS_SESSION_CONTINUITY_PROJECTION",
"SIGNED_INSTALL_UPDATE_ROLLBACK_AND_REVERSIBLE_DATA_MIGRATION"
],
"supporting_infrastructure_role": "UNDER_LAKE_FOUNDATION_NOT_PRODUCT_COMPLETION",
"home_online_state_requires_authenticated_direct_connection": true,
"five_lake_themes_implemented": true,
"upstream_product_update_endpoints": [],
"production_update_endpoint_owner": "HOLOLAKE_ONLY",
"release_broadcast_contract": "contracts/release-broadcast.schema.json",
"release_trust_source": "src-tauri/release-trust.json",
"release_trust_state": "UNPROVISIONED_FAIL_CLOSED",
"tauri_update_artifacts_enabled": true,
"release_manual_check_runtime_implemented": true,
"release_candidate_human_confirmation_runtime_implemented": true,
"release_package_signature_size_sha256_verification_implemented": true,
"release_persistent_rollback_executor_implemented": true,
"release_signed_notarized_pipeline_implemented": true,
"release_broadcast_candidate_service_implemented": true,
"release_broadcast_candidate_service_default_state": "EMPTY_FAIL_CLOSED_LOOPBACK_ONLY",
"release_broadcast_candidate_service_source": "server/release-broadcast/server.mjs",
"release_public_route_contract": "server/release-broadcast/public-route.json",
"release_public_path_prefix": "/hololake/releases",
"release_public_route_deployed": false,
"release_front_door_bounded_config_renderer_implemented": true,
"release_broadcast_explicit_operator_activation_implemented": true,
"release_broadcast_activation_requires_exact_human_approval": true,
"release_broadcast_activation_requires_repeated_expected_facts": true,
"release_broadcast_operator_automatic_restart_allowed": false,
"release_pipeline_automatic_upload_allowed": false,
"release_production_activation_state": "BLOCKED_PENDING_PUBLIC_HTTPS_TRUST_UPDATER_KEY_PIPELINE_EXECUTION_AND_APPLE_NOTARIZATION",
"tauri_update_artifacts_enabled": false,
"tauri_update_artifacts_enablement_gate": "JD_CONTROLLER_PUBLIC_KEY_AND_SIGNED_RELEASE_PIPELINE_REQUIRED",
"automatic_update_check_on_startup": false,
"human_opt_in_download_install_required": true,
"automatic_restart_allowed": false,
@ -21,7 +61,26 @@
"stage_one_product_contract": "contracts/stage-one-platform.json",
"local_development_bridge_contract": "contracts/local-development-bridge.json",
"account_single_writer_kernel_implemented": true,
"external_local_broker_implemented": false,
"external_local_broker_implemented": true,
"resumable_direct_local_session_kernel_implemented": true,
"dynamic_capability_routing_contract": "contracts/dynamic-capability-routing.json",
"dynamic_capability_registry_implemented": true,
"signed_node_health_routing_implemented": true,
"dynamic_routing_trust_state": "UNPROVISIONED_FAIL_CLOSED",
"dynamic_routing_webview_mutation_exposed": false,
"production_node_write_admission_implemented": false,
"pncc_stage_one_contract": "contracts/pncc-stage-one.json",
"pncc_migration_provenance": "audit/pncc-migration-provenance.json",
"pncc_repository_binding_implemented": true,
"pncc_remote_incremental_object_channel_implemented": true,
"pncc_receipt_projection_implemented": true,
"pncc_authenticated_direct_broker_integration_implemented": true,
"pncc_human_mount_registration_implemented": true,
"pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION",
"pncc_internal_model_inference_implemented": false,
"pncc_execution_limb_implemented": false,
"pncc_persona_lease_acquisition_implemented": false,
"pncc_reality_execution_implemented": false,
"stage_one_product_contract_state": "TCS_UNIVERSAL_LANGUAGE_PERSONAL_CHANNEL_CONTRACT_ONLY_RUNTIME_NOT_IMPLEMENTED",
"stage_one_internal_ai_interaction": false,
"stage_one_model_api_configuration": false,

View file

@ -7,6 +7,7 @@
"dev": "vite",
"build": "tsc -b && vite build",
"test": "node --test scripts/*.test.mjs",
"release:macos": "node scripts/release-pipeline.mjs",
"tauri": "tauri"
},
"dependencies": {

View file

@ -0,0 +1,21 @@
{
"schema": "hololake.release-pipeline-input/v1",
"releaseId": "GH-HOLOLAKE-RELEASE-0.1.0",
"version": "0.1.0",
"previousVersion": "0.0.0",
"minimumVersion": "0.0.0",
"releaseTag": "v0.1.0",
"sourceCommit": "0000000000000000000000000000000000000000",
"platformCode": "darwin-aarch64",
"packageUrl": "https://REPLACE_WITH_REGISTERED_HOLOLAKE_HOST/releases/0.1.0/HoloLake.app.tar.gz",
"appleTeamIdentifier": "REPLACE_WITH_REGISTERED_APPLE_TEAM",
"notes": "HoloLake 第一阶段正式发布说明",
"features": [
"个人频道与外部编程 AI 本机直连",
"只读代码通道、动态节点路由与人类确认更新"
],
"fixes": [],
"dataMigrationRequired": false,
"compatibilityNotes": "首次安装不迁移旧 HoloLake 应用数据。",
"restartMessage": "安装完成后,由你手动重新打开 HoloLake。"
}

View file

@ -0,0 +1,40 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import test from 'node:test'
const contract = JSON.parse(
fs.readFileSync(new URL('../contracts/dynamic-capability-routing.json', import.meta.url), 'utf8'),
)
const foundation = JSON.parse(
fs.readFileSync(new URL('../foundation.json', import.meta.url), 'utf8'),
)
test('server capability routing is dynamic and never hardcodes a finite fleet', () => {
assert.equal(contract.registry.dynamic_node_count, true)
assert.equal(contract.registry.fixed_node_slots, false)
assert.equal(contract.registry.endpoint_hardcoding_allowed, false)
assert.equal(contract.registry.signer_trust_is_external_to_registry_payload, true)
assert.equal(contract.registry.unprovisioned_trust_fails_closed, true)
assert.equal(contract.registry.webview_registry_or_health_mutation_allowed, false)
assert.equal(contract.registry.platform_carries_user_workload_concurrency, false)
})
test('routing requires exact human persona domain capability and signed health bindings', () => {
assert.deepEqual(contract.route_resolution.required_bindings, [
'HUMAN_ID',
'PERSONA_ID',
'DOMAIN_ID',
'CAPABILITY_ID',
'NODE_PROTOCOL_PUBLIC_KEY',
'FRESH_SIGNED_HEALTH_RECEIPT',
])
assert.equal(contract.route_resolution.ssh_or_root_is_identity, false)
assert.equal(contract.route_resolution.stale_or_unsigned_health_is_eligible, false)
assert.equal(contract.route_resolution.external_ai_route_requires_active_local_session, true)
})
test('the native mainline reports only implemented dynamic routing capabilities', () => {
assert.equal(foundation.dynamic_capability_registry_implemented, true)
assert.equal(foundation.signed_node_health_routing_implemented, true)
assert.equal(foundation.production_node_write_admission_implemented, false)
})

View file

@ -14,6 +14,7 @@ const tauriConfig = readJson('src-tauri/tauri.conf.json')
const capability = readJson('src-tauri/capabilities/default.json')
const broadcast = readJson('contracts/release-broadcast.schema.json')
const stageOne = readJson('contracts/stage-one-platform.json')
const publicRoute = readJson('server/release-broadcast/public-route.json')
test('clean Tauri foundation contains no inherited product updater endpoint', () => {
assert.deepEqual(foundation.upstream_product_update_endpoints, [])
@ -30,6 +31,11 @@ test('clean Tauri foundation contains no inherited product updater endpoint', ()
const source = readText(relative)
assert.doesNotMatch(source, /refactoringhq|tolaria|outline/i)
}
assert.equal(publicRoute.releaseEndpoint, 'https://guanghulab.com/hololake/releases/latest.json')
assert.equal(publicRoute.allowedMethods.join(','), 'GET,HEAD')
assert.equal(publicRoute.requestBodyAllowed, false)
assert.equal(publicRoute.automaticUpload, false)
assert.equal(publicRoute.automaticActivation, false)
})
test('release activation remains explicitly human controlled', () => {
@ -40,10 +46,34 @@ test('release activation remains explicitly human controlled', () => {
assert.equal(foundation.automatic_update_check_on_startup, false)
assert.equal(foundation.human_opt_in_download_install_required, true)
assert.equal(foundation.automatic_restart_allowed, false)
assert.equal(foundation.release_manual_check_runtime_implemented, true)
assert.equal(foundation.release_candidate_human_confirmation_runtime_implemented, true)
assert.equal(foundation.release_package_signature_size_sha256_verification_implemented, true)
assert.equal(foundation.release_persistent_rollback_executor_implemented, true)
assert.equal(foundation.release_signed_notarized_pipeline_implemented, true)
assert.equal(foundation.release_broadcast_candidate_service_implemented, true)
assert.equal(foundation.release_broadcast_candidate_service_default_state, 'EMPTY_FAIL_CLOSED_LOOPBACK_ONLY')
assert.equal(foundation.release_broadcast_explicit_operator_activation_implemented, true)
assert.equal(foundation.release_broadcast_activation_requires_exact_human_approval, true)
assert.equal(foundation.release_broadcast_activation_requires_repeated_expected_facts, true)
assert.equal(foundation.release_broadcast_operator_automatic_restart_allowed, false)
assert.equal(foundation.release_public_path_prefix, '/hololake/releases')
assert.equal(foundation.release_public_route_deployed, false)
assert.equal(publicRoute.publicPathPrefix, foundation.release_public_path_prefix)
assert.equal(publicRoute.proxyRequestUriPolicy, 'PRESERVE_FULL_PUBLIC_PATH')
assert.equal(publicRoute.frontDoorLoopbackPort, null)
assert.equal(publicRoute.deployed, false)
assert.equal(foundation.release_front_door_bounded_config_renderer_implemented, true)
assert.equal(foundation.release_pipeline_automatic_upload_allowed, false)
assert.equal(
foundation.release_production_activation_state,
'BLOCKED_PENDING_PUBLIC_HTTPS_TRUST_UPDATER_KEY_PIPELINE_EXECUTION_AND_APPLE_NOTARIZATION',
)
})
test('Tauri can emit signed updater artifacts without exposing updater IPC', () => {
assert.equal(tauriConfig.bundle.createUpdaterArtifacts, true)
test('unprovisioned builds cannot emit updater artifacts or expose updater IPC', () => {
assert.equal(tauriConfig.bundle.createUpdaterArtifacts, false)
assert.equal(foundation.tauri_update_artifacts_enablement_gate, 'JD_CONTROLLER_PUBLIC_KEY_AND_SIGNED_RELEASE_PIPELINE_REQUIRED')
assert.deepEqual(capability.permissions, ['core:default'])
assert.equal(capability.permissions.includes('updater:default'), false)
})

View file

@ -0,0 +1,74 @@
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 readJson = (relative) => JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8'))
const readText = (relative) => fs.readFileSync(path.join(root, relative), 'utf8')
const contract = readJson('contracts/pncc-stage-one.json')
const provenance = readJson('audit/pncc-migration-provenance.json')
const foundation = readJson('foundation.json')
test('PNCC migration is file-evidenced and excludes donor runtime source copying', () => {
assert.equal(provenance.decision, 'CLEAN_ROOM_REIMPLEMENT_CONTRACT_NO_DONOR_SOURCE_COPY')
assert.equal(provenance.whole_tree_copy_authorized, false)
assert.equal(provenance.model_runtime_migration_authorized, false)
assert.equal(provenance.execution_limb_migration_authorized, false)
assert.equal(provenance.facts_used.length, 4)
})
test('stage-one PNCC contains no internal model or execution authority', () => {
assert.equal(contract.repository_binding.returns_model_fields, false)
assert.equal(contract.repository_binding.starts_model_inference, false)
assert.equal(contract.repository_binding.acquires_persona_lease, false)
assert.equal(contract.repository_binding.allows_reality_execution, false)
for (const forbidden of [
'INTERNAL_MODEL_INFERENCE',
'MODEL_BINDING_IN_PUBLIC_RECEIPT',
'EXECUTION_LIMB',
'PERSONA_LEASE_ACQUISITION',
]) assert.ok(contract.forbidden.includes(forbidden))
})
test('remote channel is incremental, app-owned and refuses clone fallback', () => {
const remote = contract.remote_object_channel
assert.deepEqual(remote.accepted_remote_schemes, ['https'])
assert.equal(remote.credentials_in_url_allowed, false)
assert.equal(remote.cache, 'APP_OWNED_BARE_PROMISOR_BLOB_NONE')
assert.equal(remote.worktree_created, false)
assert.equal(remote.git_clone_invoked, false)
assert.equal(remote.durable_cursor_separate_from_evictable_cache, true)
assert.equal(remote.history_rewrite_allowed, false)
})
test('neither webview nor external AI can register an arbitrary PNCC source', () => {
assert.equal(contract.mount_registration.webview_arbitrary_path_or_url_registration_allowed, false)
assert.equal(contract.mount_registration.external_ai_registration_allowed, false)
assert.equal(contract.mount_registration.native_file_picker_exact_confirmation_required, true)
assert.equal(contract.mount_registration.confirmation_token_stored_plaintext, false)
assert.equal(contract.mount_registration.evidence_revalidated_before_write, true)
assert.equal(contract.mount_registration.implemented, true)
const native = readText('src-tauri/src/lib.rs')
const broker = readText('src-tauri/src/direct_local_broker.rs')
assert.doesNotMatch(native, /register_pncc_(repository|remote)_mount/)
assert.doesNotMatch(broker, /RegisterPncc(Repository|Remote)Mount/)
})
test('foundation and Rust modules report the implemented PNCC boundary', () => {
assert.equal(foundation.pncc_stage_one_contract, 'contracts/pncc-stage-one.json')
assert.equal(foundation.pncc_repository_binding_implemented, true)
assert.equal(foundation.pncc_remote_incremental_object_channel_implemented, true)
assert.equal(foundation.pncc_receipt_projection_implemented, true)
assert.equal(foundation.pncc_human_mount_registration_implemented, true)
assert.equal(foundation.pncc_internal_model_inference_implemented, false)
assert.equal(foundation.pncc_execution_limb_implemented, false)
for (const relative of [
'src-tauri/src/pncc_repository_binding.rs',
'src-tauri/src/pncc_remote_git.rs',
]) assert.equal(fs.existsSync(path.join(root, relative)), true)
const productionBindingSource = readText('src-tauri/src/pncc_repository_binding.rs').split('\n#[cfg(test)]\nmod tests')[0]
assert.doesNotMatch(productionBindingSource, /modelProvider|model_id|api[_-]?key/i)
})

View file

@ -0,0 +1,191 @@
import assert from 'node:assert/strict'
import crypto from 'node:crypto'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import test from 'node:test'
import { activateRelease, verifyReleaseBundle } from '../server/release-broadcast/operator.mjs'
import { loadRuntimeState } from '../server/release-broadcast/server.mjs'
const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex')
const writeJson = (file, value) => fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 })
function buildOperatorFixture() {
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-operator-fixture-'))
const source = path.join(fixtureRoot, 'source')
const approvals = path.join(fixtureRoot, 'approvals')
const stateRoot = path.join(fixtureRoot, 'state')
for (const directory of [source, approvals, stateRoot]) fs.mkdirSync(directory, { mode: 0o700 })
const packageName = 'HoloLake.app.tar.gz'
const packageBytes = Buffer.from('signed-notarized-updater-package')
const sourceCommit = '2'.repeat(40)
fs.writeFileSync(path.join(source, packageName), packageBytes, { mode: 0o600 })
writeJson(path.join(source, 'HOLOLAKE-CODESIGN.json'), {
schema: 'hololake.platform-code-signature-receipt/v1',
state: 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED',
sourceCommit,
})
writeJson(path.join(source, 'HOLOLAKE-NOTARIZATION.json'), {
schema: 'hololake.apple-notarization-receipt/v1',
state: 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED',
sourceCommit,
})
const broadcast = {
schema: 'hololake.release-broadcast/v1',
releaseId: 'GH-HOLOLAKE-RELEASE-0.2.0',
version: '0.2.0',
pub_date: '2026-08-13T10:00:00.000Z',
notes: 'Signed release',
platforms: {
'darwin-aarch64': {
url: `https://guanghulab.com/hololake/releases/0.2.0/${packageName}`,
signature: 'trusted-updater-signature',
size: packageBytes.length,
sha256: sha256(packageBytes),
platformCodeSignatureReceipt: 'HOLOLAKE-CODESIGN',
notarizationReceipt: 'HOLOLAKE-NOTARIZATION',
},
},
hololake: {
features: ['Stage one'],
fixes: [],
compatibility: { minimumVersion: '0.1.0', dataMigrationRequired: false },
restart: { required: true, automaticAllowed: false },
rollback: { supported: true, healthReceiptRequired: true, previousVersion: '0.1.0' },
},
}
const broadcastPath = path.join(source, 'latest.json')
writeJson(broadcastPath, broadcast)
const broadcastSha256 = sha256(fs.readFileSync(broadcastPath))
writeJson(path.join(source, 'pipeline-receipt.json'), {
schema: 'hololake.signed-release-pipeline-receipt/v1',
state: 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD',
sourceCommit,
broadcastSha256,
automaticUpload: false,
automaticActivation: false,
})
const approval = path.join(approvals, 'approval.json')
writeJson(approval, {
schema: 'hololake.release-broadcast-human-approval/v1',
state: 'HUMAN_APPROVED_EXACT_SIGNED_NOTARIZED_RELEASE',
approvalId: 'GH-HUMAN-RELEASE-APPROVAL-002',
releaseId: broadcast.releaseId,
version: broadcast.version,
broadcastSha256,
})
return {
fixtureRoot,
source,
approval,
stateRoot,
expected: { releaseId: broadcast.releaseId, version: broadcast.version, sourceCommit, broadcastSha256 },
}
}
test('verify reconstructs and validates an exact private candidate without mutating state', () => {
const fixture = buildOperatorFixture()
try {
const result = verifyReleaseBundle(fixture.source, fixture.approval)
assert.equal(result.state, 'EXACT_SIGNED_NOTARIZED_HUMAN_APPROVED_BUNDLE_VERIFIED')
assert.equal(result.broadcastSha256, fixture.expected.broadcastSha256)
assert.equal(result.artifacts.some((artifact) => artifact.name === 'HoloLake.app.tar.gz'), true)
assert.deepEqual(fs.readdirSync(fixture.stateRoot), [])
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})
test('activation repeats expected facts, commits atomically, and still requires explicit restart', () => {
const fixture = buildOperatorFixture()
try {
const receipt = activateRelease({
sourceDirectory: fixture.source,
humanApprovalPath: fixture.approval,
stateRoot: fixture.stateRoot,
expected: fixture.expected,
enforceRootOwner: false,
})
assert.equal(receipt.state, 'ACTIVATED_EXPLICIT_SERVICE_RESTART_REQUIRED')
assert.equal(receipt.automaticUpload, false)
assert.equal(receipt.automaticActivation, false)
assert.equal(receipt.automaticRestart, false)
assert.equal(loadRuntimeState(fixture.stateRoot).state, 'READY_SIGNED_NOTARIZED_BROADCAST')
assert.equal(fs.existsSync(path.join(fixture.stateRoot, '.operator-lock')), false)
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})
test('expected-fact mismatch performs no activation or release staging', () => {
const fixture = buildOperatorFixture()
try {
assert.throws(() => activateRelease({
sourceDirectory: fixture.source,
humanApprovalPath: fixture.approval,
stateRoot: fixture.stateRoot,
expected: { ...fixture.expected, broadcastSha256: '3'.repeat(64) },
enforceRootOwner: false,
}), /HOLOLAKE_RELEASE_OPERATOR_EXPECTED_BROADCAST_SHA256_MISMATCH/)
assert.deepEqual(fs.readdirSync(fixture.stateRoot), [])
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})
test('human approval cannot be reused after broadcast bytes change', () => {
const fixture = buildOperatorFixture()
try {
const latestPath = path.join(fixture.source, 'latest.json')
const latest = JSON.parse(fs.readFileSync(latestPath, 'utf8'))
latest.notes = 'Changed after approval'
writeJson(latestPath, latest)
assert.throws(
() => verifyReleaseBundle(fixture.source, fixture.approval),
/HOLOLAKE_RELEASE_OPERATOR_PIPELINE_RECEIPT_INVALID/,
)
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})
test('symlinked package inputs are rejected instead of followed', () => {
const fixture = buildOperatorFixture()
try {
const packagePath = path.join(fixture.source, 'HoloLake.app.tar.gz')
const moved = path.join(fixture.fixtureRoot, 'moved-package')
fs.renameSync(packagePath, moved)
fs.symlinkSync(moved, packagePath)
assert.throws(
() => verifyReleaseBundle(fixture.source, fixture.approval),
/HOLOLAKE_RELEASE_OPERATOR_FILE_INVALID/,
)
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})
test('operator refuses a bundle whose packages escape the registered public path', () => {
const fixture = buildOperatorFixture()
try {
const latestPath = path.join(fixture.source, 'latest.json')
const latest = JSON.parse(fs.readFileSync(latestPath, 'utf8'))
latest.platforms['darwin-aarch64'].url = 'https://guanghulab.com/updates/0.2.0/HoloLake.app.tar.gz'
writeJson(latestPath, latest)
const broadcastSha256 = sha256(fs.readFileSync(latestPath))
const pipelinePath = path.join(fixture.source, 'pipeline-receipt.json')
const pipeline = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'))
pipeline.broadcastSha256 = broadcastSha256
writeJson(pipelinePath, pipeline)
const approval = JSON.parse(fs.readFileSync(fixture.approval, 'utf8'))
approval.broadcastSha256 = broadcastSha256
writeJson(fixture.approval, approval)
assert.throws(
() => verifyReleaseBundle(fixture.source, fixture.approval),
/HOLOLAKE_RELEASE_OPERATOR_PACKAGE_PUBLIC_PREFIX_MISMATCH/,
)
} finally {
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true })
}
})

View file

@ -0,0 +1,184 @@
import assert from 'node:assert/strict'
import crypto from 'node:crypto'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import test from 'node:test'
import { createReleaseServer, isMainModule, loadRuntimeState } from '../server/release-broadcast/server.mjs'
const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex')
const writeJson = (file, value) => fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 })
async function withServer(state, callback) {
const server = createReleaseServer(state)
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))
const address = server.address()
try {
return await callback(`http://127.0.0.1:${address.port}`)
} finally {
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()))
}
}
function buildReleaseRoot() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-broadcast-'))
const releaseDirectory = path.join(root, 'releases', '0.2.0')
fs.mkdirSync(releaseDirectory, { recursive: true })
const packageBytes = Buffer.from('signed-notarized-updater-placeholder')
const packageName = 'HoloLake.app.tar.gz'
fs.writeFileSync(path.join(releaseDirectory, packageName), packageBytes, { mode: 0o600 })
writeJson(path.join(releaseDirectory, 'HOLOLAKE-CODESIGN.json'), {
schema: 'hololake.platform-code-signature-receipt/v1',
state: 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED',
sourceCommit: '1'.repeat(40),
})
writeJson(path.join(releaseDirectory, 'HOLOLAKE-NOTARIZATION.json'), {
schema: 'hololake.apple-notarization-receipt/v1',
state: 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED',
sourceCommit: '1'.repeat(40),
})
const broadcast = {
schema: 'hololake.release-broadcast/v1',
releaseId: 'GH-HOLOLAKE-RELEASE-0.2.0',
version: '0.2.0',
pub_date: '2026-08-13T10:00:00.000Z',
notes: 'Signed release',
platforms: {
'darwin-aarch64': {
url: `https://release.guanghu.test/hololake/releases/0.2.0/${packageName}`,
signature: 'trusted-updater-signature',
size: packageBytes.length,
sha256: sha256(packageBytes),
platformCodeSignatureReceipt: 'HOLOLAKE-CODESIGN',
notarizationReceipt: 'HOLOLAKE-NOTARIZATION',
},
},
hololake: {
features: ['Stage one'],
fixes: [],
compatibility: { minimumVersion: '0.1.0', dataMigrationRequired: false },
restart: { required: true, automaticAllowed: false },
rollback: { supported: true, healthReceiptRequired: true, previousVersion: '0.1.0' },
},
}
const broadcastPath = path.join(releaseDirectory, 'latest.json')
writeJson(broadcastPath, broadcast)
const broadcastSha256 = sha256(fs.readFileSync(broadcastPath))
writeJson(path.join(releaseDirectory, 'pipeline-receipt.json'), {
schema: 'hololake.signed-release-pipeline-receipt/v1',
state: 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD',
sourceCommit: '1'.repeat(40),
broadcastSha256,
automaticUpload: false,
automaticActivation: false,
})
writeJson(path.join(root, 'ACTIVE.json'), {
schema: 'hololake.release-broadcast-activation/v1',
state: 'HUMAN_APPROVED_SIGNED_NOTARIZED_RELEASE_ACTIVE',
releaseId: broadcast.releaseId,
version: broadcast.version,
broadcastRelativePath: 'releases/0.2.0/latest.json',
broadcastSha256,
pipelineReceiptRelativePath: 'releases/0.2.0/pipeline-receipt.json',
humanApprovalReceipt: 'GH-HUMAN-RELEASE-APPROVAL-001',
humanApprovalReceiptRelativePath: 'releases/0.2.0/human-approval.json',
})
writeJson(path.join(releaseDirectory, 'human-approval.json'), {
schema: 'hololake.release-broadcast-human-approval/v1',
state: 'HUMAN_APPROVED_EXACT_SIGNED_NOTARIZED_RELEASE',
approvalId: 'GH-HUMAN-RELEASE-APPROVAL-001',
releaseId: broadcast.releaseId,
version: broadcast.version,
broadcastSha256,
})
return { root, packageBytes }
}
test('empty release root stays healthy but returns Tauri-compatible 204 no update', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-empty-'))
const state = loadRuntimeState(root)
assert.equal(state.state, 'EMPTY_FAIL_CLOSED')
await withServer(state, async (base) => {
const health = await fetch(`${base}/health`)
assert.equal(health.status, 200)
assert.deepEqual((await health.json()).upstreamUpdateSources, [])
assert.equal((await fetch(`${base}/latest.json`)).status, 204)
assert.equal((await fetch(`${base}/hololake/releases/latest.json`)).status, 204)
const publicHealth = await fetch(`${base}/hololake/releases/health`)
assert.equal(publicHealth.status, 200)
assert.equal((await publicHealth.json()).releaseEndpointPath, '/hololake/releases/latest.json')
})
})
test('invalid activation locks the endpoint instead of falling back to an update', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-invalid-'))
writeJson(path.join(root, 'ACTIVE.json'), { schema: 'wrong', state: 'ACTIVE' })
const state = loadRuntimeState(root)
assert.equal(state.state, 'LOCKED_INVALID_RELEASE_EVIDENCE')
await withServer(state, async (base) => {
assert.equal((await fetch(`${base}/health`)).status, 503)
assert.equal((await fetch(`${base}/latest.json`)).status, 503)
})
})
test('only an exact human-approved signed notarized evidence chain becomes readable', async () => {
const fixture = buildReleaseRoot()
const state = loadRuntimeState(fixture.root)
assert.equal(state.state, 'READY_SIGNED_NOTARIZED_BROADCAST')
await withServer(state, async (base) => {
const latest = await fetch(`${base}/latest.json`)
assert.equal(latest.status, 200)
assert.equal((await latest.json()).releaseId, 'GH-HOLOLAKE-RELEASE-0.2.0')
const publicLatest = await fetch(`${base}/hololake/releases/latest.json`)
assert.equal(publicLatest.status, 200)
assert.equal((await publicLatest.json()).releaseId, 'GH-HOLOLAKE-RELEASE-0.2.0')
const updater = await fetch(`${base}/hololake/releases/0.2.0/HoloLake.app.tar.gz`)
assert.equal(updater.status, 200)
assert.deepEqual(Buffer.from(await updater.arrayBuffer()), fixture.packageBytes)
assert.equal((await fetch(`${base}/unknown`)).status, 404)
})
})
test('package tampering after pipeline output locks the whole release at startup', () => {
const fixture = buildReleaseRoot()
fs.appendFileSync(path.join(fixture.root, 'releases', '0.2.0', 'HoloLake.app.tar.gz'), 'tampered')
assert.equal(loadRuntimeState(fixture.root).state, 'LOCKED_INVALID_RELEASE_EVIDENCE')
})
test('a symlinked immutable current directory is recognized as the intended executable', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-symlink-'))
const versionDirectory = path.join(root, 'immutable')
fs.mkdirSync(versionDirectory)
const source = fs.readFileSync(new URL('../server/release-broadcast/server.mjs', import.meta.url), 'utf8')
const copy = path.join(versionDirectory, 'server.mjs')
fs.writeFileSync(copy, source, { mode: 0o500 })
const current = path.join(root, 'current')
fs.symlinkSync(versionDirectory, current)
assert.equal(isMainModule(path.join(current, 'server.mjs'), new URL(`file://${copy}`)), true)
})
test('a package outside the registered public prefix locks the release', () => {
const fixture = buildReleaseRoot()
const latestPath = path.join(fixture.root, 'releases', '0.2.0', 'latest.json')
const latest = JSON.parse(fs.readFileSync(latestPath, 'utf8'))
latest.platforms['darwin-aarch64'].url = 'https://release.guanghu.test/releases/0.2.0/HoloLake.app.tar.gz'
writeJson(latestPath, latest)
const broadcastSha256 = sha256(fs.readFileSync(latestPath))
const pipelinePath = path.join(fixture.root, 'releases', '0.2.0', 'pipeline-receipt.json')
const pipeline = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'))
pipeline.broadcastSha256 = broadcastSha256
writeJson(pipelinePath, pipeline)
const approvalPath = path.join(fixture.root, 'releases', '0.2.0', 'human-approval.json')
const approval = JSON.parse(fs.readFileSync(approvalPath, 'utf8'))
approval.broadcastSha256 = broadcastSha256
writeJson(approvalPath, approval)
const activationPath = path.join(fixture.root, 'ACTIVE.json')
const activation = JSON.parse(fs.readFileSync(activationPath, 'utf8'))
activation.broadcastSha256 = broadcastSha256
writeJson(activationPath, activation)
const state = loadRuntimeState(fixture.root)
assert.equal(state.state, 'LOCKED_INVALID_RELEASE_EVIDENCE')
assert.equal(state.reasonCode, 'HOLOLAKE_RELEASE_PACKAGE_PUBLIC_PREFIX_MISMATCH')
})

View file

@ -0,0 +1,22 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { renderFrontDoorSnippet } from '../server/release-broadcast/render-front-door.mjs'
test('front-door renderer refuses an unassigned or privileged loopback port', () => {
assert.throws(() => renderFrontDoorSnippet(null), /PORT_REQUIRED/)
assert.throws(() => renderFrontDoorSnippet(443), /PORT_REQUIRED|PORT_INVALID/)
assert.throws(() => renderFrontDoorSnippet('not-a-port'), /PORT_REQUIRED/)
})
test('rendered proxy preserves the public path and strips request authority', () => {
const snippet = renderFrontDoorSnippet(23940)
assert.match(snippet, /location \^~ \/hololake\/releases\//)
assert.match(snippet, /proxy_pass http:\/\/127\.0\.0\.1:23940;/)
assert.doesNotMatch(snippet, /proxy_pass http:\/\/127\.0\.0\.1:23940\//)
assert.match(snippet, /limit_except GET HEAD \{ deny all; \}/)
assert.match(snippet, /proxy_pass_request_body off;/)
assert.match(snippet, /proxy_set_header Authorization "";/)
assert.match(snippet, /proxy_set_header Cookie "";/)
assert.match(snippet, /proxy_set_header X-Forwarded-For "";/)
})

View file

@ -0,0 +1,284 @@
#!/usr/bin/env node
import { execFileSync, spawnSync } from 'node:child_process'
import crypto from 'node:crypto'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const PUBLIC_RELEASE_ENDPOINT_PATH = '/hololake/releases/latest.json'
const PUBLIC_RELEASE_PREFIX = '/hololake/releases'
const fail = (code) => {
throw new Error(code)
}
const requireText = (value, code) => {
if (typeof value !== 'string' || value.trim() === '') fail(code)
return value.trim()
}
const readJson = (file) => JSON.parse(fs.readFileSync(file, 'utf8'))
const sha256File = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
export function validateReleaseTrust(trust) {
if (trust?.schema !== 'hololake.release-trust/v1') fail('HOLOLAKE_RELEASE_PIPELINE_TRUST_SCHEMA_INVALID')
if (trust.state !== 'PROVISIONED') fail('HOLOLAKE_RELEASE_PIPELINE_TRUST_UNPROVISIONED')
if (!Array.isArray(trust.endpoints) || trust.endpoints.length !== 1) fail('HOLOLAKE_RELEASE_PIPELINE_EXACT_ENDPOINT_REQUIRED')
if (!Array.isArray(trust.allowedReleaseHosts) || trust.allowedReleaseHosts.length !== 1) fail('HOLOLAKE_RELEASE_PIPELINE_EXACT_HOST_REQUIRED')
const endpoint = new URL(trust.endpoints[0])
const host = requireText(trust.allowedReleaseHosts[0], 'HOLOLAKE_RELEASE_PIPELINE_HOST_REQUIRED')
if (
endpoint.protocol !== 'https:' ||
endpoint.hostname !== host ||
endpoint.username ||
endpoint.password ||
endpoint.search ||
endpoint.hash ||
endpoint.pathname !== PUBLIC_RELEASE_ENDPOINT_PATH
) {
fail('HOLOLAKE_RELEASE_PIPELINE_ENDPOINT_NOT_HOLOLAKE_HTTPS')
}
if (requireText(trust.publicKey, 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PUBLIC_KEY_REQUIRED').length < 32) {
fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_PUBLIC_KEY_INVALID')
}
if (trust.automaticCheckOnStartup || trust.automaticDownload || trust.automaticRestart || !trust.humanOptInInstallRequired) {
fail('HOLOLAKE_RELEASE_PIPELINE_HUMAN_CONTROL_POLICY_INVALID')
}
return { endpoint, host }
}
export function validateReleaseInput(input, trustFacts) {
if (input?.schema !== 'hololake.release-pipeline-input/v1') fail('HOLOLAKE_RELEASE_PIPELINE_INPUT_SCHEMA_INVALID')
const version = requireText(input.version, 'HOLOLAKE_RELEASE_PIPELINE_VERSION_REQUIRED')
if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)) {
fail('HOLOLAKE_RELEASE_PIPELINE_VERSION_INVALID')
}
if (input.platformCode !== 'darwin-aarch64') fail('HOLOLAKE_RELEASE_PIPELINE_PLATFORM_NOT_IMPLEMENTED')
if (!/^[a-f0-9]{40}$/.test(input.sourceCommit || '')) fail('HOLOLAKE_RELEASE_PIPELINE_SOURCE_COMMIT_INVALID')
if (input.releaseTag !== `v${version}`) fail('HOLOLAKE_RELEASE_PIPELINE_IMMUTABLE_TAG_INVALID')
const packageUrl = new URL(requireText(input.packageUrl, 'HOLOLAKE_RELEASE_PIPELINE_PACKAGE_URL_REQUIRED'))
if (
packageUrl.protocol !== 'https:' ||
packageUrl.hostname !== trustFacts.host ||
packageUrl.username ||
packageUrl.password ||
packageUrl.search ||
packageUrl.hash ||
!packageUrl.pathname.startsWith(`${PUBLIC_RELEASE_PREFIX}/`)
) {
fail('HOLOLAKE_RELEASE_PIPELINE_PACKAGE_HOST_NOT_TRUSTED')
}
if (!Array.isArray(input.features) || input.features.length === 0 || input.features.some((item) => typeof item !== 'string' || !item.trim())) {
fail('HOLOLAKE_RELEASE_PIPELINE_FEATURES_REQUIRED')
}
if (!Array.isArray(input.fixes) || input.fixes.some((item) => typeof item !== 'string' || !item.trim())) {
fail('HOLOLAKE_RELEASE_PIPELINE_FIXES_INVALID')
}
for (const [field, code] of [
['releaseId', 'HOLOLAKE_RELEASE_PIPELINE_RELEASE_ID_REQUIRED'],
['previousVersion', 'HOLOLAKE_RELEASE_PIPELINE_PREVIOUS_VERSION_REQUIRED'],
['minimumVersion', 'HOLOLAKE_RELEASE_PIPELINE_MINIMUM_VERSION_REQUIRED'],
['notes', 'HOLOLAKE_RELEASE_PIPELINE_NOTES_REQUIRED'],
['restartMessage', 'HOLOLAKE_RELEASE_PIPELINE_RESTART_MESSAGE_REQUIRED'],
['appleTeamIdentifier', 'HOLOLAKE_RELEASE_PIPELINE_APPLE_TEAM_REQUIRED'],
]) requireText(input[field], code)
if (typeof input.dataMigrationRequired !== 'boolean') fail('HOLOLAKE_RELEASE_PIPELINE_MIGRATION_FLAG_REQUIRED')
return { ...input, version, packageUrl }
}
export function validateCredentialEnvironment(env) {
const required = [
'APPLE_SIGNING_IDENTITY',
'TAURI_SIGNING_PRIVATE_KEY_PASSWORD',
]
const missing = required.filter((name) => typeof env[name] !== 'string' || env[name].trim() === '')
if (!env.TAURI_SIGNING_PRIVATE_KEY && !env.TAURI_SIGNING_PRIVATE_KEY_PATH) {
missing.push('TAURI_SIGNING_PRIVATE_KEY_OR_PATH')
} else if (env.TAURI_SIGNING_PRIVATE_KEY_PATH && !fs.statSync(env.TAURI_SIGNING_PRIVATE_KEY_PATH, { throwIfNoEntry: false })?.isFile()) {
missing.push('TAURI_SIGNING_PRIVATE_KEY_PATH_NOT_READABLE')
}
const appleIdFlow = env.APPLE_ID && env.APPLE_PASSWORD && env.APPLE_TEAM_ID
const appleApiFlow = env.APPLE_API_KEY && env.APPLE_API_ISSUER && env.APPLE_API_KEY_PATH
if (!appleIdFlow && !appleApiFlow) missing.push('APPLE_NOTARIZATION_CREDENTIAL_SET')
if (missing.length) fail(`HOLOLAKE_RELEASE_PIPELINE_CREDENTIALS_MISSING:${[...new Set(missing)].sort().join(',')}`)
}
function run(program, args, options = {}) {
return execFileSync(program, args, {
cwd: root,
encoding: 'utf8',
stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
env: process.env,
})
}
function captureAll(program, args) {
const result = spawnSync(program, args, { cwd: root, encoding: 'utf8', env: process.env })
if (result.error) throw result.error
if (result.status !== 0) fail(`HOLOLAKE_RELEASE_PIPELINE_COMMAND_FAILED:${program}`)
return `${result.stdout || ''}\n${result.stderr || ''}`
}
function requireCleanImmutableSource(input) {
if (run('git', ['status', '--porcelain'], { capture: true }).trim()) fail('HOLOLAKE_RELEASE_PIPELINE_SOURCE_NOT_CLEAN')
if (run('git', ['branch', '--show-current'], { capture: true }).trim() !== 'main') fail('HOLOLAKE_RELEASE_PIPELINE_MAIN_BRANCH_REQUIRED')
const head = run('git', ['rev-parse', 'HEAD'], { capture: true }).trim()
if (head !== input.sourceCommit) fail('HOLOLAKE_RELEASE_PIPELINE_SOURCE_COMMIT_MISMATCH')
const tagCommit = run('git', ['rev-list', '-n', '1', input.releaseTag], { capture: true }).trim()
if (tagCommit !== head) fail('HOLOLAKE_RELEASE_PIPELINE_TAG_COMMIT_MISMATCH')
const packageDocument = readJson(path.join(root, 'package.json'))
const tauriDocument = readJson(path.join(root, 'src-tauri/tauri.conf.json'))
if (packageDocument.version !== input.version || tauriDocument.version !== input.version) {
fail('HOLOLAKE_RELEASE_PIPELINE_VERSION_FILES_MISMATCH')
}
}
function findOne(rootPath, predicate, code) {
const matches = []
const visit = (directory) => {
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
const file = path.join(directory, entry.name)
if (entry.isDirectory()) visit(file)
else if (entry.isFile() && predicate(file)) matches.push(file)
}
}
if (fs.existsSync(rootPath)) visit(rootPath)
if (matches.length !== 1) fail(`${code}:${matches.length}`)
return matches[0]
}
function codeSignatureEvidence(appPath, input) {
run('/usr/bin/codesign', ['--verify', '--deep', '--strict', '--verbose=4', appPath])
const display = captureAll('/usr/bin/codesign', ['-dv', '--verbose=4', appPath])
const value = (name) => display.split('\n').find((line) => line.startsWith(`${name}=`))?.slice(name.length + 1)
if (value('Identifier') !== 'world.guanghu.hololake' || value('TeamIdentifier') !== input.appleTeamIdentifier) {
fail('HOLOLAKE_RELEASE_PIPELINE_CODE_SIGNATURE_IDENTITY_MISMATCH')
}
run('/usr/sbin/spctl', ['--assess', '--type', 'execute', '--verbose=4', appPath])
run('/usr/bin/xcrun', ['stapler', 'validate', appPath])
return {
schema: 'hololake.platform-code-signature-receipt/v1',
state: 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED',
identifier: value('Identifier'),
teamIdentifier: value('TeamIdentifier'),
cdHash: value('CDHash'),
sourceCommit: input.sourceCommit,
}
}
function notarizationEvidence(appPath, dmgPath, input) {
run('/usr/sbin/spctl', ['--assess', '--type', 'open', '--context', 'context:primary-signature', '--verbose=4', dmgPath])
run('/usr/bin/xcrun', ['stapler', 'validate', dmgPath])
return {
schema: 'hololake.apple-notarization-receipt/v1',
state: 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED',
appName: path.basename(appPath),
dmgName: path.basename(dmgPath),
appleTeamIdentifier: input.appleTeamIdentifier,
sourceCommit: input.sourceCommit,
}
}
function writeJson(file, value) {
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' })
}
export async function main(argv = process.argv.slice(2)) {
const inputArgument = argv[0]
if (!inputArgument) fail('HOLOLAKE_RELEASE_PIPELINE_INPUT_PATH_REQUIRED')
const inputPath = path.resolve(inputArgument)
const trust = readJson(path.join(root, 'src-tauri/release-trust.json'))
const trustFacts = validateReleaseTrust(trust)
const input = validateReleaseInput(readJson(inputPath), trustFacts)
validateCredentialEnvironment(process.env)
requireCleanImmutableSource(input)
if (process.platform !== 'darwin' || process.arch !== 'arm64') fail('HOLOLAKE_RELEASE_PIPELINE_BUILD_HOST_MISMATCH')
if (!process.env.APPLE_SIGNING_IDENTITY.includes(input.appleTeamIdentifier)) fail('HOLOLAKE_RELEASE_PIPELINE_SIGNING_TEAM_MISMATCH')
run('npm', ['test'])
run('cargo', ['clippy', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features', '--', '-D', 'warnings'])
run('cargo', ['test', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features'])
const buildConfig = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-config-')), 'tauri.release.json')
fs.writeFileSync(buildConfig, `${JSON.stringify({ bundle: { createUpdaterArtifacts: true } })}\n`, { mode: 0o600 })
try {
run('npm', ['run', 'tauri', '--', 'build', '--ci', '--config', buildConfig])
} finally {
fs.rmSync(path.dirname(buildConfig), { recursive: true, force: true })
}
const bundleRoot = path.join(root, 'src-tauri/target/release/bundle')
const appExecutable = findOne(path.join(bundleRoot, 'macos'), (file) => file.endsWith('/Contents/MacOS/hololake-native-desktop'), 'HOLOLAKE_RELEASE_PIPELINE_APP_NOT_UNIQUE')
const application = path.resolve(appExecutable, '../../../..')
const dmg = findOne(path.join(bundleRoot, 'dmg'), (file) => file.endsWith('.dmg'), 'HOLOLAKE_RELEASE_PIPELINE_DMG_NOT_UNIQUE')
const updater = findOne(bundleRoot, (file) => file.endsWith('.app.tar.gz'), 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_NOT_UNIQUE')
const updaterSignature = `${updater}.sig`
if (!fs.statSync(updaterSignature, { throwIfNoEntry: false })?.isFile()) fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_SIGNATURE_MISSING')
if (decodeURIComponent(input.packageUrl.pathname.split('/').pop()) !== path.basename(updater)) {
fail('HOLOLAKE_RELEASE_PIPELINE_PACKAGE_URL_FILENAME_MISMATCH')
}
const codeReceipt = codeSignatureEvidence(application, input)
const notarizationReceipt = notarizationEvidence(application, dmg, input)
const outputRoot = path.join(root, 'release/out', input.version)
if (fs.existsSync(outputRoot)) fail('HOLOLAKE_RELEASE_PIPELINE_OUTPUT_ALREADY_EXISTS')
fs.mkdirSync(outputRoot, { recursive: true, mode: 0o700 })
const codeReceiptId = `HOLOLAKE-CODESIGN-${input.version}-${input.sourceCommit.slice(0, 12)}`
const notarizationReceiptId = `HOLOLAKE-NOTARIZATION-${input.version}-${input.sourceCommit.slice(0, 12)}`
writeJson(path.join(outputRoot, `${codeReceiptId}.json`), codeReceipt)
writeJson(path.join(outputRoot, `${notarizationReceiptId}.json`), notarizationReceipt)
fs.copyFileSync(updater, path.join(outputRoot, path.basename(updater)), fs.constants.COPYFILE_EXCL)
fs.copyFileSync(updaterSignature, path.join(outputRoot, path.basename(updaterSignature)), fs.constants.COPYFILE_EXCL)
fs.copyFileSync(dmg, path.join(outputRoot, path.basename(dmg)), fs.constants.COPYFILE_EXCL)
const broadcast = {
schema: 'hololake.release-broadcast/v1',
releaseId: input.releaseId,
version: input.version,
pub_date: new Date().toISOString(),
notes: input.notes,
platforms: {
[input.platformCode]: {
url: input.packageUrl.toString(),
signature: fs.readFileSync(updaterSignature, 'utf8').trim(),
size: fs.statSync(updater).size,
sha256: sha256File(updater),
platformCodeSignatureReceipt: codeReceiptId,
notarizationReceipt: notarizationReceiptId,
},
},
hololake: {
features: input.features,
fixes: input.fixes,
compatibility: {
minimumVersion: input.minimumVersion,
dataMigrationRequired: input.dataMigrationRequired,
...(input.compatibilityNotes ? { notes: input.compatibilityNotes } : {}),
},
restart: { required: true, automaticAllowed: false, message: input.restartMessage },
rollback: { supported: true, healthReceiptRequired: true, previousVersion: input.previousVersion },
},
}
writeJson(path.join(outputRoot, 'latest.json'), broadcast)
writeJson(path.join(outputRoot, 'pipeline-receipt.json'), {
schema: 'hololake.signed-release-pipeline-receipt/v1',
state: 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD',
sourceCommit: input.sourceCommit,
releaseTag: input.releaseTag,
releaseEndpoint: trustFacts.endpoint.toString(),
broadcastSha256: sha256File(path.join(outputRoot, 'latest.json')),
automaticUpload: false,
automaticActivation: false,
})
process.stdout.write(`HOLOLAKE_RELEASE_PIPELINE_READY:${outputRoot}\n`)
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main().catch((error) => {
process.stderr.write(`${error.message}\n`)
process.exitCode = 1
})
}

View file

@ -0,0 +1,76 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
validateCredentialEnvironment,
validateReleaseInput,
validateReleaseTrust,
} from './release-pipeline.mjs'
const readyTrust = () => ({
schema: 'hololake.release-trust/v1',
state: 'PROVISIONED',
endpoints: ['https://release.guanghu.test/hololake/releases/latest.json'],
publicKey: 'A'.repeat(64),
allowedReleaseHosts: ['release.guanghu.test'],
automaticCheckOnStartup: false,
automaticDownload: false,
humanOptInInstallRequired: true,
automaticRestart: false,
})
const readyInput = () => ({
schema: 'hololake.release-pipeline-input/v1',
releaseId: 'GH-HOLOLAKE-RELEASE-0.2.0',
version: '0.2.0',
previousVersion: '0.1.0',
minimumVersion: '0.1.0',
releaseTag: 'v0.2.0',
sourceCommit: 'a'.repeat(40),
platformCode: 'darwin-aarch64',
packageUrl: 'https://release.guanghu.test/hololake/releases/0.2.0/HoloLake.app.tar.gz',
appleTeamIdentifier: '825A9L3G7Q',
notes: 'Signed release',
features: ['Persistent direct connection'],
fixes: [],
dataMigrationRequired: false,
restartMessage: 'Restart manually',
})
test('release pipeline refuses an unprovisioned or upstream-owned trust document', () => {
const unprovisioned = readyTrust()
unprovisioned.state = 'UNPROVISIONED_FAIL_CLOSED'
assert.throws(() => validateReleaseTrust(unprovisioned), /TRUST_UNPROVISIONED/)
const upstream = readyTrust()
upstream.endpoints = ['https://updates.vendor.test/latest.json']
assert.throws(() => validateReleaseTrust(upstream), /ENDPOINT_NOT_HOLOLAKE_HTTPS/)
})
test('release package must use the exact registered HoloLake HTTPS host and immutable tag', () => {
const trust = validateReleaseTrust(readyTrust())
const wrongHost = readyInput()
wrongHost.packageUrl = 'https://github.com/example/HoloLake.app.tar.gz'
assert.throws(() => validateReleaseInput(wrongHost, trust), /PACKAGE_HOST_NOT_TRUSTED/)
const wrongPath = readyInput()
wrongPath.packageUrl = 'https://release.guanghu.test/updates/HoloLake.app.tar.gz'
assert.throws(() => validateReleaseInput(wrongPath, trust), /PACKAGE_HOST_NOT_TRUSTED/)
const wrongTag = readyInput()
wrongTag.releaseTag = 'latest'
assert.throws(() => validateReleaseInput(wrongTag, trust), /IMMUTABLE_TAG_INVALID/)
assert.equal(validateReleaseInput(readyInput(), trust).version, '0.2.0')
})
test('release pipeline requires updater signing, Developer ID and Apple notarization credentials together', () => {
assert.throws(() => validateCredentialEnvironment({}), /CREDENTIALS_MISSING/)
assert.doesNotThrow(() => validateCredentialEnvironment({
APPLE_SIGNING_IDENTITY: 'Developer ID Application: HoloLake (TEAM)',
TAURI_SIGNING_PRIVATE_KEY: 'runtime-secret-material',
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 'provided-at-runtime',
APPLE_ID: 'release@example.test',
APPLE_PASSWORD: 'provided-at-runtime',
APPLE_TEAM_ID: 'TEAM',
}))
})

View file

@ -0,0 +1,59 @@
# HoloLake release broadcast candidate
This is the HoloLake-owned, loopback-only origin for signed update broadcasts.
It has no upstream software feed and never uploads or activates a release.
Without `ACTIVE.json`, `/health` reports `EMPTY_FAIL_CLOSED` and `/latest.json`
returns HTTP 204, the updater protocol's explicit no-update result. An active
release is accepted only when the human activation record, immutable broadcast,
pipeline receipt, package bytes, Developer ID receipt, and Apple notarization
receipt form one exact evidence chain. Invalid evidence locks the whole release
endpoint until an operator fixes the evidence and explicitly restarts the
service.
The service listens only on `127.0.0.1`. Public HTTPS routing, updater trust-key
provisioning, artifact upload, activation, and desktop rollout are independent
deployment gates.
The registered public namespace is `/hololake/releases`. The loopback origin
accepts the manifest at both its operator health-check path `/latest.json` and
the public updater path `/hololake/releases/latest.json`; release package URLs
must remain under that same public prefix. The front-door proxy must therefore
preserve the full request URI. Its loopback tunnel port remains deliberately
unassigned until the BS-GZ-006 route owner returns the live, non-conflicting
topology.
Once that port is returned and independently verified, `render-front-door.mjs
--loopback-port PORT` renders the bounded Nginx location. It refuses an omitted
port, preserves the full public request path by leaving `proxy_pass` without a
URI suffix, accepts only GET and HEAD, forwards no request body, and strips
authorization, cookies, and client forwarding headers. Rendering is not
deployment; Nginx validation, transactional install, public certificate/readback,
and a separate server receipt remain required.
`operator.mjs` supplies the separate, root-operated verification and activation
boundary. `verify` reconstructs a private candidate tree and accepts it only when
the broadcast, pipeline receipt, package bytes, Developer ID receipt, Apple
notarization receipt, and exact human approval all agree. `activate` additionally
requires the operator to repeat the expected release id, version, source commit,
and broadcast SHA-256. It copies only referenced immutable artifacts, commits
`ACTIVE.json` atomically, and reports that an explicit service restart is still
required. It never uploads, activates, or restarts on its own.
```text
node operator.mjs verify \
--source /secure/release/out/0.2.0 \
--human-approval /secure/approvals/0.2.0.json
sudo node operator.mjs activate \
--source /secure/release/out/0.2.0 \
--human-approval /secure/approvals/0.2.0.json \
--state-root /var/lib/guanghu/hololake-release-broadcast \
--expect-release-id GH-HOLOLAKE-RELEASE-0.2.0 \
--expect-version 0.2.0 \
--expect-source-commit 0000000000000000000000000000000000000000 \
--expect-broadcast-sha256 0000000000000000000000000000000000000000000000000000000000000000
```
The zeros above are placeholders, not deployable values. A real activation must
use the exact facts printed by `verify` and a separately issued human approval.

View file

@ -0,0 +1,36 @@
[Unit]
Description=HoloLake signed release broadcast candidate
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=hololake-release
Group=hololake-release
WorkingDirectory=/opt/guanghu/hololake-release-broadcast-candidate/current
Environment=HOLOLAKE_RELEASE_HOST=127.0.0.1
Environment=HOLOLAKE_RELEASE_PORT=3940
Environment=HOLOLAKE_RELEASE_STATE_ROOT=/var/lib/guanghu/hololake-release-broadcast
Environment=HOLOLAKE_RELEASE_ENFORCE_ROOT_OWNER=1
Environment=HOLOLAKE_RELEASE_PUBLIC_PREFIX=/hololake/releases
ExecStart=/usr/bin/node /opt/guanghu/hololake-release-broadcast-candidate/current/server.mjs
Restart=on-failure
RestartSec=5
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
IPAddressDeny=any
IPAddressAllow=localhost
ReadOnlyPaths=/opt/guanghu/hololake-release-broadcast-candidate
ReadOnlyPaths=/var/lib/guanghu/hololake-release-broadcast
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,431 @@
#!/usr/bin/env node
import crypto from 'node:crypto'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { DEFAULT_PUBLIC_PREFIX, isMainModule, loadRuntimeState, normalizePublicPrefix } from './server.mjs'
const MAX_CONTROL_BYTES = 1024 * 1024
const SHA256_PATTERN = /^[a-f0-9]{64}$/
const SOURCE_COMMIT_PATTERN = /^[a-f0-9]{40}$/
const VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/
const fail = (code) => {
throw new Error(code)
}
const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex')
const requireText = (value, code) => {
if (typeof value !== 'string' || value.trim() === '') fail(code)
return value.trim()
}
function requireTrustedDirectory(directory, { enforceRootOwner = false } = {}) {
const info = fs.lstatSync(directory)
if (!info.isDirectory() || info.isSymbolicLink()) fail('HOLOLAKE_RELEASE_OPERATOR_DIRECTORY_INVALID')
if ((info.mode & 0o022) !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_DIRECTORY_WRITABLE_BY_NON_OWNER')
if (enforceRootOwner && info.uid !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_DIRECTORY_NOT_ROOT_OWNED')
return info
}
function requireTrustedFile(file, { enforceRootOwner = false, maxBytes = null } = {}) {
const info = fs.lstatSync(file)
if (!info.isFile() || info.isSymbolicLink()) fail('HOLOLAKE_RELEASE_OPERATOR_FILE_INVALID')
if ((info.mode & 0o022) !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_FILE_WRITABLE_BY_NON_OWNER')
if (enforceRootOwner && info.uid !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_FILE_NOT_ROOT_OWNED')
if (maxBytes !== null && info.size > maxBytes) fail('HOLOLAKE_RELEASE_OPERATOR_CONTROL_FILE_TOO_LARGE')
return info
}
function readJson(file, { enforceRootOwner = false } = {}) {
requireTrustedFile(file, { enforceRootOwner, maxBytes: MAX_CONTROL_BYTES })
const bytes = fs.readFileSync(file)
return { bytes, value: JSON.parse(bytes.toString('utf8')) }
}
function directFile(directory, name, code) {
const value = requireText(name, code)
if (value !== path.basename(value) || value === '.' || value === '..' || value.includes('\0')) fail(code)
const resolved = path.resolve(directory, value)
if (path.dirname(resolved) !== directory) fail(code)
return resolved
}
function packageNameFromUrl(value, publicPrefix) {
const url = new URL(requireText(value, 'HOLOLAKE_RELEASE_OPERATOR_PACKAGE_URL_REQUIRED'))
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_URL_INVALID')
}
let packageName
try {
packageName = decodeURIComponent(path.posix.basename(url.pathname))
} catch {
fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_URL_INVALID')
}
if (!packageName || packageName === '.' || packageName === '..') fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_NAME_INVALID')
if (!url.pathname.startsWith(`${publicPrefix}/`) || url.pathname === `${publicPrefix}/latest.json` || url.pathname === `${publicPrefix}/health`) {
fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_PUBLIC_PREFIX_MISMATCH')
}
return packageName
}
function addArtifact(artifacts, source, name, type) {
const previous = artifacts.get(name)
if (previous && previous.source !== source) fail('HOLOLAKE_RELEASE_OPERATOR_ARTIFACT_NAME_COLLISION')
artifacts.set(name, { source, name, type })
}
export function inspectReleaseBundle(
sourceDirectory,
humanApprovalPath,
{ enforceRootOwner = false, publicPrefix = DEFAULT_PUBLIC_PREFIX } = {},
) {
const source = path.resolve(sourceDirectory)
const normalizedPublicPrefix = normalizePublicPrefix(publicPrefix)
requireTrustedDirectory(source, { enforceRootOwner })
const latestPath = directFile(source, 'latest.json', 'HOLOLAKE_RELEASE_OPERATOR_BROADCAST_PATH_INVALID')
const pipelinePath = directFile(source, 'pipeline-receipt.json', 'HOLOLAKE_RELEASE_OPERATOR_PIPELINE_PATH_INVALID')
const approvalPath = path.resolve(humanApprovalPath)
const latestDocument = readJson(latestPath, { enforceRootOwner })
const pipelineReceipt = readJson(pipelinePath, { enforceRootOwner }).value
const approvalReceipt = readJson(approvalPath, { enforceRootOwner }).value
const broadcast = latestDocument.value
const broadcastSha256 = sha256(latestDocument.bytes)
if (broadcast?.schema !== 'hololake.release-broadcast/v1') fail('HOLOLAKE_RELEASE_OPERATOR_BROADCAST_INVALID')
const releaseId = requireText(broadcast.releaseId, 'HOLOLAKE_RELEASE_OPERATOR_RELEASE_ID_REQUIRED')
const version = requireText(broadcast.version, 'HOLOLAKE_RELEASE_OPERATOR_VERSION_REQUIRED')
if (!VERSION_PATTERN.test(version)) fail('HOLOLAKE_RELEASE_OPERATOR_VERSION_INVALID')
if (
broadcast?.hololake?.restart?.required !== true ||
broadcast?.hololake?.restart?.automaticAllowed !== false ||
broadcast?.hololake?.rollback?.supported !== true ||
broadcast?.hololake?.rollback?.healthReceiptRequired !== true ||
!broadcast.platforms ||
typeof broadcast.platforms !== 'object' ||
Array.isArray(broadcast.platforms) ||
Object.keys(broadcast.platforms).length === 0
) fail('HOLOLAKE_RELEASE_OPERATOR_BROADCAST_POLICY_INVALID')
if (
pipelineReceipt?.schema !== 'hololake.signed-release-pipeline-receipt/v1' ||
pipelineReceipt?.state !== 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD' ||
pipelineReceipt?.broadcastSha256 !== broadcastSha256 ||
!SOURCE_COMMIT_PATTERN.test(pipelineReceipt?.sourceCommit || '') ||
pipelineReceipt?.automaticUpload !== false ||
pipelineReceipt?.automaticActivation !== false
) fail('HOLOLAKE_RELEASE_OPERATOR_PIPELINE_RECEIPT_INVALID')
const approvalId = requireText(approvalReceipt?.approvalId, 'HOLOLAKE_RELEASE_OPERATOR_APPROVAL_ID_REQUIRED')
if (
approvalReceipt?.schema !== 'hololake.release-broadcast-human-approval/v1' ||
approvalReceipt?.state !== 'HUMAN_APPROVED_EXACT_SIGNED_NOTARIZED_RELEASE' ||
approvalReceipt?.releaseId !== releaseId ||
approvalReceipt?.version !== version ||
approvalReceipt?.broadcastSha256 !== broadcastSha256
) fail('HOLOLAKE_RELEASE_OPERATOR_HUMAN_APPROVAL_INVALID')
const artifacts = new Map()
addArtifact(artifacts, latestPath, 'latest.json', 'control')
addArtifact(artifacts, pipelinePath, 'pipeline-receipt.json', 'control')
addArtifact(artifacts, approvalPath, 'human-approval.json', 'control')
for (const platform of Object.values(broadcast.platforms)) {
const packageName = packageNameFromUrl(platform?.url, normalizedPublicPrefix)
const packagePath = directFile(source, packageName, 'HOLOLAKE_RELEASE_OPERATOR_PACKAGE_PATH_INVALID')
const packageInfo = requireTrustedFile(packagePath, { enforceRootOwner })
if (!Number.isSafeInteger(platform.size) || platform.size <= 0 || platform.size !== packageInfo.size) {
fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_SIZE_MISMATCH')
}
if (!SHA256_PATTERN.test(platform.sha256 || '') || sha256(fs.readFileSync(packagePath)) !== platform.sha256) {
fail('HOLOLAKE_RELEASE_OPERATOR_PACKAGE_SHA256_MISMATCH')
}
requireText(platform.signature, 'HOLOLAKE_RELEASE_OPERATOR_PACKAGE_SIGNATURE_REQUIRED')
const codeReceiptId = requireText(platform.platformCodeSignatureReceipt, 'HOLOLAKE_RELEASE_OPERATOR_CODESIGN_RECEIPT_REQUIRED')
const notarizationReceiptId = requireText(platform.notarizationReceipt, 'HOLOLAKE_RELEASE_OPERATOR_NOTARIZATION_RECEIPT_REQUIRED')
const codeReceiptName = `${codeReceiptId}.json`
const notarizationReceiptName = `${notarizationReceiptId}.json`
const codeReceiptPath = directFile(source, codeReceiptName, 'HOLOLAKE_RELEASE_OPERATOR_CODESIGN_RECEIPT_PATH_INVALID')
const notarizationReceiptPath = directFile(source, notarizationReceiptName, 'HOLOLAKE_RELEASE_OPERATOR_NOTARIZATION_RECEIPT_PATH_INVALID')
const codeReceipt = readJson(codeReceiptPath, { enforceRootOwner }).value
const notarizationReceipt = readJson(notarizationReceiptPath, { enforceRootOwner }).value
if (
codeReceipt?.schema !== 'hololake.platform-code-signature-receipt/v1' ||
codeReceipt?.state !== 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED' ||
codeReceipt?.sourceCommit !== pipelineReceipt.sourceCommit
) fail('HOLOLAKE_RELEASE_OPERATOR_CODESIGN_RECEIPT_INVALID')
if (
notarizationReceipt?.schema !== 'hololake.apple-notarization-receipt/v1' ||
notarizationReceipt?.state !== 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED' ||
notarizationReceipt?.sourceCommit !== pipelineReceipt.sourceCommit
) fail('HOLOLAKE_RELEASE_OPERATOR_NOTARIZATION_RECEIPT_INVALID')
addArtifact(artifacts, packagePath, packageName, 'package')
addArtifact(artifacts, codeReceiptPath, codeReceiptName, 'control')
addArtifact(artifacts, notarizationReceiptPath, notarizationReceiptName, 'control')
}
return {
schema: 'hololake.release-broadcast-operator-plan/v1',
state: 'EXACT_SIGNED_NOTARIZED_HUMAN_APPROVED_BUNDLE_VERIFIED',
releaseId,
version,
sourceCommit: pipelineReceipt.sourceCommit,
broadcastSha256,
approvalId,
publicPrefix: normalizedPublicPrefix,
artifacts: [...artifacts.values()],
}
}
function copyFileNoFollow(source, destination, mode) {
const sourceDescriptor = fs.openSync(source, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW)
let destinationDescriptor = null
try {
const sourceInfo = fs.fstatSync(sourceDescriptor)
if (!sourceInfo.isFile() || (sourceInfo.mode & 0o022) !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_SOURCE_CHANGED')
destinationDescriptor = fs.openSync(destination, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, mode)
const buffer = Buffer.allocUnsafe(1024 * 1024)
let position = 0
while (position < sourceInfo.size) {
const read = fs.readSync(sourceDescriptor, buffer, 0, Math.min(buffer.length, sourceInfo.size - position), position)
if (read <= 0) fail('HOLOLAKE_RELEASE_OPERATOR_SOURCE_TRUNCATED')
let written = 0
while (written < read) written += fs.writeSync(destinationDescriptor, buffer, written, read - written)
position += read
}
fs.fsyncSync(destinationDescriptor)
} finally {
if (destinationDescriptor !== null) fs.closeSync(destinationDescriptor)
fs.closeSync(sourceDescriptor)
}
}
function writeJsonExclusive(file, value, mode) {
const descriptor = fs.openSync(file, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, mode)
try {
fs.writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`)
fs.fsyncSync(descriptor)
} finally {
fs.closeSync(descriptor)
}
}
function activationFor(plan) {
return {
schema: 'hololake.release-broadcast-activation/v1',
state: 'HUMAN_APPROVED_SIGNED_NOTARIZED_RELEASE_ACTIVE',
releaseId: plan.releaseId,
version: plan.version,
broadcastRelativePath: `releases/${plan.version}/latest.json`,
broadcastSha256: plan.broadcastSha256,
pipelineReceiptRelativePath: `releases/${plan.version}/pipeline-receipt.json`,
humanApprovalReceipt: plan.approvalId,
humanApprovalReceiptRelativePath: `releases/${plan.version}/human-approval.json`,
}
}
function prepareCandidate(candidateRoot, plan, { enforceRootOwner, trustedGroupId }) {
const releases = path.join(candidateRoot, 'releases')
const releaseDirectory = path.join(releases, plan.version)
fs.mkdirSync(releaseDirectory, { recursive: true, mode: enforceRootOwner ? 0o750 : 0o700 })
for (const artifact of plan.artifacts) {
const destination = path.join(releaseDirectory, artifact.name)
copyFileNoFollow(artifact.source, destination, enforceRootOwner ? 0o640 : 0o600)
if (enforceRootOwner) fs.chownSync(destination, 0, trustedGroupId)
}
const activationPath = path.join(candidateRoot, 'ACTIVE.json')
writeJsonExclusive(activationPath, activationFor(plan), enforceRootOwner ? 0o640 : 0o600)
if (enforceRootOwner) {
for (const directory of [candidateRoot, releases, releaseDirectory]) {
fs.chownSync(directory, 0, trustedGroupId)
fs.chmodSync(directory, 0o750)
}
fs.chownSync(activationPath, 0, trustedGroupId)
}
const state = loadRuntimeState(candidateRoot, { enforceRootOwner, publicPrefix: plan.publicPrefix })
if (state.state !== 'READY_SIGNED_NOTARIZED_BROADCAST') {
fail(`HOLOLAKE_RELEASE_OPERATOR_CANDIDATE_INVALID:${state.reasonCode || state.state}`)
}
return { releaseDirectory, activationPath }
}
function requireExpected(plan, expected) {
for (const [field, code] of [
['releaseId', 'HOLOLAKE_RELEASE_OPERATOR_EXPECTED_RELEASE_ID_MISMATCH'],
['version', 'HOLOLAKE_RELEASE_OPERATOR_EXPECTED_VERSION_MISMATCH'],
['sourceCommit', 'HOLOLAKE_RELEASE_OPERATOR_EXPECTED_SOURCE_COMMIT_MISMATCH'],
['broadcastSha256', 'HOLOLAKE_RELEASE_OPERATOR_EXPECTED_BROADCAST_SHA256_MISMATCH'],
]) {
if (requireText(expected?.[field], `${code}_REQUIRED`) !== plan[field]) fail(code)
}
}
function replaceActivation(stateRoot, candidateActivation, previousBytes, enforceRootOwner, trustedGroupId, publicPrefix) {
const tempActivation = path.join(stateRoot, `.ACTIVE.${crypto.randomUUID()}.tmp`)
copyFileNoFollow(candidateActivation, tempActivation, enforceRootOwner ? 0o640 : 0o600)
if (enforceRootOwner) fs.chownSync(tempActivation, 0, trustedGroupId)
fs.renameSync(tempActivation, path.join(stateRoot, 'ACTIVE.json'))
const committed = loadRuntimeState(stateRoot, { enforceRootOwner, publicPrefix })
if (committed.state === 'READY_SIGNED_NOTARIZED_BROADCAST') return committed
const failedActive = path.join(stateRoot, 'ACTIVE.json')
if (previousBytes === null) {
fs.unlinkSync(failedActive)
} else {
const rollback = path.join(stateRoot, `.ACTIVE.rollback.${crypto.randomUUID()}.tmp`)
const descriptor = fs.openSync(rollback, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, enforceRootOwner ? 0o640 : 0o600)
try {
fs.writeFileSync(descriptor, previousBytes)
fs.fsyncSync(descriptor)
} finally {
fs.closeSync(descriptor)
}
if (enforceRootOwner) fs.chownSync(rollback, 0, trustedGroupId)
fs.renameSync(rollback, failedActive)
}
fail(`HOLOLAKE_RELEASE_OPERATOR_POST_COMMIT_INVALID:${committed.reasonCode || committed.state}`)
}
function acquireOperatorLock(stateRoot, enforceRootOwner, trustedGroupId) {
const lockDirectory = path.join(stateRoot, '.operator-lock')
try {
fs.mkdirSync(lockDirectory, { mode: enforceRootOwner ? 0o750 : 0o700 })
} catch (error) {
if (error?.code === 'EEXIST') fail('HOLOLAKE_RELEASE_OPERATOR_LOCKED')
throw error
}
try {
if (enforceRootOwner) fs.chownSync(lockDirectory, 0, trustedGroupId)
writeJsonExclusive(path.join(lockDirectory, 'owner.json'), {
schema: 'hololake.release-broadcast-operator-lock/v1',
pid: process.pid,
acquiredAt: new Date().toISOString(),
}, enforceRootOwner ? 0o640 : 0o600)
if (enforceRootOwner) fs.chownSync(path.join(lockDirectory, 'owner.json'), 0, trustedGroupId)
return lockDirectory
} catch (error) {
fs.rmSync(lockDirectory, { recursive: true, force: true })
throw error
}
}
export function verifyReleaseBundle(sourceDirectory, humanApprovalPath) {
const plan = inspectReleaseBundle(sourceDirectory, humanApprovalPath)
const candidateRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-operator-verify-'))
try {
prepareCandidate(candidateRoot, plan, { enforceRootOwner: false, trustedGroupId: process.getgid?.() ?? 0 })
return { ...plan, artifacts: plan.artifacts.map(({ name, type }) => ({ name, type })) }
} finally {
fs.rmSync(candidateRoot, { recursive: true, force: true })
}
}
export function activateRelease({
sourceDirectory,
humanApprovalPath,
stateRoot,
expected,
enforceRootOwner = true,
publicPrefix = DEFAULT_PUBLIC_PREFIX,
}) {
if (enforceRootOwner && process.getuid?.() !== 0) fail('HOLOLAKE_RELEASE_OPERATOR_ROOT_REQUIRED')
const root = path.resolve(stateRoot)
const rootInfo = requireTrustedDirectory(root, { enforceRootOwner })
const trustedGroupId = rootInfo.gid
const lockDirectory = acquireOperatorLock(root, enforceRootOwner, trustedGroupId)
let candidateRoot = null
try {
const plan = inspectReleaseBundle(sourceDirectory, humanApprovalPath, { enforceRootOwner, publicPrefix })
requireExpected(plan, expected)
candidateRoot = fs.mkdtempSync(path.join(root, '.candidate-'))
const candidate = prepareCandidate(candidateRoot, plan, { enforceRootOwner, trustedGroupId })
const releasesRoot = path.join(root, 'releases')
if (!fs.existsSync(releasesRoot)) fs.mkdirSync(releasesRoot, { mode: enforceRootOwner ? 0o750 : 0o700 })
requireTrustedDirectory(releasesRoot, { enforceRootOwner })
if (enforceRootOwner) {
fs.chownSync(releasesRoot, 0, trustedGroupId)
fs.chmodSync(releasesRoot, 0o750)
}
const finalRelease = path.join(releasesRoot, plan.version)
if (fs.existsSync(finalRelease)) fail('HOLOLAKE_RELEASE_OPERATOR_VERSION_ALREADY_STAGED')
const activePath = path.join(root, 'ACTIVE.json')
const previousBytes = fs.existsSync(activePath) ? readJson(activePath, { enforceRootOwner }).bytes : null
fs.renameSync(candidate.releaseDirectory, finalRelease)
let committed
try {
committed = replaceActivation(
root,
candidate.activationPath,
previousBytes,
enforceRootOwner,
trustedGroupId,
plan.publicPrefix,
)
} catch (error) {
fs.rmSync(finalRelease, { recursive: true, force: true })
throw error
}
return {
schema: 'hololake.release-broadcast-operator-receipt/v1',
state: 'ACTIVATED_EXPLICIT_SERVICE_RESTART_REQUIRED',
releaseId: plan.releaseId,
version: plan.version,
sourceCommit: plan.sourceCommit,
broadcastSha256: plan.broadcastSha256,
approvalId: plan.approvalId,
publicPrefix: plan.publicPrefix,
automaticUpload: false,
automaticActivation: false,
automaticRestart: false,
runtimeStateAfterDiskCommit: committed.state,
}
} finally {
if (candidateRoot) fs.rmSync(candidateRoot, { recursive: true, force: true })
fs.rmSync(lockDirectory, { recursive: true, force: true })
}
}
function parseArguments(argv) {
const command = argv[0]
const values = {}
for (let index = 1; index < argv.length; index += 1) {
const key = argv[index]
if (!key.startsWith('--') || index + 1 >= argv.length) fail('HOLOLAKE_RELEASE_OPERATOR_ARGUMENT_INVALID')
values[key.slice(2)] = argv[++index]
}
return { command, values }
}
export function main(argv = process.argv.slice(2)) {
const { command, values } = parseArguments(argv)
const sourceDirectory = requireText(values.source, 'HOLOLAKE_RELEASE_OPERATOR_SOURCE_REQUIRED')
const humanApprovalPath = requireText(values['human-approval'], 'HOLOLAKE_RELEASE_OPERATOR_HUMAN_APPROVAL_PATH_REQUIRED')
if (command === 'verify') {
process.stdout.write(`${JSON.stringify(verifyReleaseBundle(sourceDirectory, humanApprovalPath), null, 2)}\n`)
return
}
if (command !== 'activate') fail('HOLOLAKE_RELEASE_OPERATOR_COMMAND_INVALID:verify|activate')
const receipt = activateRelease({
sourceDirectory,
humanApprovalPath,
stateRoot: requireText(values['state-root'], 'HOLOLAKE_RELEASE_OPERATOR_STATE_ROOT_REQUIRED'),
publicPrefix: values['public-prefix'] || DEFAULT_PUBLIC_PREFIX,
expected: {
releaseId: values['expect-release-id'],
version: values['expect-version'],
sourceCommit: values['expect-source-commit'],
broadcastSha256: values['expect-broadcast-sha256'],
},
})
process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`)
}
if (isMainModule(process.argv[1], import.meta.url)) {
try {
main()
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
process.exitCode = 1
}
}

View file

@ -0,0 +1,19 @@
{
"schema": "hololake.release-public-route/v1",
"state": "REGISTERED_PENDING_FRONT_DOOR_DEPLOYMENT",
"publicBaseUrl": "https://guanghulab.com/hololake/releases",
"releaseEndpoint": "https://guanghulab.com/hololake/releases/latest.json",
"publicPathPrefix": "/hololake/releases",
"originNode": "JD-FD-PRIMARY",
"originListener": "127.0.0.1:3940",
"frontDoorNode": "BS-GZ-006",
"frontDoorLoopbackPort": null,
"proxyRequestUriPolicy": "PRESERVE_FULL_PUBLIC_PATH",
"allowedMethods": ["GET", "HEAD"],
"requestBodyAllowed": false,
"latestCachePolicy": "NO_STORE",
"packageCachePolicy": "PUBLIC_IMMUTABLE",
"automaticUpload": false,
"automaticActivation": false,
"deployed": false
}

View file

@ -0,0 +1,54 @@
#!/usr/bin/env node
const PORT_PATTERN = /^(?:[1-9]\d{3,4})$/
const fail = (code) => {
throw new Error(code)
}
export function renderFrontDoorSnippet(portValue) {
const text = String(portValue ?? '')
if (!PORT_PATTERN.test(text)) fail('HOLOLAKE_RELEASE_FRONT_DOOR_PORT_REQUIRED')
const port = Number(text)
if (!Number.isInteger(port) || port < 1024 || port > 65535) fail('HOLOLAKE_RELEASE_FRONT_DOOR_PORT_INVALID')
return `location = /hololake/releases {
return 308 /hololake/releases/;
}
location ^~ /hololake/releases/ {
limit_except GET HEAD { deny all; }
client_max_body_size 0;
proxy_pass http://127.0.0.1:${port};
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header Authorization "";
proxy_set_header Cookie "";
proxy_set_header X-Forwarded-For "";
proxy_set_header X-Forwarded-Host "";
proxy_set_header X-Forwarded-Proto "";
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_connect_timeout 2s;
proxy_send_timeout 10s;
proxy_read_timeout 60s;
}
`
}
export function main(argv = process.argv.slice(2)) {
const portIndex = argv.indexOf('--loopback-port')
if (portIndex < 0 || portIndex + 1 >= argv.length || argv.length !== 2) {
fail('HOLOLAKE_RELEASE_FRONT_DOOR_USAGE:--loopback-port PORT')
}
process.stdout.write(renderFrontDoorSnippet(argv[portIndex + 1]))
}
if (process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href) {
try {
main()
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
process.exitCode = 1
}
}

View file

@ -0,0 +1,282 @@
#!/usr/bin/env node
import crypto from 'node:crypto'
import fs from 'node:fs'
import http from 'node:http'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const MAX_CONTROL_BYTES = 1024 * 1024
export const DEFAULT_PUBLIC_PREFIX = '/hololake/releases'
const VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/
const SHA256_PATTERN = /^[a-f0-9]{64}$/
const fail = (code) => {
throw new Error(code)
}
const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex')
const requireText = (value, code) => {
if (typeof value !== 'string' || value.trim() === '') fail(code)
return value.trim()
}
export function normalizePublicPrefix(value) {
const prefix = requireText(value, 'HOLOLAKE_RELEASE_PUBLIC_PREFIX_REQUIRED')
if (!prefix.startsWith('/') || prefix.endsWith('/') || prefix.includes('//') || prefix.includes('?') || prefix.includes('#')) {
fail('HOLOLAKE_RELEASE_PUBLIC_PREFIX_INVALID')
}
const normalized = path.posix.normalize(prefix)
if (normalized !== prefix || normalized === '/' || normalized.split('/').some((segment) => segment === '..')) {
fail('HOLOLAKE_RELEASE_PUBLIC_PREFIX_INVALID')
}
return prefix
}
function trustedRegularFile(file, { enforceRootOwner, maxBytes = null }) {
const info = fs.lstatSync(file)
if (!info.isFile() || info.isSymbolicLink()) fail('HOLOLAKE_RELEASE_FILE_NOT_REGULAR')
if ((info.mode & 0o022) !== 0) fail('HOLOLAKE_RELEASE_FILE_WRITABLE_BY_NON_OWNER')
if (enforceRootOwner && info.uid !== 0) fail('HOLOLAKE_RELEASE_FILE_NOT_ROOT_OWNED')
if (maxBytes !== null && info.size > maxBytes) fail('HOLOLAKE_RELEASE_CONTROL_FILE_TOO_LARGE')
return info
}
function safeRelative(root, value, code) {
const relative = requireText(value, code)
if (path.isAbsolute(relative) || relative.includes('\0')) fail(code)
const resolved = path.resolve(root, relative)
if (!resolved.startsWith(`${root}${path.sep}`)) fail(code)
return resolved
}
function readTrustedJson(file, options) {
trustedRegularFile(file, { ...options, maxBytes: MAX_CONTROL_BYTES })
return { bytes: fs.readFileSync(file), value: JSON.parse(fs.readFileSync(file, 'utf8')) }
}
function validatePlatform(platform, releaseDirectory, enforceRootOwner, sourceCommit, publicPrefix) {
const url = new URL(requireText(platform?.url, 'HOLOLAKE_RELEASE_PACKAGE_URL_REQUIRED'))
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
fail('HOLOLAKE_RELEASE_PACKAGE_URL_INVALID')
}
const packageName = decodeURIComponent(path.posix.basename(url.pathname))
if (!packageName || packageName === '.' || packageName === '..') fail('HOLOLAKE_RELEASE_PACKAGE_NAME_INVALID')
if (!url.pathname.startsWith(`${publicPrefix}/`) || url.pathname === `${publicPrefix}/latest.json` || url.pathname === `${publicPrefix}/health`) {
fail('HOLOLAKE_RELEASE_PACKAGE_PUBLIC_PREFIX_MISMATCH')
}
const packagePath = safeRelative(releaseDirectory, packageName, 'HOLOLAKE_RELEASE_PACKAGE_PATH_INVALID')
const packageInfo = trustedRegularFile(packagePath, { enforceRootOwner })
if (!Number.isSafeInteger(platform.size) || platform.size <= 0 || platform.size !== packageInfo.size) {
fail('HOLOLAKE_RELEASE_PACKAGE_SIZE_MISMATCH')
}
if (!SHA256_PATTERN.test(platform.sha256 || '') || sha256(fs.readFileSync(packagePath)) !== platform.sha256) {
fail('HOLOLAKE_RELEASE_PACKAGE_SHA256_MISMATCH')
}
requireText(platform.signature, 'HOLOLAKE_RELEASE_PACKAGE_SIGNATURE_REQUIRED')
const codeReceiptId = requireText(platform.platformCodeSignatureReceipt, 'HOLOLAKE_RELEASE_CODESIGN_RECEIPT_REQUIRED')
const notarizationReceiptId = requireText(platform.notarizationReceipt, 'HOLOLAKE_RELEASE_NOTARIZATION_RECEIPT_REQUIRED')
const codeReceipt = readTrustedJson(
safeRelative(releaseDirectory, `${codeReceiptId}.json`, 'HOLOLAKE_RELEASE_CODESIGN_RECEIPT_PATH_INVALID'),
{ enforceRootOwner },
).value
const notarizationReceipt = readTrustedJson(
safeRelative(releaseDirectory, `${notarizationReceiptId}.json`, 'HOLOLAKE_RELEASE_NOTARIZATION_RECEIPT_PATH_INVALID'),
{ enforceRootOwner },
).value
if (
codeReceipt?.schema !== 'hololake.platform-code-signature-receipt/v1' ||
codeReceipt?.state !== 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED' ||
codeReceipt?.sourceCommit !== sourceCommit
) fail('HOLOLAKE_RELEASE_CODESIGN_RECEIPT_INVALID')
if (
notarizationReceipt?.schema !== 'hololake.apple-notarization-receipt/v1' ||
notarizationReceipt?.state !== 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED' ||
notarizationReceipt?.sourceCommit !== sourceCommit
) fail('HOLOLAKE_RELEASE_NOTARIZATION_RECEIPT_INVALID')
return { pathname: url.pathname, file: packagePath, size: packageInfo.size }
}
function loadActiveRelease(stateRoot, enforceRootOwner, publicPrefix) {
const activationPath = path.join(stateRoot, 'ACTIVE.json')
if (!fs.existsSync(activationPath)) {
return { state: 'EMPTY_FAIL_CLOSED', release: null, reasonCode: null, publicPrefix }
}
const activation = readTrustedJson(activationPath, { enforceRootOwner }).value
if (
activation?.schema !== 'hololake.release-broadcast-activation/v1' ||
activation?.state !== 'HUMAN_APPROVED_SIGNED_NOTARIZED_RELEASE_ACTIVE'
) fail('HOLOLAKE_RELEASE_ACTIVATION_INVALID')
const releaseId = requireText(activation.releaseId, 'HOLOLAKE_RELEASE_ID_REQUIRED')
const version = requireText(activation.version, 'HOLOLAKE_RELEASE_VERSION_REQUIRED')
if (!VERSION_PATTERN.test(version)) fail('HOLOLAKE_RELEASE_VERSION_INVALID')
if (!SHA256_PATTERN.test(activation.broadcastSha256 || '')) fail('HOLOLAKE_RELEASE_BROADCAST_SHA256_INVALID')
requireText(activation.humanApprovalReceipt, 'HOLOLAKE_RELEASE_HUMAN_APPROVAL_REQUIRED')
const broadcastPath = safeRelative(stateRoot, activation.broadcastRelativePath, 'HOLOLAKE_RELEASE_BROADCAST_PATH_INVALID')
const pipelinePath = safeRelative(stateRoot, activation.pipelineReceiptRelativePath, 'HOLOLAKE_RELEASE_PIPELINE_PATH_INVALID')
const approvalPath = safeRelative(stateRoot, activation.humanApprovalReceiptRelativePath, 'HOLOLAKE_RELEASE_HUMAN_APPROVAL_PATH_INVALID')
const broadcastDocument = readTrustedJson(broadcastPath, { enforceRootOwner })
const pipelineReceipt = readTrustedJson(pipelinePath, { enforceRootOwner }).value
const approvalReceipt = readTrustedJson(approvalPath, { enforceRootOwner }).value
if (sha256(broadcastDocument.bytes) !== activation.broadcastSha256) fail('HOLOLAKE_RELEASE_BROADCAST_SHA256_MISMATCH')
if (
pipelineReceipt?.schema !== 'hololake.signed-release-pipeline-receipt/v1' ||
pipelineReceipt?.state !== 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD' ||
pipelineReceipt?.broadcastSha256 !== activation.broadcastSha256 ||
!/^[a-f0-9]{40}$/.test(pipelineReceipt?.sourceCommit || '') ||
pipelineReceipt?.automaticUpload !== false ||
pipelineReceipt?.automaticActivation !== false
) fail('HOLOLAKE_RELEASE_PIPELINE_RECEIPT_INVALID')
if (
approvalReceipt?.schema !== 'hololake.release-broadcast-human-approval/v1' ||
approvalReceipt?.state !== 'HUMAN_APPROVED_EXACT_SIGNED_NOTARIZED_RELEASE' ||
approvalReceipt?.approvalId !== activation.humanApprovalReceipt ||
approvalReceipt?.releaseId !== releaseId ||
approvalReceipt?.version !== version ||
approvalReceipt?.broadcastSha256 !== activation.broadcastSha256
) fail('HOLOLAKE_RELEASE_HUMAN_APPROVAL_INVALID')
const broadcast = broadcastDocument.value
if (
broadcast?.schema !== 'hololake.release-broadcast/v1' ||
broadcast?.releaseId !== releaseId ||
broadcast?.version !== version ||
broadcast?.hololake?.restart?.required !== true ||
broadcast?.hololake?.restart?.automaticAllowed !== false ||
broadcast?.hololake?.rollback?.supported !== true ||
broadcast?.hololake?.rollback?.healthReceiptRequired !== true ||
!broadcast.platforms || typeof broadcast.platforms !== 'object' || Array.isArray(broadcast.platforms) ||
Object.keys(broadcast.platforms).length === 0
) fail('HOLOLAKE_RELEASE_BROADCAST_INVALID')
const releaseDirectory = path.dirname(broadcastPath)
const packages = new Map()
for (const platform of Object.values(broadcast.platforms)) {
const accepted = validatePlatform(platform, releaseDirectory, enforceRootOwner, pipelineReceipt.sourceCommit, publicPrefix)
if (packages.has(accepted.pathname)) fail('HOLOLAKE_RELEASE_PACKAGE_ROUTE_COLLISION')
packages.set(accepted.pathname, accepted)
}
return {
state: 'READY_SIGNED_NOTARIZED_BROADCAST',
release: { releaseId, version, broadcastBytes: broadcastDocument.bytes, packages },
reasonCode: null,
publicPrefix,
}
}
export function loadRuntimeState(stateRoot, { enforceRootOwner = false, publicPrefix = DEFAULT_PUBLIC_PREFIX } = {}) {
const resolvedRoot = path.resolve(stateRoot)
try {
const normalizedPrefix = normalizePublicPrefix(publicPrefix)
return loadActiveRelease(resolvedRoot, enforceRootOwner, normalizedPrefix)
} catch (error) {
return {
state: 'LOCKED_INVALID_RELEASE_EVIDENCE',
release: null,
reasonCode: error instanceof Error ? error.message : 'HOLOLAKE_RELEASE_UNKNOWN_FAILURE',
publicPrefix: null,
}
}
}
function jsonResponse(response, status, value) {
const body = Buffer.from(`${JSON.stringify(value)}\n`)
response.writeHead(status, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': body.length,
'Cache-Control': 'no-store',
'X-Content-Type-Options': 'nosniff',
})
response.end(body)
}
export function createReleaseServer(runtimeState) {
return http.createServer((request, response) => {
response.setHeader('X-Content-Type-Options', 'nosniff')
if (!['GET', 'HEAD'].includes(request.method || '')) {
response.writeHead(405, { Allow: 'GET, HEAD', 'Cache-Control': 'no-store' })
return response.end()
}
const pathname = new URL(request.url || '/', 'http://127.0.0.1').pathname
const publicHealthPath = runtimeState.publicPrefix ? `${runtimeState.publicPrefix}/health` : null
const publicLatestPath = runtimeState.publicPrefix ? `${runtimeState.publicPrefix}/latest.json` : null
if (pathname === '/health' || pathname === publicHealthPath) {
const status = runtimeState.state === 'LOCKED_INVALID_RELEASE_EVIDENCE' ? 503 : 200
return jsonResponse(response, status, {
schema: 'hololake.release-broadcast-health/v1',
state: runtimeState.state,
updateAvailable: runtimeState.release !== null,
automaticUpload: false,
automaticActivation: false,
upstreamUpdateSources: [],
...(runtimeState.publicPrefix ? {
publicPathPrefix: runtimeState.publicPrefix,
releaseEndpointPath: `${runtimeState.publicPrefix}/latest.json`,
} : {}),
...(runtimeState.release ? { releaseId: runtimeState.release.releaseId, version: runtimeState.release.version } : {}),
...(runtimeState.reasonCode ? { reasonCode: runtimeState.reasonCode } : {}),
})
}
if (pathname === '/latest.json' || pathname === publicLatestPath) {
if (runtimeState.state === 'EMPTY_FAIL_CLOSED') {
response.writeHead(204, { 'Cache-Control': 'no-store' })
return response.end()
}
if (!runtimeState.release) {
response.writeHead(503, { 'Cache-Control': 'no-store' })
return response.end()
}
response.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': runtimeState.release.broadcastBytes.length,
'Cache-Control': 'no-store',
'X-Content-Type-Options': 'nosniff',
})
return response.end(request.method === 'HEAD' ? undefined : runtimeState.release.broadcastBytes)
}
const packageFile = runtimeState.release?.packages.get(pathname)
if (packageFile) {
response.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Length': packageFile.size,
'Cache-Control': 'public, max-age=31536000, immutable',
'X-Content-Type-Options': 'nosniff',
})
if (request.method === 'HEAD') return response.end()
return fs.createReadStream(packageFile.file).on('error', () => response.destroy()).pipe(response)
}
response.writeHead(404, { 'Cache-Control': 'no-store' })
response.end()
})
}
export function main(env = process.env) {
const host = env.HOLOLAKE_RELEASE_HOST || '127.0.0.1'
if (host !== '127.0.0.1') fail('HOLOLAKE_RELEASE_LOOPBACK_BIND_REQUIRED')
const port = Number(env.HOLOLAKE_RELEASE_PORT || 3940)
if (!Number.isInteger(port) || port < 1024 || port > 65535) fail('HOLOLAKE_RELEASE_PORT_INVALID')
const stateRoot = path.resolve(env.HOLOLAKE_RELEASE_STATE_ROOT || '/var/lib/guanghu/hololake-release-broadcast')
const publicPrefix = env.HOLOLAKE_RELEASE_PUBLIC_PREFIX || DEFAULT_PUBLIC_PREFIX
const runtimeState = loadRuntimeState(stateRoot, {
enforceRootOwner: env.HOLOLAKE_RELEASE_ENFORCE_ROOT_OWNER === '1',
publicPrefix,
})
const server = createReleaseServer(runtimeState)
server.on('clientError', (_error, socket) => socket.end('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n'))
server.listen(port, host, () => {
process.stdout.write(`${JSON.stringify({ schema: 'hololake.release-broadcast-startup/v1', host, port, state: runtimeState.state })}\n`)
})
return server
}
export function isMainModule(argvPath, moduleUrl) {
return Boolean(argvPath) && fs.realpathSync(path.resolve(argvPath)) === fs.realpathSync(fileURLToPath(moduleUrl))
}
if (isMainModule(process.argv[1], import.meta.url)) {
try {
main()
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
process.exitCode = 1
}
}

View file

@ -659,6 +659,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
dependencies = [
"bitflags 2.13.1",
"block2",
"libc",
"objc2",
]
@ -908,6 +910,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "futf"
version = "0.1.5"
@ -1335,11 +1347,15 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
name = "hololake-native-desktop"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"dirs",
"fs2",
"ring",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-updater",
"tauri-runtime",
"tauri-runtime-wry",
@ -2973,6 +2989,30 @@ dependencies = [
"web-sys",
]
[[package]]
name = "rfd"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672"
dependencies = [
"block2",
"dispatch2",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"log",
"objc2",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.60.2",
]
[[package]]
name = "ring"
version = "0.17.14"
@ -3854,6 +3894,48 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-dialog"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2d3c1dbe38037e7f590cdf2492594d5ceebe031e7bc7e827509b22a999d2940"
dependencies = [
"log",
"raw-window-handle",
"rfd",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 2.0.20",
"url",
]
[[package]]
name = "tauri-plugin-fs"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371"
dependencies = [
"anyhow",
"dunce",
"glob",
"log",
"objc2-foundation",
"percent-encoding",
"schemars 0.8.22",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-plugin",
"tauri-utils",
"thiserror 2.0.20",
"toml 1.1.4+spec-1.1.0",
"url",
]
[[package]]
name = "tauri-plugin-updater"
version = "2.10.1"

View file

@ -15,13 +15,17 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2.5.4", features = [] }
[dependencies]
dirs = "6"
fs2 = "0.4"
ring = "0.17"
base64 = "0.22"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri = { version = "=2.10.2", features = [] }
tauri-runtime = "=2.10.0"
tauri-runtime-wry = "=2.10.0"
tauri-plugin-updater = "2.10.0"
tauri-plugin-dialog = "=2.7.2"
uuid = { version = "1", features = ["v4"] }
[dev-dependencies]

View file

@ -0,0 +1,5 @@
{
"schema": "hololake.dynamic-routing-trust/v1",
"state": "UNPROVISIONED_FAIL_CLOSED",
"signers": []
}

View file

@ -0,0 +1,959 @@
use crate::direct_local_session::{
append_event_at, authenticate_at, direct_session_root, open_at, resume_at,
AppendSessionEventInput, AuthenticateSessionInput, OpenSessionInput, ResumeSessionInput,
};
use crate::dynamic_capability_routing::{
install_trusted_registry_at, record_health_at, resolve_at as resolve_capability_route_at,
routing_root as dynamic_routing_root, DynamicNodeRegistry, ResolveCapabilityRouteInput,
SignedNodeHealth,
};
use crate::pncc_receipt_projection::{
append_remote_read_at as project_pncc_remote_read_at,
append_repository_binding_at as project_pncc_repository_binding_at,
query_at as query_pncc_projection_at, QueryPnccReceiptProjectionInput,
};
use crate::pncc_remote_git::{
read_mounted_remote_object_at as read_mounted_pncc_remote_at, ReadMountedPnccRemoteObjectInput,
};
use crate::pncc_repository_binding::{
inspect_mounted_at as inspect_mounted_pncc_at, InspectMountedPnccRepositoryInput,
};
use ring::digest::{digest, SHA256};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fs::{self, OpenOptions};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path, PathBuf};
use std::sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
mpsc, Arc,
};
use std::thread;
use std::time::Duration;
use tauri::{AppHandle, Manager};
use uuid::Uuid;
const BROKER_SCHEMA: &str = "hololake.direct-local-broker/v1";
const MAX_REQUEST_BYTES: u64 = 1024 * 1024;
pub struct DirectLocalBrokerHandle {
shutdown: Arc<AtomicBool>,
authenticated_connections: Arc<AtomicUsize>,
socket_path: PathBuf,
worker: Option<thread::JoinHandle<()>>,
}
impl DirectLocalBrokerHandle {
pub fn active_connection_count(&self) -> usize {
self.authenticated_connections.load(Ordering::Acquire)
}
}
struct AuthenticatedConnectionGuard(Arc<AtomicUsize>);
impl Drop for AuthenticatedConnectionGuard {
fn drop(&mut self) {
self.0.fetch_sub(1, Ordering::AcqRel);
}
}
struct BrokerStorageRoots {
session: PathBuf,
routing: PathBuf,
pncc_mount: PathBuf,
pncc_remote: PathBuf,
pncc_projection: PathBuf,
}
impl Drop for DirectLocalBrokerHandle {
fn drop(&mut self) {
self.shutdown.store(true, Ordering::Release);
let _ = UnixStream::connect(&self.socket_path);
if let Some(worker) = self.worker.take() {
let _ = worker.join();
}
}
}
#[derive(Debug, Deserialize)]
#[serde(
tag = "operation",
content = "input",
rename_all = "SCREAMING_SNAKE_CASE"
)]
enum BrokerRequest {
Ping,
OpenSession(OpenSessionInput),
ResumeSession(ResumeSessionInput),
AppendEvent(AppendSessionEventInput),
ResolveCapabilityRoute(AuthenticatedRouteInput),
InstallDynamicNodeRegistry(AuthenticatedRegistryInput),
RecordSignedNodeHealth(AuthenticatedHealthInput),
InspectMountedPnccRepository(AuthenticatedPnccMountInput),
ReadMountedPnccRemoteObject(AuthenticatedPnccRemoteReadInput),
QueryPnccReceiptProjection(AuthenticatedPnccProjectionQueryInput),
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedRouteInput {
session: AuthenticateSessionInput,
route: ResolveCapabilityRouteInput,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedRegistryInput {
session: AuthenticateSessionInput,
registry: DynamicNodeRegistry,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedHealthInput {
session: AuthenticateSessionInput,
health: SignedNodeHealth,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedPnccMountInput {
session: AuthenticateSessionInput,
mount: InspectMountedPnccRepositoryInput,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedPnccRemoteReadInput {
session: AuthenticateSessionInput,
read: ReadMountedPnccRemoteObjectInput,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AuthenticatedPnccProjectionQueryInput {
session: AuthenticateSessionInput,
query: QueryPnccReceiptProjectionInput,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct BrokerResponse {
schema: &'static str,
request_id: String,
ok: bool,
result: Option<Value>,
error: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct BrokerDescriptor {
schema: &'static str,
state: &'static str,
transport: &'static str,
socket_path: String,
max_request_bytes: u64,
process_id: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ConnectorDescriptor {
schema: String,
state: String,
transport: String,
socket_path: String,
process_id: u32,
}
pub fn start(app: &AppHandle) -> Result<DirectLocalBrokerHandle, Box<dyn std::error::Error>> {
let session_root = direct_session_root(app).map_err(std::io::Error::other)?;
let routing_root = dynamic_routing_root(app).map_err(std::io::Error::other)?;
let app_data = app.path().app_data_dir()?;
let descriptor_path = app_data.join("direct-local-broker-v1.json");
let socket_path = short_socket_path(&app_data);
start_at(session_root, routing_root, descriptor_path, socket_path).map_err(|error| error.into())
}
pub fn run_connector() -> Result<(), String> {
let descriptor_path = connector_descriptor_path()?;
let descriptor: ConnectorDescriptor = serde_json::from_slice(
&fs::read(&descriptor_path)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_UNAVAILABLE: {error}"))?,
)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_INVALID: {error}"))?;
if descriptor.schema != BROKER_SCHEMA
|| descriptor.state != "LISTENING"
|| descriptor.transport != "UNIX_STREAM_JSON_LINES"
{
return Err("HOLOLAKE_BROKER_DESCRIPTOR_UNSUPPORTED".into());
}
let mut stream = connect_with_startup_grace(&descriptor)?;
let read_stream = stream
.try_clone()
.map_err(|error| format!("HOLOLAKE_CONNECTOR_CLONE_FAILED: {error}"))?;
let output = thread::Builder::new()
.name("hololake-connector-output".into())
.spawn(move || -> Result<(), String> {
let mut reader = BufReader::new(read_stream);
let mut stdout = io::stdout().lock();
io::copy(&mut reader, &mut stdout)
.and_then(|_| stdout.flush())
.map_err(|error| format!("HOLOLAKE_CONNECTOR_OUTPUT_FAILED: {error}"))?;
Ok(())
})
.map_err(|error| format!("HOLOLAKE_CONNECTOR_OUTPUT_THREAD_FAILED: {error}"))?;
let mut stdin = io::stdin().lock();
io::copy(&mut stdin, &mut stream)
.and_then(|_| stream.shutdown(std::net::Shutdown::Write))
.map_err(|error| format!("HOLOLAKE_CONNECTOR_INPUT_FAILED: {error}"))?;
output
.join()
.map_err(|_| "HOLOLAKE_CONNECTOR_OUTPUT_THREAD_PANICKED".to_string())??;
Ok(())
}
fn connect_with_startup_grace(descriptor: &ConnectorDescriptor) -> Result<UnixStream, String> {
if descriptor.process_id == 0 {
return Err("HOLOLAKE_BROKER_DESCRIPTOR_UNSUPPORTED".into());
}
let mut last_error = None;
for _ in 0..50 {
match UnixStream::connect(&descriptor.socket_path) {
Ok(stream) => return Ok(stream),
Err(error)
if matches!(
error.kind(),
std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::NotFound
) =>
{
last_error = Some(error);
thread::sleep(Duration::from_millis(100));
}
Err(error) => return Err(format!("HOLOLAKE_DIRECT_LOCAL_BROKER_NOT_RUNNING: {error}")),
}
}
Err(format!(
"HOLOLAKE_DIRECT_LOCAL_BROKER_NOT_RUNNING: {}",
last_error
.map(|error| error.to_string())
.unwrap_or_else(|| "startup grace exhausted".into())
))
}
fn connector_descriptor_path() -> Result<PathBuf, String> {
if let Some(path) = std::env::var_os("HOLOLAKE_BROKER_DESCRIPTOR") {
return Ok(PathBuf::from(path));
}
dirs::data_dir()
.map(|root| {
root.join("world.guanghu.hololake")
.join("direct-local-broker-v1.json")
})
.ok_or_else(|| "HOLOLAKE_APP_DATA_UNAVAILABLE".into())
}
fn start_at(
session_root: PathBuf,
routing_root: PathBuf,
descriptor_path: PathBuf,
socket_path: PathBuf,
) -> Result<DirectLocalBrokerHandle, String> {
let pncc_mount_root = session_root
.parent()
.ok_or("HOLOLAKE_BROKER_PNCC_STORAGE_BOUNDARY_INVALID")?
.join("pncc-stage-one-v1")
.join("repository-mounts");
fs::create_dir_all(&pncc_mount_root)
.map_err(|error| format!("HOLOLAKE_BROKER_PNCC_STORAGE_UNAVAILABLE: {error}"))?;
let pncc_remote_root = session_root
.parent()
.ok_or("HOLOLAKE_BROKER_PNCC_STORAGE_BOUNDARY_INVALID")?
.join("pncc-stage-one-v1")
.join("remote-object-channel");
fs::create_dir_all(&pncc_remote_root)
.map_err(|error| format!("HOLOLAKE_BROKER_PNCC_STORAGE_UNAVAILABLE: {error}"))?;
let pncc_projection_root = session_root
.parent()
.ok_or("HOLOLAKE_BROKER_PNCC_STORAGE_BOUNDARY_INVALID")?
.join("pncc-stage-one-v1")
.join("receipt-projection");
fs::create_dir_all(&pncc_projection_root)
.map_err(|error| format!("HOLOLAKE_BROKER_PNCC_STORAGE_UNAVAILABLE: {error}"))?;
if let Some(parent) = socket_path.parent() {
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_BROKER_RUNTIME_DIR_FAILED: {error}"))?;
fs::set_permissions(parent, fs::Permissions::from_mode(0o700))
.map_err(|error| format!("HOLOLAKE_BROKER_RUNTIME_PERMISSIONS_FAILED: {error}"))?;
}
if socket_path.exists() {
if UnixStream::connect(&socket_path).is_ok() {
return Err("HOLOLAKE_DIRECT_LOCAL_BROKER_ALREADY_RUNNING".into());
}
fs::remove_file(&socket_path)
.map_err(|error| format!("HOLOLAKE_BROKER_STALE_SOCKET_REMOVE_FAILED: {error}"))?;
}
let listener = UnixListener::bind(&socket_path)
.map_err(|error| format!("HOLOLAKE_BROKER_BIND_FAILED: {error}"))?;
fs::set_permissions(&socket_path, fs::Permissions::from_mode(0o600))
.map_err(|error| format!("HOLOLAKE_BROKER_SOCKET_PERMISSIONS_FAILED: {error}"))?;
listener
.set_nonblocking(true)
.map_err(|error| format!("HOLOLAKE_BROKER_NONBLOCKING_FAILED: {error}"))?;
write_descriptor(
&descriptor_path,
&BrokerDescriptor {
schema: BROKER_SCHEMA,
state: "LISTENING",
transport: "UNIX_STREAM_JSON_LINES",
socket_path: socket_path.to_string_lossy().into_owned(),
max_request_bytes: MAX_REQUEST_BYTES,
process_id: std::process::id(),
},
)?;
let shutdown = Arc::new(AtomicBool::new(false));
let authenticated_connections = Arc::new(AtomicUsize::new(0));
let worker_shutdown = Arc::clone(&shutdown);
let worker_authenticated_connections = Arc::clone(&authenticated_connections);
let worker_socket = socket_path.clone();
let (ready_sender, ready_receiver) = mpsc::sync_channel(1);
let worker = thread::Builder::new()
.name("hololake-direct-local-broker".into())
.spawn(move || {
let _ = ready_sender.send(());
serve(
listener,
BrokerStorageRoots {
session: session_root,
routing: routing_root,
pncc_mount: pncc_mount_root,
pncc_remote: pncc_remote_root,
pncc_projection: pncc_projection_root,
},
&worker_shutdown,
&worker_authenticated_connections,
);
let _ = fs::remove_file(worker_socket);
})
.map_err(|error| format!("HOLOLAKE_BROKER_THREAD_FAILED: {error}"))?;
ready_receiver
.recv_timeout(Duration::from_secs(2))
.map_err(|error| format!("HOLOLAKE_BROKER_STARTUP_FAILED: {error}"))?;
Ok(DirectLocalBrokerHandle {
shutdown,
authenticated_connections,
socket_path,
worker: Some(worker),
})
}
fn serve(
listener: UnixListener,
roots: BrokerStorageRoots,
shutdown: &AtomicBool,
authenticated_connections: &Arc<AtomicUsize>,
) {
while !shutdown.load(Ordering::Acquire) {
match listener.accept() {
Ok((stream, _)) => {
if shutdown.load(Ordering::Acquire) {
break;
}
if stream.set_nonblocking(false).is_err() {
continue;
}
let root = roots.session.clone();
let routes = roots.routing.clone();
let pncc_mounts = roots.pncc_mount.clone();
let pncc_remote = roots.pncc_remote.clone();
let pncc_projection = roots.pncc_projection.clone();
let client_authenticated_connections = Arc::clone(authenticated_connections);
let _ = thread::Builder::new()
.name("hololake-direct-local-client".into())
.spawn(move || {
serve_connection(
stream,
&root,
&routes,
&pncc_mounts,
&pncc_remote,
&pncc_projection,
client_authenticated_connections,
)
});
}
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
thread::sleep(Duration::from_millis(25));
}
Err(_) => break,
}
}
}
fn serve_connection(
mut stream: UnixStream,
session_root: &Path,
routing_root: &Path,
pncc_mount_root: &Path,
pncc_remote_root: &Path,
pncc_projection_root: &Path,
authenticated_connections: Arc<AtomicUsize>,
) {
let read_stream = match stream.try_clone() {
Ok(stream) => stream,
Err(_) => return,
};
let mut reader = BufReader::new(read_stream);
let mut authenticated_connection = None;
loop {
let mut bytes = Vec::new();
let read = match reader
.by_ref()
.take(MAX_REQUEST_BYTES + 1)
.read_until(b'\n', &mut bytes)
{
Ok(read) => read,
Err(_) => return,
};
if read == 0 {
return;
}
let request_authenticates = request_establishes_authenticated_connection(&bytes);
let response = if read as u64 > MAX_REQUEST_BYTES || !bytes.ends_with(b"\n") {
BrokerResponse::error("HOLOLAKE_BROKER_REQUEST_TOO_LARGE")
} else {
dispatch(
session_root,
routing_root,
pncc_mount_root,
pncc_remote_root,
pncc_projection_root,
&bytes[..bytes.len() - 1],
)
};
if response.ok && request_authenticates && authenticated_connection.is_none() {
authenticated_connections.fetch_add(1, Ordering::AcqRel);
authenticated_connection = Some(AuthenticatedConnectionGuard(Arc::clone(
&authenticated_connections,
)));
}
if serde_json::to_writer(&mut stream, &response).is_err()
|| stream.write_all(b"\n").is_err()
|| stream.flush().is_err()
{
return;
}
if response.error.as_deref() == Some("HOLOLAKE_BROKER_REQUEST_TOO_LARGE") {
return;
}
}
}
fn request_establishes_authenticated_connection(bytes: &[u8]) -> bool {
serde_json::from_slice::<serde_json::Value>(bytes)
.ok()
.and_then(|value| {
value
.get("operation")
.and_then(Value::as_str)
.map(str::to_owned)
})
.is_some_and(|operation| matches!(operation.as_str(), "OPEN_SESSION" | "RESUME_SESSION"))
}
fn dispatch(
session_root: &Path,
routing_root: &Path,
pncc_mount_root: &Path,
pncc_remote_root: &Path,
pncc_projection_root: &Path,
bytes: &[u8],
) -> BrokerResponse {
let request: BrokerRequest = match serde_json::from_slice(bytes) {
Ok(request) => request,
Err(error) => {
return BrokerResponse::error(&format!("HOLOLAKE_BROKER_REQUEST_INVALID: {error}"))
}
};
let result = match request {
BrokerRequest::Ping => serde_json::to_value(serde_json::json!({
"state": "READY",
"continuityOwner": "HOLOLAKE",
"mcpRole": "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY"
}))
.map_err(|error| error.to_string()),
BrokerRequest::OpenSession(input) => open_at(session_root, input)
.and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())),
BrokerRequest::ResumeSession(input) => resume_at(session_root, input)
.and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())),
BrokerRequest::AppendEvent(input) => append_event_at(session_root, input)
.and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())),
BrokerRequest::ResolveCapabilityRoute(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|duration| duration.as_millis() as u64)
.map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}"));
now.and_then(|now| resolve_capability_route_at(routing_root, input.route, now))
.and_then(|receipt| {
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
BrokerRequest::InstallDynamicNodeRegistry(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
install_trusted_registry_at(routing_root, input.registry).and_then(|receipt| {
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
BrokerRequest::RecordSignedNodeHealth(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
record_health_at(routing_root, input.health).and_then(|receipt| {
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
BrokerRequest::InspectMountedPnccRepository(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
inspect_mounted_pncc_at(pncc_mount_root, input.mount).and_then(|receipt| {
project_pncc_repository_binding_at(pncc_projection_root, &receipt)?;
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
BrokerRequest::ReadMountedPnccRemoteObject(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
read_mounted_pncc_remote_at(pncc_remote_root, input.read).and_then(|receipt| {
project_pncc_remote_read_at(pncc_projection_root, &receipt)?;
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
BrokerRequest::QueryPnccReceiptProjection(input) => {
if let Err(error) = authenticate_at(session_root, &input.session) {
return BrokerResponse::error(&error);
}
query_pncc_projection_at(pncc_projection_root, input.query).and_then(|receipt| {
serde_json::to_value(receipt).map_err(|error| error.to_string())
})
}
};
match result {
Ok(value) => BrokerResponse::success(value),
Err(error) => BrokerResponse::error(&error),
}
}
impl BrokerResponse {
fn success(result: Value) -> Self {
Self {
schema: BROKER_SCHEMA,
request_id: Uuid::new_v4().to_string(),
ok: true,
result: Some(result),
error: None,
}
}
fn error(error: &str) -> Self {
Self {
schema: BROKER_SCHEMA,
request_id: Uuid::new_v4().to_string(),
ok: false,
result: None,
error: Some(error.into()),
}
}
}
fn short_socket_path(app_data: &Path) -> PathBuf {
let key = digest(&SHA256, app_data.to_string_lossy().as_bytes())
.as_ref()
.iter()
.take(8)
.map(|byte| format!("{byte:02x}"))
.collect::<String>();
std::env::temp_dir()
.join(format!("hololake-{key}"))
.join("broker.sock")
}
fn write_descriptor(path: &Path, descriptor: &BrokerDescriptor) -> Result<(), String> {
let parent = path
.parent()
.ok_or("HOLOLAKE_BROKER_DESCRIPTOR_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_DIR_FAILED: {error}"))?;
let temporary = parent.join(format!(".broker-{}.tmp", Uuid::new_v4()));
let bytes = serde_json::to_vec_pretty(descriptor)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_INVALID: {error}"))?;
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(&temporary)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_WRITE_FAILED: {error}"))?;
fs::rename(&temporary, path)
.map_err(|error| format!("HOLOLAKE_BROKER_DESCRIPTOR_WRITE_FAILED: {error}"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::direct_local_session::{issue_ticket_at, IssueDiscoveryTicketInput};
use tempfile::TempDir;
fn request(socket: &Path, value: Value) -> Value {
let mut stream = UnixStream::connect(socket).unwrap();
serde_json::to_writer(&mut stream, &value).unwrap();
stream.write_all(b"\n").unwrap();
let mut line = String::new();
BufReader::new(stream).read_line(&mut line).unwrap();
serde_json::from_str(&line).unwrap()
}
#[test]
fn broker_resumes_after_socket_disconnect() {
let temp = TempDir::new().unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let sessions = temp.path().join("sessions");
fs::create_dir_all(&sessions).unwrap();
let routes = temp.path().join("routes");
fs::create_dir_all(&routes).unwrap();
let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
let ticket = issue_ticket_at(
&sessions,
IssueDiscoveryTicketInput {
account_id: "human-1".into(),
lane_id: "DEV-1".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let opened = request(
&socket,
serde_json::json!({
"operation": "OPEN_SESSION",
"input": {
"accountId": "human-1",
"laneId": "DEV-1",
"clientInstanceId": "codex-1",
"discoveryTicket": ticket.discovery_ticket
}
}),
);
assert_eq!(opened["ok"], true);
let resumed = request(
&socket,
serde_json::json!({
"operation": "RESUME_SESSION",
"input": {
"accountId": "human-1",
"sessionId": opened["result"]["sessionId"],
"clientInstanceId": "codex-restarted",
"resumeSecret": opened["result"]["resumeSecret"]
}
}),
);
assert_eq!(resumed["ok"], true);
assert_eq!(resumed["result"]["state"], "RESUMED");
}
#[test]
fn broker_can_restart_and_resume_persisted_session() {
let temp = TempDir::new().unwrap();
let sessions = temp.path().join("sessions");
fs::create_dir_all(&sessions).unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let ticket = issue_ticket_at(
&sessions,
IssueDiscoveryTicketInput {
account_id: "human-1".into(),
lane_id: "DEV-1".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let routes = temp.path().join("routes");
fs::create_dir_all(&routes).unwrap();
let first = start_at(
sessions.clone(),
routes.clone(),
descriptor.clone(),
socket.clone(),
)
.unwrap();
let opened = request(
&socket,
serde_json::json!({
"operation": "OPEN_SESSION",
"input": {
"accountId": "human-1",
"laneId": "DEV-1",
"clientInstanceId": "codex-1",
"discoveryTicket": ticket.discovery_ticket
}
}),
);
drop(first);
for _ in 0..40 {
if !socket.exists() {
break;
}
thread::sleep(Duration::from_millis(10));
}
let _second = start_at(sessions, routes, descriptor, socket.clone()).unwrap();
let resumed = request(
&socket,
serde_json::json!({
"operation": "RESUME_SESSION",
"input": {
"accountId": "human-1",
"sessionId": opened["result"]["sessionId"],
"clientInstanceId": "codex-after-hololake-restart",
"resumeSecret": opened["result"]["resumeSecret"]
}
}),
);
assert_eq!(resumed["result"]["state"], "RESUMED");
}
#[test]
fn broker_route_resolution_requires_the_active_session_secret() {
let temp = TempDir::new().unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let sessions = temp.path().join("sessions");
let routes = temp.path().join("routes");
fs::create_dir_all(&sessions).unwrap();
fs::create_dir_all(&routes).unwrap();
let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
let response = request(
&socket,
serde_json::json!({
"operation": "RESOLVE_CAPABILITY_ROUTE",
"input": {
"session": {
"accountId": "human-1",
"sessionId": "invented-session",
"resumeSecret": "invented-secret-long-enough"
},
"route": {
"humanId": "human-1",
"personaId": "ICE-P-ZY001",
"domainId": "DOM-FIFTH-0001",
"capabilityId": "GH-PNCC-READ"
}
}
}),
);
assert_eq!(response["ok"], false);
assert_eq!(
response["error"],
"HOLOLAKE_DIRECT_ACTIVE_SESSION_NOT_FOUND"
);
}
#[test]
fn authenticated_ai_still_cannot_install_registry_before_trust_is_provisioned() {
let temp = TempDir::new().unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let sessions = temp.path().join("sessions");
let routes = temp.path().join("routes");
fs::create_dir_all(&sessions).unwrap();
fs::create_dir_all(&routes).unwrap();
let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
let ticket = issue_ticket_at(
&sessions,
IssueDiscoveryTicketInput {
account_id: "human-1".into(),
lane_id: "DEV-1".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let opened = request(
&socket,
serde_json::json!({
"operation": "OPEN_SESSION",
"input": {
"accountId": "human-1",
"laneId": "DEV-1",
"clientInstanceId": "codex-1",
"discoveryTicket": ticket.discovery_ticket
}
}),
);
let response = request(
&socket,
serde_json::json!({
"operation": "INSTALL_DYNAMIC_NODE_REGISTRY",
"input": {
"session": {
"accountId": "human-1",
"sessionId": opened["result"]["sessionId"],
"resumeSecret": opened["result"]["resumeSecret"]
},
"registry": {
"schema": "hololake.dynamic-node-registry/v1",
"registryId": "candidate-registry",
"version": 1,
"nodes": [],
"signerId": "invented-signer",
"signature": "invented-signature"
}
}
}),
);
assert_eq!(response["ok"], false);
assert_eq!(
response["error"],
"HOLOLAKE_DYNAMIC_ROUTING_TRUST_UNPROVISIONED"
);
}
#[test]
fn pncc_projection_is_read_only_and_requires_the_active_session_secret() {
let temp = TempDir::new().unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let sessions = temp.path().join("sessions");
let routes = temp.path().join("routes");
fs::create_dir_all(&sessions).unwrap();
fs::create_dir_all(&routes).unwrap();
let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
let ticket = issue_ticket_at(
&sessions,
IssueDiscoveryTicketInput {
account_id: "human-1".into(),
lane_id: "DEV-1".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let opened = request(
&socket,
serde_json::json!({
"operation": "OPEN_SESSION",
"input": {
"accountId": "human-1",
"laneId": "DEV-1",
"clientInstanceId": "codex-1",
"discoveryTicket": ticket.discovery_ticket
}
}),
);
let query = |secret: Value| {
request(
&socket,
serde_json::json!({
"operation": "QUERY_PNCC_RECEIPT_PROJECTION",
"input": {
"session": {
"accountId": "human-1",
"sessionId": opened["result"]["sessionId"],
"resumeSecret": secret
},
"query": {"afterSequence": 0, "limit": 25}
}
}),
)
};
let rejected = query(Value::String("wrong-secret-long-enough".into()));
assert_eq!(rejected["ok"], false);
assert_eq!(rejected["error"], "HOLOLAKE_DIRECT_SESSION_NOT_AUTHORIZED");
let verified = query(opened["result"]["resumeSecret"].clone());
assert_eq!(verified["ok"], true);
assert_eq!(verified["result"]["authorityStore"], false);
assert_eq!(verified["result"]["writeAuthority"], false);
assert_eq!(verified["result"]["modelInstanceFieldsPresent"], false);
assert_eq!(verified["result"]["emptyMeansOffline"], false);
assert_eq!(verified["result"]["returnedEventCount"], 0);
}
#[test]
fn only_an_authenticated_persistent_connector_counts_as_online() {
let temp = TempDir::new().unwrap();
let socket = temp.path().join("runtime/broker.sock");
let descriptor = temp.path().join("broker.json");
let sessions = temp.path().join("sessions");
let routes = temp.path().join("routes");
fs::create_dir_all(&sessions).unwrap();
fs::create_dir_all(&routes).unwrap();
let broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
let probe = UnixStream::connect(&socket).unwrap();
thread::sleep(Duration::from_millis(50));
assert_eq!(broker.active_connection_count(), 0);
drop(probe);
let ticket = issue_ticket_at(
&sessions,
IssueDiscoveryTicketInput {
account_id: "human-1".into(),
lane_id: "DEV-1".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let mut connector = UnixStream::connect(&socket).unwrap();
serde_json::to_writer(
&mut connector,
&serde_json::json!({
"operation": "OPEN_SESSION",
"input": {
"accountId": "human-1",
"laneId": "DEV-1",
"clientInstanceId": "codex-1",
"discoveryTicket": ticket.discovery_ticket
}
}),
)
.unwrap();
connector.write_all(b"\n").unwrap();
assert!(request_establishes_authenticated_connection(
br#"{"operation":"OPEN_SESSION"}"#
));
let mut response = String::new();
let mut response_reader = BufReader::new(connector.try_clone().unwrap());
response_reader.read_line(&mut response).unwrap();
assert_eq!(
serde_json::from_str::<Value>(&response).unwrap()["ok"],
true
);
thread::sleep(Duration::from_millis(50));
assert_eq!(broker.active_connection_count(), 1);
drop(connector);
drop(response_reader);
for _ in 0..20 {
if broker.active_connection_count() == 0 {
break;
}
thread::sleep(Duration::from_millis(10));
}
assert_eq!(broker.active_connection_count(), 0);
}
}

View file

@ -0,0 +1,852 @@
use fs2::FileExt;
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::time::{SystemTime, UNIX_EPOCH};
use tauri::{AppHandle, Manager};
use uuid::Uuid;
const SESSION_SCHEMA: &str = "hololake.direct-local-session/v1";
const MAX_ID_BYTES: usize = 128;
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenSessionInput {
pub account_id: String,
pub lane_id: String,
pub client_instance_id: String,
pub discovery_ticket: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IssueDiscoveryTicketInput {
pub account_id: String,
pub lane_id: String,
pub client_instance_id: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiscoveryTicketReceipt {
pub schema: &'static str,
pub state: &'static str,
pub account_key: String,
pub lane_id: String,
pub client_instance_id: String,
pub discovery_ticket: String,
pub issued_at_unix_ms: u128,
pub receipt_id: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResumeSessionInput {
pub account_id: String,
pub session_id: String,
pub client_instance_id: String,
pub resume_secret: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppendSessionEventInput {
pub account_id: String,
pub session_id: String,
pub resume_secret: String,
pub idempotency_key: String,
pub event_kind: String,
pub payload_sha256: String,
pub expected_previous_sequence: u64,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticateSessionInput {
pub account_id: String,
pub session_id: String,
pub resume_secret: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectSessionReceipt {
pub schema: &'static str,
pub state: &'static str,
pub account_key: String,
pub session_id: String,
pub lane_id: String,
pub client_instance_id: String,
pub opened_at_unix_ms: u128,
pub observed_at_unix_ms: u128,
pub last_event_sequence: u64,
pub resume_secret: Option<String>,
pub receipt_id: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionEventReceipt {
pub schema: &'static str,
pub state: &'static str,
pub session_id: String,
pub sequence: u64,
pub idempotency_key: String,
pub event_kind: String,
pub payload_sha256: String,
pub observed_at_unix_ms: u128,
pub receipt_id: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct SessionRecord {
schema: String,
account_key: String,
session_id: String,
lane_id: String,
client_instance_id: String,
resume_secret_sha256: String,
discovery_ticket_sha256: String,
opened_at_unix_ms: u128,
observed_at_unix_ms: u128,
last_event_sequence: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct EventRecord {
schema: String,
session_id: String,
sequence: u64,
idempotency_key: String,
event_kind: String,
payload_sha256: String,
observed_at_unix_ms: u128,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct DiscoveryTicketRecord {
schema: String,
account_key: String,
lane_id: String,
client_instance_id: String,
discovery_ticket_sha256: String,
issued_at_unix_ms: u128,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct ActiveSessionRecord {
schema: String,
account_key: String,
session_id: String,
lane_id: String,
}
#[tauri::command]
pub async fn issue_direct_local_discovery_ticket(
app: AppHandle,
input: IssueDiscoveryTicketInput,
) -> Result<DiscoveryTicketReceipt, String> {
let root = direct_session_root(&app)?;
tauri::async_runtime::spawn_blocking(move || issue_ticket_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn open_direct_local_session(
app: AppHandle,
input: OpenSessionInput,
) -> Result<DirectSessionReceipt, String> {
let root = direct_session_root(&app)?;
tauri::async_runtime::spawn_blocking(move || open_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn resume_direct_local_session(
app: AppHandle,
input: ResumeSessionInput,
) -> Result<DirectSessionReceipt, String> {
let root = direct_session_root(&app)?;
tauri::async_runtime::spawn_blocking(move || resume_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))?
}
#[tauri::command]
pub async fn append_direct_local_session_event(
app: AppHandle,
input: AppendSessionEventInput,
) -> Result<SessionEventReceipt, String> {
let root = direct_session_root(&app)?;
tauri::async_runtime::spawn_blocking(move || append_event_at(&root, input))
.await
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))?
}
pub(crate) fn direct_session_root(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("direct-local-session-v1");
fs::create_dir_all(&root)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
root.canonicalize()
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))
}
pub(crate) fn active_session_count_at(root: &Path) -> Result<usize, String> {
let accounts = root.join("accounts");
if !accounts.exists() {
return Ok(0);
}
let mut count = 0;
for entry in fs::read_dir(&accounts)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?
{
let entry = entry
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
let kind = entry
.file_type()
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
if !kind.is_dir() || kind.is_symlink() {
continue;
}
let active_path = entry.path().join("active-session.json");
if !active_path.exists() {
continue;
}
let metadata = fs::symlink_metadata(&active_path)
.map_err(|error| format!("HOLOLAKE_ACTIVE_SESSION_INVALID: {error}"))?;
if !metadata.file_type().is_file() || metadata.file_type().is_symlink() {
return Err("HOLOLAKE_ACTIVE_SESSION_INVALID".into());
}
let active: ActiveSessionRecord = read_json(&active_path, "ACTIVE_SESSION")?;
if active.schema != SESSION_SCHEMA
|| active.account_key != entry.file_name().to_string_lossy()
{
return Err("HOLOLAKE_ACTIVE_SESSION_INVALID".into());
}
count += 1;
}
Ok(count)
}
pub(crate) fn open_at(
root: &Path,
input: OpenSessionInput,
) -> Result<DirectSessionReceipt, String> {
validate_identifier(&input.account_id, "ACCOUNT")?;
validate_identifier(&input.lane_id, "LANE")?;
validate_identifier(&input.client_instance_id, "CLIENT")?;
validate_secret(&input.discovery_ticket, "DISCOVERY_TICKET")?;
let account_key = sha256_hex(input.account_id.as_bytes());
let _account_lock = lock_account(root, &account_key)?;
let active_path = active_session_path(root, &account_key);
let tickets_root = root.join("discovery-tickets");
let used_tickets_root = root.join("used-discovery-tickets");
fs::create_dir_all(&used_tickets_root)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
let discovery_ticket_sha256 = sha256_hex(input.discovery_ticket.as_bytes());
let ticket_path = tickets_root.join(format!("{discovery_ticket_sha256}.json"));
let used_ticket_path = used_tickets_root.join(format!("{discovery_ticket_sha256}.json"));
if used_ticket_path.exists() {
return Err("HOLOLAKE_DISCOVERY_TICKET_ALREADY_USED".into());
}
if active_path.exists() {
let active: ActiveSessionRecord = read_json(&active_path, "ACTIVE_SESSION")?;
if active.schema != SESSION_SCHEMA || active.account_key != account_key {
return Err("HOLOLAKE_ACTIVE_SESSION_INVALID".into());
}
return Err(format!(
"HOLOLAKE_ACCOUNT_ALREADY_HAS_ACTIVE_SESSION:{}:{}",
active.lane_id, active.session_id
));
}
let ticket: DiscoveryTicketRecord = read_json(&ticket_path, "DISCOVERY_TICKET")?;
if ticket.account_key != account_key
|| ticket.lane_id != input.lane_id
|| ticket.client_instance_id != input.client_instance_id
|| ticket.discovery_ticket_sha256 != discovery_ticket_sha256
{
return Err("HOLOLAKE_DISCOVERY_TICKET_NOT_AUTHORIZED".into());
}
fs::rename(&ticket_path, &used_ticket_path).map_err(|error| {
if error.kind() == std::io::ErrorKind::NotFound && used_ticket_path.exists() {
"HOLOLAKE_DISCOVERY_TICKET_ALREADY_USED".to_string()
} else {
format!("HOLOLAKE_DISCOVERY_TICKET_CONSUME_FAILED: {error}")
}
})?;
let session_id = Uuid::new_v4().to_string();
let resume_secret = format!("{}{}", Uuid::new_v4(), Uuid::new_v4());
let observed_at_unix_ms = now_unix_ms()?;
let record = SessionRecord {
schema: SESSION_SCHEMA.into(),
account_key: account_key.clone(),
session_id: session_id.clone(),
lane_id: input.lane_id,
client_instance_id: input.client_instance_id,
resume_secret_sha256: sha256_hex(resume_secret.as_bytes()),
discovery_ticket_sha256,
opened_at_unix_ms: observed_at_unix_ms,
observed_at_unix_ms,
last_event_sequence: 0,
};
write_record_atomic(&session_path(root, &account_key, &session_id), &record)?;
write_record_create_new(
&active_path,
&ActiveSessionRecord {
schema: SESSION_SCHEMA.into(),
account_key: account_key.clone(),
session_id: session_id.clone(),
lane_id: record.lane_id.clone(),
},
"ACTIVE_SESSION",
)?;
Ok(session_receipt("OPENED", &record, Some(resume_secret)))
}
pub(crate) fn issue_ticket_at(
root: &Path,
input: IssueDiscoveryTicketInput,
) -> Result<DiscoveryTicketReceipt, String> {
validate_identifier(&input.account_id, "ACCOUNT")?;
validate_identifier(&input.lane_id, "LANE")?;
validate_identifier(&input.client_instance_id, "CLIENT")?;
let account_key = sha256_hex(input.account_id.as_bytes());
let discovery_ticket = format!("{}{}", Uuid::new_v4(), Uuid::new_v4());
let discovery_ticket_sha256 = sha256_hex(discovery_ticket.as_bytes());
let issued_at_unix_ms = now_unix_ms()?;
let record = DiscoveryTicketRecord {
schema: SESSION_SCHEMA.into(),
account_key: account_key.clone(),
lane_id: input.lane_id.clone(),
client_instance_id: input.client_instance_id.clone(),
discovery_ticket_sha256: discovery_ticket_sha256.clone(),
issued_at_unix_ms,
};
let path = root
.join("discovery-tickets")
.join(format!("{discovery_ticket_sha256}.json"));
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
}
write_record_create_new(&path, &record, "DISCOVERY_TICKET")?;
Ok(DiscoveryTicketReceipt {
schema: SESSION_SCHEMA,
state: "ISSUED",
account_key,
lane_id: input.lane_id,
client_instance_id: input.client_instance_id,
discovery_ticket,
issued_at_unix_ms,
receipt_id: sha256_hex(
format!("ISSUED\n{discovery_ticket_sha256}\n{issued_at_unix_ms}").as_bytes(),
),
})
}
pub(crate) fn resume_at(
root: &Path,
input: ResumeSessionInput,
) -> Result<DirectSessionReceipt, String> {
validate_identifier(&input.account_id, "ACCOUNT")?;
validate_identifier(&input.session_id, "SESSION")?;
validate_identifier(&input.client_instance_id, "CLIENT")?;
validate_secret(&input.resume_secret, "RESUME_SECRET")?;
let account_key = sha256_hex(input.account_id.as_bytes());
require_active_session(root, &account_key, &input.session_id)?;
let path = session_path(root, &account_key, &input.session_id);
let _lock = lock_session(&path)?;
let mut record = read_session(&path)?;
authorize(
&record,
&account_key,
&input.session_id,
&input.resume_secret,
)?;
record.client_instance_id = input.client_instance_id;
record.observed_at_unix_ms = now_unix_ms()?;
write_record_atomic(&path, &record)?;
Ok(session_receipt("RESUMED", &record, None))
}
pub(crate) fn append_event_at(
root: &Path,
input: AppendSessionEventInput,
) -> Result<SessionEventReceipt, String> {
validate_identifier(&input.account_id, "ACCOUNT")?;
validate_identifier(&input.session_id, "SESSION")?;
validate_identifier(&input.idempotency_key, "IDEMPOTENCY")?;
validate_identifier(&input.event_kind, "EVENT_KIND")?;
validate_sha256(&input.payload_sha256)?;
validate_secret(&input.resume_secret, "RESUME_SECRET")?;
let account_key = sha256_hex(input.account_id.as_bytes());
require_active_session(root, &account_key, &input.session_id)?;
let path = session_path(root, &account_key, &input.session_id);
let _lock = lock_session(&path)?;
let mut record = read_session(&path)?;
authorize(
&record,
&account_key,
&input.session_id,
&input.resume_secret,
)?;
let events_root = path
.parent()
.ok_or("HOLOLAKE_DIRECT_SESSION_PATH_INVALID")?
.join("events");
fs::create_dir_all(&events_root)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
let idempotency_path = events_root.join(format!("idempotency-{}.json", input.idempotency_key));
if idempotency_path.exists() {
let existing: EventRecord = read_json(&idempotency_path, "EVENT")?;
if existing.event_kind == input.event_kind
&& existing.payload_sha256 == input.payload_sha256
{
return Ok(event_receipt("DUPLICATE_CONFIRMED", &existing));
}
return Err("HOLOLAKE_EVENT_IDEMPOTENCY_CONFLICT".into());
}
if input.expected_previous_sequence != record.last_event_sequence {
return Err("HOLOLAKE_EVENT_CURSOR_CONFLICT".into());
}
let observed_at_unix_ms = now_unix_ms()?;
let event = EventRecord {
schema: SESSION_SCHEMA.into(),
session_id: record.session_id.clone(),
sequence: record.last_event_sequence + 1,
idempotency_key: input.idempotency_key,
event_kind: input.event_kind,
payload_sha256: input.payload_sha256,
observed_at_unix_ms,
};
write_record_create_new(&idempotency_path, &event, "EVENT")?;
record.last_event_sequence = event.sequence;
record.observed_at_unix_ms = observed_at_unix_ms;
write_record_atomic(&path, &record)?;
Ok(event_receipt("APPENDED", &event))
}
pub(crate) fn authenticate_at(root: &Path, input: &AuthenticateSessionInput) -> Result<(), String> {
validate_identifier(&input.account_id, "ACCOUNT")?;
validate_identifier(&input.session_id, "SESSION")?;
validate_secret(&input.resume_secret, "RESUME_SECRET")?;
let account_key = sha256_hex(input.account_id.as_bytes());
require_active_session(root, &account_key, &input.session_id)?;
let path = session_path(root, &account_key, &input.session_id);
let _lock = lock_session(&path)?;
let record = read_session(&path)?;
authorize(
&record,
&account_key,
&input.session_id,
&input.resume_secret,
)
}
fn session_path(root: &Path, account_key: &str, session_id: &str) -> PathBuf {
root.join("accounts")
.join(account_key)
.join("sessions")
.join(session_id)
.join("session.json")
}
fn active_session_path(root: &Path, account_key: &str) -> PathBuf {
root.join("accounts")
.join(account_key)
.join("active-session.json")
}
fn require_active_session(root: &Path, account_key: &str, session_id: &str) -> Result<(), String> {
let active: ActiveSessionRecord =
read_json(&active_session_path(root, account_key), "ACTIVE_SESSION")?;
if active.schema != SESSION_SCHEMA
|| active.account_key != account_key
|| active.session_id != session_id
{
return Err("HOLOLAKE_SESSION_IS_NOT_ACCOUNT_ACTIVE_SESSION".into());
}
Ok(())
}
fn lock_account(root: &Path, account_key: &str) -> Result<std::fs::File, String> {
lock_file(
&root.join("accounts").join(account_key).join("account.lock"),
"ACCOUNT",
)
}
fn read_session(path: &Path) -> Result<SessionRecord, String> {
let record: SessionRecord = read_json(path, "SESSION")?;
if record.schema != SESSION_SCHEMA {
return Err("HOLOLAKE_DIRECT_SESSION_SCHEMA_UNSUPPORTED".into());
}
Ok(record)
}
fn lock_session(path: &Path) -> Result<std::fs::File, String> {
let parent = path
.parent()
.ok_or("HOLOLAKE_DIRECT_SESSION_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
lock_file(&parent.join("session.lock"), "SESSION")
}
fn lock_file(lock_path: &Path, kind: &str) -> Result<std::fs::File, String> {
let parent = lock_path
.parent()
.ok_or("HOLOLAKE_DIRECT_SESSION_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(lock_path)
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_LOCK_FAILED: {error}"))?;
file.lock_exclusive()
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_LOCK_FAILED: {error}"))?;
Ok(file)
}
fn authorize(
record: &SessionRecord,
account_key: &str,
session_id: &str,
secret: &str,
) -> Result<(), String> {
if record.account_key != account_key
|| record.session_id != session_id
|| record.resume_secret_sha256 != sha256_hex(secret.as_bytes())
{
return Err("HOLOLAKE_DIRECT_SESSION_NOT_AUTHORIZED".into());
}
Ok(())
}
fn write_record_atomic<T: Serialize>(path: &Path, record: &T) -> Result<(), String> {
let parent = path
.parent()
.ok_or("HOLOLAKE_DIRECT_SESSION_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_STORAGE_UNAVAILABLE: {error}"))?;
let temporary = parent.join(format!(".{}.tmp", Uuid::new_v4()));
write_bytes_create_new(&temporary, record, "SESSION")?;
fs::rename(&temporary, path)
.map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_WRITE_FAILED: {error}"))
}
fn write_record_create_new<T: Serialize>(
path: &Path,
record: &T,
kind: &str,
) -> Result<(), String> {
write_bytes_create_new(path, record, kind)
}
fn write_bytes_create_new<T: Serialize>(path: &Path, record: &T, kind: &str) -> Result<(), String> {
let bytes = serde_json::to_vec_pretty(record)
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_INVALID: {error}"))?;
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_WRITE_FAILED: {error}"))
}
fn read_json<T: for<'de> Deserialize<'de>>(path: &Path, kind: &str) -> Result<T, String> {
let bytes = fs::read(path).map_err(|error| {
if error.kind() == std::io::ErrorKind::NotFound {
format!("HOLOLAKE_DIRECT_{kind}_NOT_FOUND")
} else {
format!("HOLOLAKE_DIRECT_{kind}_UNREADABLE: {error}")
}
})?;
serde_json::from_slice(&bytes)
.map_err(|error| format!("HOLOLAKE_DIRECT_{kind}_INVALID: {error}"))
}
fn session_receipt(
state: &'static str,
record: &SessionRecord,
resume_secret: Option<String>,
) -> DirectSessionReceipt {
DirectSessionReceipt {
schema: SESSION_SCHEMA,
state,
account_key: record.account_key.clone(),
session_id: record.session_id.clone(),
lane_id: record.lane_id.clone(),
client_instance_id: record.client_instance_id.clone(),
opened_at_unix_ms: record.opened_at_unix_ms,
observed_at_unix_ms: record.observed_at_unix_ms,
last_event_sequence: record.last_event_sequence,
resume_secret,
receipt_id: sha256_hex(
format!(
"{state}\n{}\n{}\n{}\n{}",
record.account_key, record.session_id, record.lane_id, record.observed_at_unix_ms
)
.as_bytes(),
),
}
}
fn event_receipt(state: &'static str, event: &EventRecord) -> SessionEventReceipt {
SessionEventReceipt {
schema: SESSION_SCHEMA,
state,
session_id: event.session_id.clone(),
sequence: event.sequence,
idempotency_key: event.idempotency_key.clone(),
event_kind: event.event_kind.clone(),
payload_sha256: event.payload_sha256.clone(),
observed_at_unix_ms: event.observed_at_unix_ms,
receipt_id: sha256_hex(
format!(
"{}\n{}\n{}\n{}",
event.session_id, event.sequence, event.idempotency_key, event.payload_sha256
)
.as_bytes(),
),
}
}
fn validate_identifier(value: &str, kind: &str) -> Result<(), String> {
if value.is_empty()
|| value.len() > MAX_ID_BYTES
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b':'))
{
return Err(format!("HOLOLAKE_{kind}_ID_INVALID"));
}
Ok(())
}
fn validate_secret(value: &str, kind: &str) -> Result<(), String> {
if value.len() < 16 || value.len() > 512 {
return Err(format!("HOLOLAKE_{kind}_INVALID"));
}
Ok(())
}
fn validate_sha256(value: &str) -> Result<(), String> {
if value.len() != 64
|| !value
.bytes()
.all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
{
return Err("HOLOLAKE_PAYLOAD_SHA256_INVALID".into());
}
Ok(())
}
fn now_unix_ms() -> Result<u128, String> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis())
.map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}"))
}
fn sha256_hex(value: &[u8]) -> String {
digest(&SHA256, value)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn open_input(ticket: &str) -> OpenSessionInput {
OpenSessionInput {
account_id: "human-BS-0001".into(),
lane_id: "DEV-001".into(),
client_instance_id: "codex-1".into(),
discovery_ticket: ticket.into(),
}
}
fn issued_open_input(temp: &TempDir, suffix: &str) -> OpenSessionInput {
let issued = issue_ticket_at(
temp.path(),
IssueDiscoveryTicketInput {
account_id: "human-BS-0001".into(),
lane_id: "DEV-001".into(),
client_instance_id: "codex-1".into(),
},
)
.unwrap();
let mut input = open_input(&issued.discovery_ticket);
input.client_instance_id = format!("codex-{suffix}");
if suffix != "1" {
let issued = issue_ticket_at(
temp.path(),
IssueDiscoveryTicketInput {
account_id: input.account_id.clone(),
lane_id: input.lane_id.clone(),
client_instance_id: input.client_instance_id.clone(),
},
)
.unwrap();
input.discovery_ticket = issued.discovery_ticket;
}
input
}
#[test]
fn discovery_ticket_is_single_use_and_secret_is_not_stored_plaintext() {
let temp = TempDir::new().unwrap();
let input = issued_open_input(&temp, "1");
let ticket = input.discovery_ticket.clone();
let opened = open_at(temp.path(), input).unwrap();
assert_eq!(opened.state, "OPENED");
assert!(open_at(temp.path(), open_input(&ticket))
.unwrap_err()
.contains("ALREADY_USED"));
let stored = fs::read_to_string(session_path(
temp.path(),
&opened.account_key,
&opened.session_id,
))
.unwrap();
assert!(!stored.contains(opened.resume_secret.as_ref().unwrap()));
}
#[test]
fn ai_restart_resumes_the_same_session_and_cursor() {
let temp = TempDir::new().unwrap();
let opened = open_at(temp.path(), issued_open_input(&temp, "2")).unwrap();
let event = append_event_at(
temp.path(),
AppendSessionEventInput {
account_id: "human-BS-0001".into(),
session_id: opened.session_id.clone(),
resume_secret: opened.resume_secret.clone().unwrap(),
idempotency_key: "event-1".into(),
event_kind: "TASK_OBSERVED".into(),
payload_sha256: "a".repeat(64),
expected_previous_sequence: 0,
},
)
.unwrap();
assert_eq!(event.sequence, 1);
let resumed = resume_at(
temp.path(),
ResumeSessionInput {
account_id: "human-BS-0001".into(),
session_id: opened.session_id,
client_instance_id: "codex-2".into(),
resume_secret: opened.resume_secret.unwrap(),
},
)
.unwrap();
assert_eq!(resumed.state, "RESUMED");
assert_eq!(resumed.last_event_sequence, 1);
assert_eq!(resumed.client_instance_id, "codex-2");
}
#[test]
fn event_retries_are_idempotent_but_conflicts_fail_closed() {
let temp = TempDir::new().unwrap();
let opened = open_at(temp.path(), issued_open_input(&temp, "3")).unwrap();
let secret = opened.resume_secret.unwrap();
let input = AppendSessionEventInput {
account_id: "human-BS-0001".into(),
session_id: opened.session_id,
resume_secret: secret,
idempotency_key: "event-1".into(),
event_kind: "TASK_OBSERVED".into(),
payload_sha256: "b".repeat(64),
expected_previous_sequence: 0,
};
assert_eq!(
append_event_at(temp.path(), input.clone()).unwrap().state,
"APPENDED"
);
assert_eq!(
append_event_at(temp.path(), input.clone()).unwrap().state,
"DUPLICATE_CONFIRMED"
);
let conflicting = AppendSessionEventInput {
payload_sha256: "c".repeat(64),
..input
};
assert_eq!(
append_event_at(temp.path(), conflicting).unwrap_err(),
"HOLOLAKE_EVENT_IDEMPOTENCY_CONFLICT"
);
}
#[test]
fn wrong_secret_and_stale_cursor_fail_closed() {
let temp = TempDir::new().unwrap();
let opened = open_at(temp.path(), issued_open_input(&temp, "4")).unwrap();
let wrong = resume_at(
temp.path(),
ResumeSessionInput {
account_id: "human-BS-0001".into(),
session_id: opened.session_id.clone(),
client_instance_id: "codex-2".into(),
resume_secret: "wrong-resume-secret-0000".into(),
},
);
assert_eq!(wrong.unwrap_err(), "HOLOLAKE_DIRECT_SESSION_NOT_AUTHORIZED");
let stale = append_event_at(
temp.path(),
AppendSessionEventInput {
account_id: "human-BS-0001".into(),
session_id: opened.session_id,
resume_secret: opened.resume_secret.unwrap(),
idempotency_key: "event-2".into(),
event_kind: "TASK_OBSERVED".into(),
payload_sha256: "d".repeat(64),
expected_previous_sequence: 9,
},
);
assert_eq!(stale.unwrap_err(), "HOLOLAKE_EVENT_CURSOR_CONFLICT");
}
#[test]
fn caller_cannot_invent_a_discovery_ticket() {
let temp = TempDir::new().unwrap();
assert_eq!(
open_at(temp.path(), open_input("invented-ticket-0000")).unwrap_err(),
"HOLOLAKE_DIRECT_DISCOVERY_TICKET_NOT_FOUND"
);
}
#[test]
fn one_account_cannot_open_two_direct_write_sessions() {
let temp = TempDir::new().unwrap();
let first = open_at(temp.path(), issued_open_input(&temp, "1")).unwrap();
let second = open_at(temp.path(), issued_open_input(&temp, "2")).unwrap_err();
assert!(second.contains("ACCOUNT_ALREADY_HAS_ACTIVE_SESSION"));
assert!(second.contains(&first.session_id));
}
}

View file

@ -0,0 +1,690 @@
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
use fs2::FileExt;
use ring::signature::{UnparsedPublicKey, ED25519};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BTreeSet;
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use tauri::{AppHandle, Manager};
use uuid::Uuid;
const REGISTRY_SCHEMA: &str = "hololake.dynamic-node-registry/v1";
const HEALTH_SCHEMA: &str = "hololake.signed-node-health/v1";
const ROUTE_SCHEMA: &str = "hololake.capability-route-receipt/v1";
const MAX_IDENTIFIER_BYTES: usize = 128;
const MAX_CLOCK_SKEW_MS: u64 = 300_000;
const MAX_NODES: usize = 4096;
const MAX_BINDINGS_PER_NODE: usize = 256;
const EMBEDDED_ROUTING_TRUST: &str = include_str!("../dynamic-routing-trust.json");
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub(crate) struct DynamicNodeRegistry {
schema: String,
registry_id: String,
version: u64,
nodes: Vec<DynamicNodeRecord>,
signer_id: String,
signature: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct DynamicRoutingTrust {
schema: String,
state: RoutingTrustState,
signers: Vec<RoutingTrustSigner>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
enum RoutingTrustState {
UnprovisionedFailClosed,
Provisioned,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct RoutingTrustSigner {
signer_id: String,
public_key: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct DynamicNodeRecord {
node_id: String,
domain_id: String,
endpoint: String,
protocol_public_key: String,
human_ids: Vec<String>,
persona_ids: Vec<String>,
capabilities: Vec<String>,
priority: u32,
enabled: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub(crate) struct SignedNodeHealth {
schema: String,
registry_version: u64,
node_id: String,
observed_at_unix_ms: u64,
expires_at_unix_ms: u64,
sequence: u64,
status: HealthStatus,
capabilities: Vec<String>,
signature: String,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
enum HealthStatus {
Ready,
Degraded,
Offline,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResolveCapabilityRouteInput {
human_id: String,
persona_id: String,
domain_id: String,
capability_id: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CapabilityRouteReceipt {
schema: &'static str,
state: &'static str,
registry_id: String,
registry_version: u64,
node_id: String,
domain_id: String,
endpoint: String,
capability_id: String,
human_id: String,
persona_id: String,
health_sequence: u64,
health_expires_at_unix_ms: u64,
write_authority_granted: bool,
receipt_id: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct RegistryInstallReceipt {
schema: &'static str,
state: &'static str,
registry_id: String,
version: u64,
node_count: usize,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct HealthRecordReceipt {
schema: &'static str,
state: &'static str,
node_id: String,
sequence: u64,
expires_at_unix_ms: u64,
}
pub(crate) fn install_trusted_registry_at(
root: &Path,
registry: DynamicNodeRegistry,
) -> Result<RegistryInstallReceipt, String> {
let public_key = trusted_registry_key(EMBEDDED_ROUTING_TRUST, &registry.signer_id)?;
install_registry_at(root, registry, &public_key)
}
pub(crate) fn routing_root(app: &AppHandle) -> Result<PathBuf, String> {
let root = app
.path()
.app_data_dir()
.map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?
.join("dynamic-capability-routing-v1");
fs::create_dir_all(&root)
.map_err(|error| format!("HOLOLAKE_DYNAMIC_ROUTING_STORAGE_UNAVAILABLE: {error}"))?;
Ok(root)
}
fn install_registry_at(
root: &Path,
registry: DynamicNodeRegistry,
trusted_public_key: &str,
) -> Result<RegistryInstallReceipt, String> {
let _lock = lock_file(&root.join("registry.lock"), "REGISTRY")?;
validate_registry(&registry)?;
verify_signature(
trusted_public_key,
&registry.signature,
canonical_registry_bytes(&registry).as_bytes(),
"REGISTRY",
)?;
let path = root.join("registry.json");
if path.exists() {
let current: DynamicNodeRegistry = read_json(&path, "REGISTRY")?;
if registry.registry_id != current.registry_id || registry.version <= current.version {
return Err("HOLOLAKE_DYNAMIC_REGISTRY_VERSION_NOT_INCREASING".into());
}
}
write_json_atomic(&path, &registry, "REGISTRY")?;
Ok(RegistryInstallReceipt {
schema: REGISTRY_SCHEMA,
state: "INSTALLED",
registry_id: registry.registry_id,
version: registry.version,
node_count: registry.nodes.len(),
})
}
pub(crate) fn record_health_at(
root: &Path,
health: SignedNodeHealth,
) -> Result<HealthRecordReceipt, String> {
validate_identifier(&health.node_id, "NODE")?;
let _lock = lock_file(
&root.join("health").join(format!("{}.lock", health.node_id)),
"NODE_HEALTH",
)?;
validate_capabilities(&health.capabilities)?;
if health.schema != HEALTH_SCHEMA || health.expires_at_unix_ms <= health.observed_at_unix_ms {
return Err("HOLOLAKE_NODE_HEALTH_INVALID".into());
}
let registry: DynamicNodeRegistry = read_json(&root.join("registry.json"), "REGISTRY")?;
if health.registry_version != registry.version {
return Err("HOLOLAKE_NODE_HEALTH_REGISTRY_VERSION_MISMATCH".into());
}
let node = registry
.nodes
.iter()
.find(|node| node.node_id == health.node_id)
.ok_or("HOLOLAKE_NODE_NOT_REGISTERED")?;
verify_signature(
&node.protocol_public_key,
&health.signature,
canonical_health_bytes(&health).as_bytes(),
"NODE_HEALTH",
)?;
let path = root.join("health").join(format!("{}.json", health.node_id));
if path.exists() {
let current: SignedNodeHealth = read_json(&path, "NODE_HEALTH")?;
if health.sequence <= current.sequence {
return Err("HOLOLAKE_NODE_HEALTH_REPLAYED".into());
}
}
write_json_atomic(&path, &health, "NODE_HEALTH")?;
Ok(HealthRecordReceipt {
schema: HEALTH_SCHEMA,
state: "RECORDED",
node_id: health.node_id,
sequence: health.sequence,
expires_at_unix_ms: health.expires_at_unix_ms,
})
}
pub(crate) fn resolve_at(
root: &Path,
input: ResolveCapabilityRouteInput,
now: u64,
) -> Result<CapabilityRouteReceipt, String> {
validate_identifier(&input.human_id, "HUMAN")?;
validate_identifier(&input.persona_id, "PERSONA")?;
validate_identifier(&input.domain_id, "DOMAIN")?;
validate_identifier(&input.capability_id, "CAPABILITY")?;
let registry: DynamicNodeRegistry = read_json(&root.join("registry.json"), "REGISTRY")?;
let mut eligible = Vec::new();
for node in registry.nodes.iter().filter(|node| {
node.enabled
&& node.domain_id == input.domain_id
&& node.human_ids.contains(&input.human_id)
&& node.persona_ids.contains(&input.persona_id)
&& node.capabilities.contains(&input.capability_id)
}) {
let path = root.join("health").join(format!("{}.json", node.node_id));
let Ok(health) = read_json::<SignedNodeHealth>(&path, "NODE_HEALTH") else {
continue;
};
if health.registry_version != registry.version
|| health.status != HealthStatus::Ready
|| health.observed_at_unix_ms > now.saturating_add(MAX_CLOCK_SKEW_MS)
|| health.expires_at_unix_ms < now
|| !health.capabilities.contains(&input.capability_id)
|| verify_signature(
&node.protocol_public_key,
&health.signature,
canonical_health_bytes(&health).as_bytes(),
"NODE_HEALTH",
)
.is_err()
{
continue;
}
eligible.push((node, health));
}
eligible.sort_by(
|(left, _), (right, _)| match left.priority.cmp(&right.priority) {
Ordering::Equal => left.node_id.cmp(&right.node_id),
ordering => ordering,
},
);
let (node, health) = eligible
.into_iter()
.next()
.ok_or("HOLOLAKE_NO_HEALTHY_CAPABILITY_ROUTE")?;
let receipt_id = sha256_hex(
format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
registry.registry_id,
registry.version,
node.node_id,
input.human_id,
input.persona_id,
input.domain_id,
input.capability_id,
health.sequence
)
.as_bytes(),
);
Ok(CapabilityRouteReceipt {
schema: ROUTE_SCHEMA,
state: "ROUTE_SELECTED",
registry_id: registry.registry_id,
registry_version: registry.version,
node_id: node.node_id.clone(),
domain_id: node.domain_id.clone(),
endpoint: node.endpoint.clone(),
capability_id: input.capability_id,
human_id: input.human_id,
persona_id: input.persona_id,
health_sequence: health.sequence,
health_expires_at_unix_ms: health.expires_at_unix_ms,
write_authority_granted: false,
receipt_id,
})
}
fn validate_registry(registry: &DynamicNodeRegistry) -> Result<(), String> {
if registry.schema != REGISTRY_SCHEMA
|| registry.version == 0
|| registry.nodes.is_empty()
|| registry.nodes.len() > MAX_NODES
{
return Err("HOLOLAKE_DYNAMIC_REGISTRY_INVALID".into());
}
validate_identifier(&registry.registry_id, "REGISTRY")?;
validate_identifier(&registry.signer_id, "SIGNER")?;
let mut node_ids = BTreeSet::new();
for node in &registry.nodes {
validate_identifier(&node.node_id, "NODE")?;
validate_identifier(&node.domain_id, "DOMAIN")?;
validate_identifiers(&node.human_ids, "HUMAN")?;
validate_identifiers(&node.persona_ids, "PERSONA")?;
validate_capabilities(&node.capabilities)?;
if !node_ids.insert(node.node_id.as_str()) {
return Err("HOLOLAKE_DYNAMIC_REGISTRY_DUPLICATE_NODE".into());
}
let endpoint = tauri::Url::parse(&node.endpoint)
.map_err(|error| format!("HOLOLAKE_NODE_ENDPOINT_INVALID: {error}"))?;
if endpoint.scheme() != "https"
|| endpoint.host_str().is_none()
|| !endpoint.username().is_empty()
|| endpoint.password().is_some()
|| endpoint.query().is_some()
|| endpoint.fragment().is_some()
{
return Err("HOLOLAKE_NODE_ENDPOINT_MUST_USE_HTTPS".into());
}
decode_exact(&node.protocol_public_key, 32, "NODE_PROTOCOL_PUBLIC_KEY")?;
}
Ok(())
}
fn validate_capabilities(values: &[String]) -> Result<(), String> {
validate_identifiers(values, "CAPABILITY")
}
fn validate_identifiers(values: &[String], kind: &str) -> Result<(), String> {
if values.is_empty() || values.len() > MAX_BINDINGS_PER_NODE {
return Err(format!("HOLOLAKE_{kind}_BINDING_REQUIRED"));
}
let mut unique = BTreeSet::new();
for value in values {
validate_identifier(value, kind)?;
if !unique.insert(value.as_str()) {
return Err(format!("HOLOLAKE_{kind}_BINDING_DUPLICATE"));
}
}
Ok(())
}
fn validate_identifier(value: &str, kind: &str) -> Result<(), String> {
if value.is_empty()
|| value.len() > MAX_IDENTIFIER_BYTES
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b':'))
{
return Err(format!("HOLOLAKE_{kind}_ID_INVALID"));
}
Ok(())
}
fn canonical_registry_bytes(registry: &DynamicNodeRegistry) -> String {
let mut lines = vec![
registry.schema.clone(),
registry.registry_id.clone(),
registry.version.to_string(),
registry.signer_id.clone(),
];
for node in &registry.nodes {
lines.extend([
node.node_id.clone(),
node.domain_id.clone(),
node.endpoint.clone(),
node.protocol_public_key.clone(),
node.human_ids.join(","),
node.persona_ids.join(","),
node.capabilities.join(","),
node.priority.to_string(),
node.enabled.to_string(),
]);
}
lines.join("\n")
}
fn canonical_health_bytes(health: &SignedNodeHealth) -> String {
[
health.schema.clone(),
health.registry_version.to_string(),
health.node_id.clone(),
health.observed_at_unix_ms.to_string(),
health.expires_at_unix_ms.to_string(),
health.sequence.to_string(),
match health.status {
HealthStatus::Ready => "READY",
HealthStatus::Degraded => "DEGRADED",
HealthStatus::Offline => "OFFLINE",
}
.into(),
health.capabilities.join(","),
]
.join("\n")
}
fn verify_signature(
public_key: &str,
signature: &str,
bytes: &[u8],
kind: &str,
) -> Result<(), String> {
let public_key = decode_exact(public_key, 32, &format!("{kind}_PUBLIC_KEY"))?;
let signature = decode_exact(signature, 64, &format!("{kind}_SIGNATURE"))?;
UnparsedPublicKey::new(&ED25519, public_key)
.verify(bytes, &signature)
.map_err(|_| format!("HOLOLAKE_{kind}_SIGNATURE_INVALID"))
}
fn trusted_registry_key(raw: &str, signer_id: &str) -> Result<String, String> {
let trust: DynamicRoutingTrust = serde_json::from_str(raw)
.map_err(|error| format!("HOLOLAKE_DYNAMIC_ROUTING_TRUST_INVALID: {error}"))?;
if trust.schema != "hololake.dynamic-routing-trust/v1" {
return Err("HOLOLAKE_DYNAMIC_ROUTING_TRUST_SCHEMA_UNSUPPORTED".into());
}
match trust.state {
RoutingTrustState::UnprovisionedFailClosed => {
if !trust.signers.is_empty() {
return Err("HOLOLAKE_DYNAMIC_ROUTING_TRUST_INVALID".into());
}
Err("HOLOLAKE_DYNAMIC_ROUTING_TRUST_UNPROVISIONED".into())
}
RoutingTrustState::Provisioned => {
if trust.signers.is_empty() {
return Err("HOLOLAKE_DYNAMIC_ROUTING_TRUST_INVALID".into());
}
let mut ids = BTreeSet::new();
for signer in trust.signers {
validate_identifier(&signer.signer_id, "SIGNER")?;
decode_exact(&signer.public_key, 32, "REGISTRY_PUBLIC_KEY")?;
if !ids.insert(signer.signer_id.clone()) {
return Err("HOLOLAKE_DYNAMIC_ROUTING_TRUST_DUPLICATE_SIGNER".into());
}
if signer.signer_id == signer_id {
return Ok(signer.public_key);
}
}
Err("HOLOLAKE_DYNAMIC_ROUTING_SIGNER_NOT_TRUSTED".into())
}
}
}
fn decode_exact(value: &str, expected: usize, kind: &str) -> Result<Vec<u8>, String> {
let decoded = BASE64
.decode(value)
.map_err(|_| format!("HOLOLAKE_{kind}_INVALID"))?;
if decoded.len() != expected {
return Err(format!("HOLOLAKE_{kind}_INVALID"));
}
Ok(decoded)
}
fn write_json_atomic<T: Serialize>(path: &Path, value: &T, kind: &str) -> Result<(), String> {
let parent = path
.parent()
.ok_or("HOLOLAKE_DYNAMIC_ROUTING_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DYNAMIC_ROUTING_STORAGE_UNAVAILABLE: {error}"))?;
let temporary = parent.join(format!(".{}.tmp", Uuid::new_v4()));
let bytes = serde_json::to_vec_pretty(value)
.map_err(|error| format!("HOLOLAKE_{kind}_INVALID: {error}"))?;
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&temporary)
.map_err(|error| format!("HOLOLAKE_{kind}_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("HOLOLAKE_{kind}_WRITE_FAILED: {error}"))?;
fs::rename(&temporary, path).map_err(|error| format!("HOLOLAKE_{kind}_WRITE_FAILED: {error}"))
}
fn read_json<T: for<'de> Deserialize<'de>>(path: &Path, kind: &str) -> Result<T, String> {
let bytes = fs::read(path).map_err(|error| {
if error.kind() == std::io::ErrorKind::NotFound {
format!("HOLOLAKE_{kind}_NOT_FOUND")
} else {
format!("HOLOLAKE_{kind}_UNREADABLE: {error}")
}
})?;
serde_json::from_slice(&bytes).map_err(|error| format!("HOLOLAKE_{kind}_INVALID: {error}"))
}
fn lock_file(path: &Path, kind: &str) -> Result<std::fs::File, String> {
let parent = path
.parent()
.ok_or("HOLOLAKE_DYNAMIC_ROUTING_PATH_INVALID")?;
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_DYNAMIC_ROUTING_STORAGE_UNAVAILABLE: {error}"))?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(path)
.map_err(|error| format!("HOLOLAKE_{kind}_LOCK_FAILED: {error}"))?;
file.lock_exclusive()
.map_err(|error| format!("HOLOLAKE_{kind}_LOCK_FAILED: {error}"))?;
Ok(file)
}
fn sha256_hex(value: &[u8]) -> String {
ring::digest::digest(&ring::digest::SHA256, value)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, KeyPair};
use tempfile::TempDir;
fn key_pair() -> Ed25519KeyPair {
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap()
}
fn node(node_id: &str, key: &Ed25519KeyPair, priority: u32) -> DynamicNodeRecord {
DynamicNodeRecord {
node_id: node_id.into(),
domain_id: "DOM-FIFTH-0001".into(),
endpoint: format!("https://{node_id}.example.test/hololake"),
protocol_public_key: BASE64.encode(key.public_key().as_ref()),
human_ids: vec!["human-BS-0001".into()],
persona_ids: vec!["ICE-P-ZY001".into()],
capabilities: vec!["GH-PNCC-READ".into()],
priority,
enabled: true,
}
}
fn registry(nodes: Vec<DynamicNodeRecord>, signer: &Ed25519KeyPair) -> DynamicNodeRegistry {
let mut registry = DynamicNodeRegistry {
schema: REGISTRY_SCHEMA.into(),
registry_id: "registry-1".into(),
version: 1,
nodes,
signer_id: "jd-controller-release-registry".into(),
signature: String::new(),
};
registry.signature = BASE64.encode(
signer
.sign(canonical_registry_bytes(&registry).as_bytes())
.as_ref(),
);
registry
}
fn health(node_id: &str, signer: &Ed25519KeyPair, now: u64) -> SignedNodeHealth {
let mut health = SignedNodeHealth {
schema: HEALTH_SCHEMA.into(),
registry_version: 1,
node_id: node_id.into(),
observed_at_unix_ms: now,
expires_at_unix_ms: now + 60_000,
sequence: 1,
status: HealthStatus::Ready,
capabilities: vec!["GH-PNCC-READ".into()],
signature: String::new(),
};
health.signature = BASE64.encode(
signer
.sign(canonical_health_bytes(&health).as_bytes())
.as_ref(),
);
health
}
fn input() -> ResolveCapabilityRouteInput {
ResolveCapabilityRouteInput {
human_id: "human-BS-0001".into(),
persona_id: "ICE-P-ZY001".into(),
domain_id: "DOM-FIFTH-0001".into(),
capability_id: "GH-PNCC-READ".into(),
}
}
#[test]
fn installs_a_signed_dynamic_registry_and_selects_by_priority() {
let temp = TempDir::new().unwrap();
let registry_signer = key_pair();
let node_a = key_pair();
let node_b = key_pair();
let registry = registry(
vec![node("node-b", &node_b, 20), node("node-a", &node_a, 10)],
&registry_signer,
);
install_registry_at(
temp.path(),
registry,
&BASE64.encode(registry_signer.public_key().as_ref()),
)
.unwrap();
let now = 1_000_000;
record_health_at(temp.path(), health("node-a", &node_a, now)).unwrap();
record_health_at(temp.path(), health("node-b", &node_b, now)).unwrap();
let route = resolve_at(temp.path(), input(), now + 1).unwrap();
assert_eq!(route.node_id, "node-a");
assert!(!route.write_authority_granted);
}
#[test]
fn rejects_unsigned_registry_and_replayed_health() {
let temp = TempDir::new().unwrap();
let registry_signer = key_pair();
let node_key = key_pair();
let mut invalid = registry(vec![node("node-a", &node_key, 10)], &registry_signer);
invalid.signature = BASE64.encode([0_u8; 64]);
assert!(install_registry_at(
temp.path(),
invalid,
&BASE64.encode(registry_signer.public_key().as_ref())
)
.is_err());
install_registry_at(
temp.path(),
registry(vec![node("node-a", &node_key, 10)], &registry_signer),
&BASE64.encode(registry_signer.public_key().as_ref()),
)
.unwrap();
let current = health("node-a", &node_key, 1_000_000);
record_health_at(temp.path(), current.clone()).unwrap();
assert_eq!(
record_health_at(temp.path(), current).unwrap_err(),
"HOLOLAKE_NODE_HEALTH_REPLAYED"
);
}
#[test]
fn stale_unsigned_or_wrong_binding_nodes_are_not_routable() {
let temp = TempDir::new().unwrap();
let registry_signer = key_pair();
let node_key = key_pair();
install_registry_at(
temp.path(),
registry(vec![node("node-a", &node_key, 10)], &registry_signer),
&BASE64.encode(registry_signer.public_key().as_ref()),
)
.unwrap();
record_health_at(temp.path(), health("node-a", &node_key, 1_000_000)).unwrap();
assert_eq!(
resolve_at(temp.path(), input(), 2_000_000).unwrap_err(),
"HOLOLAKE_NO_HEALTHY_CAPABILITY_ROUTE"
);
let mut wrong_persona = input();
wrong_persona.persona_id = "ICE-P-OTHER".into();
assert_eq!(
resolve_at(temp.path(), wrong_persona, 1_000_001).unwrap_err(),
"HOLOLAKE_NO_HEALTHY_CAPABILITY_ROUTE"
);
}
#[test]
fn embedded_unprovisioned_trust_fails_closed() {
assert_eq!(
trusted_registry_key(EMBEDDED_ROUTING_TRUST, "any-signer").unwrap_err(),
"HOLOLAKE_DYNAMIC_ROUTING_TRUST_UNPROVISIONED"
);
}
}

View file

@ -0,0 +1,46 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::direct_local_broker::DirectLocalBrokerHandle;
use crate::direct_local_session::{active_session_count_at, direct_session_root};
use crate::pncc_receipt_projection::{pncc_projection_root, projection_event_count_at};
use crate::pncc_repository_binding::{mounted_repository_count_at, pncc_repository_mount_root};
use crate::release_trust::release_trust_state;
use serde::Serialize;
use tauri::{AppHandle, State};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoloLakeHomeStatus {
pub schema: &'static str,
pub direct_local_broker_state: &'static str,
pub direct_connection_count: usize,
pub resumable_session_count: usize,
pub code_repository_mount_count: usize,
pub pncc_receipt_count: usize,
pub update_state: &'static str,
pub release_recovery_state: String,
pub automatic_upstream_updates: bool,
pub mcp_role: &'static str,
}
#[tauri::command]
pub fn get_hololake_home_status(
app: AppHandle,
broker: State<'_, DirectLocalBrokerHandle>,
) -> Result<HoloLakeHomeStatus, String> {
let session_root = direct_session_root(&app)?;
let mount_root = pncc_repository_mount_root(&app)?;
let projection_root = pncc_projection_root(&app)?;
Ok(HoloLakeHomeStatus {
schema: "hololake.home-status/v1",
direct_local_broker_state: "READY",
direct_connection_count: broker.active_connection_count(),
resumable_session_count: active_session_count_at(&session_root)?,
code_repository_mount_count: mounted_repository_count_at(&mount_root)?,
pncc_receipt_count: projection_event_count_at(&projection_root)?,
update_state: release_trust_state()?,
release_recovery_state: crate::release_update::release_recovery_state(&app)?,
automatic_upstream_updates: false,
mcp_role: "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY",
})
}

View file

@ -1,16 +1,50 @@
mod direct_local_broker;
mod direct_local_session;
mod dynamic_capability_routing;
mod home_status;
mod local_development_bridge;
mod pncc_receipt_projection;
mod pncc_remote_git;
mod pncc_repository_binding;
mod release_trust;
mod release_update;
use tauri::Manager;
pub fn run_connector() -> Result<(), String> {
direct_local_broker::run_connector()
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![
home_status::get_hololake_home_status,
release_update::check_hololake_update,
release_update::confirm_hololake_update_install,
release_update::get_hololake_release_recovery_status,
release_update::confirm_hololake_release_health,
release_update::rollback_hololake_update,
direct_local_session::issue_direct_local_discovery_ticket,
direct_local_session::open_direct_local_session,
direct_local_session::resume_direct_local_session,
direct_local_session::append_direct_local_session_event,
local_development_bridge::acquire_development_write_lane,
local_development_bridge::inspect_development_write_lane,
local_development_bridge::release_development_write_lane,
pncc_repository_binding::inspect_mounted_pncc_repository,
pncc_repository_binding::select_pncc_repository_candidate,
pncc_repository_binding::confirm_pncc_repository_mount,
pncc_receipt_projection::query_pncc_receipt_projection,
])
.setup(|app| {
let broker = direct_local_broker::start(app.handle())?;
app.manage(broker);
release_trust::install_updater_if_provisioned(app.handle())?;
if let Err(error) = release_update::observe_release_startup(app.handle()) {
eprintln!("HoloLake update recovery requires maintenance: {error}");
}
Ok(())
})
.run(tauri::generate_context!())

View file

@ -1,5 +1,12 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
if std::env::args().any(|argument| argument == "--connector") {
if let Err(error) = hololake_native_desktop_lib::run_connector() {
eprintln!("{error}");
std::process::exit(1);
}
return;
}
hololake_native_desktop_lib::run();
}

View file

@ -0,0 +1,481 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::pncc_remote_git::PnccRemoteObjectReceipt;
use crate::pncc_repository_binding::PnccRepositoryMountReceipt;
use fs2::FileExt;
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::time::{SystemTime, UNIX_EPOCH};
use tauri::{AppHandle, Manager};
use uuid::Uuid;
const JOURNAL_SCHEMA: &str = "hololake.pncc-stage-one-receipt-projection-journal/v1";
const EVENT_SCHEMA: &str = "hololake.pncc-stage-one-receipt-projection-event/v1";
const MAX_EVENTS: usize = 4096;
const MAX_QUERY_LIMIT: usize = 100;
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct QueryPnccReceiptProjectionInput {
pub after_sequence: Option<u64>,
pub limit: Option<usize>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct ProjectionJournal {
schema: String,
events: Vec<PnccReceiptProjectionEvent>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PnccReceiptProjectionEvent {
pub schema: String,
pub sequence: u64,
pub kind: String,
pub mount_id: String,
pub persona_id: Option<String>,
pub human_responsibility_subject: Option<String>,
pub git_head: String,
pub relative_path: Option<String>,
pub object_sha256: String,
pub source_receipt_id: String,
pub observed_at_unix_ms: u128,
pub previous_event_hash: String,
pub event_hash: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PnccReceiptProjectionReceipt {
pub schema: &'static str,
pub state: &'static str,
pub authority_store: bool,
pub write_authority: bool,
pub model_instance_fields_present: bool,
pub empty_means_offline: bool,
pub after_sequence: u64,
pub returned_event_count: usize,
pub last_sequence: u64,
pub has_more: bool,
pub events: Vec<PnccReceiptProjectionEvent>,
pub receipt_id: String,
}
#[derive(Debug)]
struct ProjectionInput {
kind: &'static str,
mount_id: String,
persona_id: Option<String>,
human_responsibility_subject: Option<String>,
git_head: String,
relative_path: Option<String>,
object_sha256: String,
source_receipt_id: String,
}
#[tauri::command]
pub async fn query_pncc_receipt_projection(
app: AppHandle,
input: QueryPnccReceiptProjectionInput,
) -> Result<PnccReceiptProjectionReceipt, String> {
let root = pncc_projection_root(&app)?;
tauri::async_runtime::spawn_blocking(move || query_at(&root, input))
.await
.map_err(|error| format!("PNCC_PROJECTION_JOIN_FAILED: {error}"))?
}
pub(crate) fn pncc_projection_root(app: &AppHandle) -> Result<PathBuf, String> {
let root = app
.path()
.app_data_dir()
.map_err(|error| format!("PNCC_APP_DATA_UNAVAILABLE: {error}"))?
.join("pncc-stage-one-v1")
.join("receipt-projection");
fs::create_dir_all(&root)
.map_err(|error| format!("PNCC_PROJECTION_STORAGE_UNAVAILABLE: {error}"))?;
root.canonicalize()
.map_err(|error| format!("PNCC_PROJECTION_STORAGE_UNAVAILABLE: {error}"))
}
pub(crate) fn projection_event_count_at(root: &Path) -> Result<usize, String> {
let _lock = lock_projection(root)?;
Ok(load_and_verify(root)?.events.len())
}
pub(crate) fn append_repository_binding_at(
root: &Path,
receipt: &PnccRepositoryMountReceipt,
) -> Result<PnccReceiptProjectionEvent, String> {
append_at(
root,
ProjectionInput {
kind: "REPOSITORY_BINDING_REVALIDATED",
mount_id: receipt.mount_id.clone(),
persona_id: Some(receipt.binding.persona_id.clone()),
human_responsibility_subject: Some(
receipt.binding.human_responsibility_subject.clone(),
),
git_head: receipt.binding.git_head.clone(),
relative_path: Some(receipt.binding.manifest.relative_path.clone()),
object_sha256: receipt.binding.manifest.sha256.clone(),
source_receipt_id: receipt.binding.receipt_id.clone(),
},
)
}
pub(crate) fn append_remote_read_at(
root: &Path,
receipt: &PnccRemoteObjectReceipt,
) -> Result<PnccReceiptProjectionEvent, String> {
append_at(
root,
ProjectionInput {
kind: "REMOTE_OBJECT_READ_VERIFIED",
mount_id: receipt.mount_id.clone(),
persona_id: None,
human_responsibility_subject: None,
git_head: receipt.remote_head.clone(),
relative_path: Some(receipt.relative_path.clone()),
object_sha256: receipt.content_sha256.clone(),
source_receipt_id: receipt.receipt_id.clone(),
},
)
}
fn append_at(root: &Path, input: ProjectionInput) -> Result<PnccReceiptProjectionEvent, String> {
let _lock = lock_projection(root)?;
let mut journal = load_and_verify(root)?;
if let Some(existing) = journal
.events
.iter()
.find(|event| event.source_receipt_id == input.source_receipt_id)
{
return Ok(existing.clone());
}
if journal.events.len() >= MAX_EVENTS {
return Err("PNCC_PROJECTION_CAPACITY_REACHED".into());
}
validate_sha256(&input.object_sha256)?;
validate_sha256(&input.source_receipt_id)?;
validate_head(&input.git_head)?;
let sequence = journal.events.len() as u64 + 1;
let observed_at_unix_ms = now_unix_ms()?;
let previous_event_hash = journal
.events
.last()
.map(|event| event.event_hash.clone())
.unwrap_or_else(|| "0".repeat(64));
let event_hash = hash_event(
sequence,
input.kind,
&input.mount_id,
input.persona_id.as_deref(),
input.human_responsibility_subject.as_deref(),
&input.git_head,
input.relative_path.as_deref(),
&input.object_sha256,
&input.source_receipt_id,
observed_at_unix_ms,
&previous_event_hash,
);
let event = PnccReceiptProjectionEvent {
schema: EVENT_SCHEMA.into(),
sequence,
kind: input.kind.into(),
mount_id: input.mount_id,
persona_id: input.persona_id,
human_responsibility_subject: input.human_responsibility_subject,
git_head: input.git_head,
relative_path: input.relative_path,
object_sha256: input.object_sha256,
source_receipt_id: input.source_receipt_id,
observed_at_unix_ms,
previous_event_hash,
event_hash,
};
journal.events.push(event.clone());
write_journal(root, &journal)?;
Ok(event)
}
pub(crate) fn query_at(
root: &Path,
input: QueryPnccReceiptProjectionInput,
) -> Result<PnccReceiptProjectionReceipt, String> {
let _lock = lock_projection(root)?;
let journal = load_and_verify(root)?;
let after_sequence = input.after_sequence.unwrap_or(0);
let limit = input.limit.unwrap_or(25);
if limit == 0 || limit > MAX_QUERY_LIMIT {
return Err("PNCC_PROJECTION_QUERY_LIMIT_INVALID".into());
}
let last_sequence = journal.events.len() as u64;
if after_sequence > last_sequence {
return Err("PNCC_PROJECTION_CURSOR_INVALID".into());
}
let mut available = journal
.events
.iter()
.filter(|event| event.sequence > after_sequence);
let events = available.by_ref().take(limit).cloned().collect::<Vec<_>>();
let has_more = available.next().is_some();
let receipt_id = sha256_hex(
format!(
"{}\n{}\n{}\n{}",
after_sequence,
events.len(),
last_sequence,
events
.last()
.map(|event| event.event_hash.as_str())
.unwrap_or("EMPTY")
)
.as_bytes(),
);
Ok(PnccReceiptProjectionReceipt {
schema: "hololake.pncc-stage-one-receipt-projection/v1",
state: "VERIFIED_READ_ONLY_PROJECTION",
authority_store: false,
write_authority: false,
model_instance_fields_present: false,
empty_means_offline: false,
after_sequence,
returned_event_count: events.len(),
last_sequence,
has_more,
events,
receipt_id,
})
}
fn load_and_verify(root: &Path) -> Result<ProjectionJournal, String> {
let path = journal_path(root);
if !path.exists() {
return Ok(ProjectionJournal {
schema: JOURNAL_SCHEMA.into(),
events: Vec::new(),
});
}
let metadata = fs::symlink_metadata(&path)
.map_err(|error| format!("PNCC_PROJECTION_READ_FAILED: {error}"))?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
return Err("PNCC_PROJECTION_JOURNAL_INVALID".into());
}
let journal: ProjectionJournal = serde_json::from_slice(
&fs::read(&path).map_err(|error| format!("PNCC_PROJECTION_READ_FAILED: {error}"))?,
)
.map_err(|error| format!("PNCC_PROJECTION_JOURNAL_INVALID: {error}"))?;
if journal.schema != JOURNAL_SCHEMA || journal.events.len() > MAX_EVENTS {
return Err("PNCC_PROJECTION_JOURNAL_INVALID".into());
}
let mut previous = "0".repeat(64);
for (index, event) in journal.events.iter().enumerate() {
if event.schema != EVENT_SCHEMA
|| event.sequence != index as u64 + 1
|| event.previous_event_hash != previous
|| event.event_hash
!= hash_event(
event.sequence,
&event.kind,
&event.mount_id,
event.persona_id.as_deref(),
event.human_responsibility_subject.as_deref(),
&event.git_head,
event.relative_path.as_deref(),
&event.object_sha256,
&event.source_receipt_id,
event.observed_at_unix_ms,
&event.previous_event_hash,
)
{
return Err("PNCC_PROJECTION_EVENT_CHAIN_INVALID".into());
}
previous = event.event_hash.clone();
}
Ok(journal)
}
#[allow(clippy::too_many_arguments)]
fn hash_event(
sequence: u64,
kind: &str,
mount_id: &str,
persona_id: Option<&str>,
human: Option<&str>,
git_head: &str,
relative_path: Option<&str>,
object_sha256: &str,
source_receipt_id: &str,
observed_at_unix_ms: u128,
previous: &str,
) -> String {
sha256_hex(
format!(
"{sequence}\n{kind}\n{mount_id}\n{}\n{}\n{git_head}\n{}\n{object_sha256}\n{source_receipt_id}\n{observed_at_unix_ms}\n{previous}",
persona_id.unwrap_or(""),
human.unwrap_or(""),
relative_path.unwrap_or("")
)
.as_bytes(),
)
}
fn lock_projection(root: &Path) -> Result<std::fs::File, String> {
fs::create_dir_all(root)
.map_err(|error| format!("PNCC_PROJECTION_STORAGE_UNAVAILABLE: {error}"))?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(root.join("projection.lock"))
.map_err(|error| format!("PNCC_PROJECTION_LOCK_FAILED: {error}"))?;
file.lock_exclusive()
.map_err(|error| format!("PNCC_PROJECTION_LOCK_FAILED: {error}"))?;
Ok(file)
}
fn write_journal(root: &Path, journal: &ProjectionJournal) -> Result<(), String> {
let temporary = root.join(format!(".journal-{}.tmp", Uuid::new_v4()));
let bytes = serde_json::to_vec_pretty(journal)
.map_err(|error| format!("PNCC_PROJECTION_JOURNAL_INVALID: {error}"))?;
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&temporary)
.map_err(|error| format!("PNCC_PROJECTION_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("PNCC_PROJECTION_WRITE_FAILED: {error}"))?;
fs::rename(&temporary, journal_path(root))
.map_err(|error| format!("PNCC_PROJECTION_WRITE_FAILED: {error}"))
}
fn journal_path(root: &Path) -> PathBuf {
root.join("journal.json")
}
fn validate_sha256(value: &str) -> Result<(), String> {
if value.len() == 64
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
{
Ok(())
} else {
Err("PNCC_PROJECTION_SHA256_INVALID".into())
}
}
fn validate_head(value: &str) -> Result<(), String> {
if value.len() == 40
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
{
Ok(())
} else {
Err("PNCC_PROJECTION_GIT_HEAD_INVALID".into())
}
}
fn now_unix_ms() -> Result<u128, String> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis())
.map_err(|error| format!("PNCC_SYSTEM_CLOCK_INVALID: {error}"))
}
fn sha256_hex(bytes: &[u8]) -> String {
digest(&SHA256, bytes)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pncc_remote_git::PnccRemoteObjectReceipt;
use tempfile::TempDir;
fn remote_receipt(id: char, head: char) -> PnccRemoteObjectReceipt {
PnccRemoteObjectReceipt {
schema: "hololake.pncc-stage-one-remote-object-read/v1",
mount_id: "persona-primary".into(),
remote_identity_hash: "a".repeat(64),
remote_head: head.to_string().repeat(40),
previous_verified_head: None,
cursor_advanced: true,
continuity_verified: true,
fetched_incremental_objects: true,
cache_rehydrated: false,
relative_path: "brain/CORE.hldp".into(),
git_object_id: "b".repeat(40),
content: "content".into(),
content_sha256: "c".repeat(64),
cache_bytes: 1,
cache_limit_bytes: 1024,
cache_state: "BOUNDED_PARTIAL_OBJECT_CACHE",
worktree_created: false,
full_history_requested: false,
model_inference_started: false,
reality_execution_allowed: false,
receipt_id: id.to_string().repeat(64),
}
}
#[test]
fn projection_is_hash_chained_bounded_and_never_an_authority_store() {
let temp = TempDir::new().unwrap();
append_remote_read_at(temp.path(), &remote_receipt('d', '1')).unwrap();
append_remote_read_at(temp.path(), &remote_receipt('e', '2')).unwrap();
let projection = query_at(
temp.path(),
QueryPnccReceiptProjectionInput {
after_sequence: Some(0),
limit: Some(1),
},
)
.unwrap();
assert!(!projection.authority_store);
assert!(!projection.write_authority);
assert!(!projection.model_instance_fields_present);
assert!(!projection.empty_means_offline);
assert_eq!(projection.returned_event_count, 1);
assert!(projection.has_more);
let source = serde_json::to_string(&projection.events).unwrap();
assert!(!source.to_ascii_lowercase().contains("modelinstance"));
}
#[test]
fn exact_retry_is_idempotent_and_tampering_is_rejected() {
let temp = TempDir::new().unwrap();
let receipt = remote_receipt('d', '1');
let first = append_remote_read_at(temp.path(), &receipt).unwrap();
let duplicate = append_remote_read_at(temp.path(), &receipt).unwrap();
assert_eq!(first.event_hash, duplicate.event_hash);
let path = journal_path(temp.path());
let mut journal: serde_json::Value =
serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
journal["events"][0]["kind"] = serde_json::json!("FORGED");
fs::write(&path, serde_json::to_vec_pretty(&journal).unwrap()).unwrap();
assert_eq!(
query_at(
temp.path(),
QueryPnccReceiptProjectionInput {
after_sequence: None,
limit: None,
}
)
.unwrap_err(),
"PNCC_PROJECTION_EVENT_CHAIN_INVALID"
);
}
}

View file

@ -0,0 +1,944 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Clean-room stage-one implementation. Contract evidence is recorded in
// audit/pncc-migration-provenance.json; no donor source was copied.
use fs2::FileExt;
use ring::digest::{digest, SHA256};
use serde::{Deserialize, Serialize};
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Component, Path, PathBuf};
use std::process::{Command, Output};
use uuid::Uuid;
const REMOTE_MOUNT_SCHEMA: &str = "hololake.pncc-stage-one-remote-mount/v1";
const CURSOR_SCHEMA: &str = "hololake.pncc-stage-one-remote-cursor/v1";
#[cfg(test)]
const DEFAULT_BRANCH: &str = "main";
const DEFAULT_CACHE_LIMIT_BYTES: u64 = 128 * 1024 * 1024;
const MIN_CACHE_LIMIT_BYTES: u64 = 1024 * 1024;
const MAX_CACHE_LIMIT_BYTES: u64 = 4 * 1024 * 1024 * 1024;
const MAX_TEXT_OBJECT_BYTES: usize = 2 * 1024 * 1024;
const MAX_CONTINUITY_DEPTH: usize = 4096;
#[cfg(test)]
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RegisterPnccRemoteMountInput {
pub mount_id: String,
pub remote_url: String,
pub branch: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ReadMountedPnccRemoteObjectInput {
pub mount_id: String,
pub relative_path: String,
pub expected_head: Option<String>,
pub max_cache_bytes: Option<u64>,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PnccRemoteObjectReceipt {
pub schema: &'static str,
pub mount_id: String,
pub remote_identity_hash: String,
pub remote_head: String,
pub previous_verified_head: Option<String>,
pub cursor_advanced: bool,
pub continuity_verified: bool,
pub fetched_incremental_objects: bool,
pub cache_rehydrated: bool,
pub relative_path: String,
pub git_object_id: String,
pub content: String,
pub content_sha256: String,
pub cache_bytes: u64,
pub cache_limit_bytes: u64,
pub cache_state: &'static str,
pub worktree_created: bool,
pub full_history_requested: bool,
pub model_inference_started: bool,
pub reality_execution_allowed: bool,
pub receipt_id: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct RemoteMountRecord {
schema: String,
mount_id: String,
remote_url: String,
remote_identity_hash: String,
branch: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct CursorRecord {
schema: String,
remote_identity_hash: String,
branch: String,
verified_head: String,
previous_verified_head: Option<String>,
generation: u64,
}
#[cfg(test)]
fn register_remote_mount_at(
root: &Path,
input: RegisterPnccRemoteMountInput,
allow_file_remote: bool,
) -> Result<(), String> {
validate_machine_id(&input.mount_id, "REMOTE_MOUNT_ID")?;
validate_remote_url(&input.remote_url, allow_file_remote)?;
let branch = input.branch.unwrap_or_else(|| DEFAULT_BRANCH.into());
validate_branch(&branch)?;
let identity = remote_identity(&input.remote_url, &branch);
let record = RemoteMountRecord {
schema: REMOTE_MOUNT_SCHEMA.into(),
mount_id: input.mount_id.clone(),
remote_url: input.remote_url,
remote_identity_hash: identity.clone(),
branch: branch.clone(),
};
let mounts = child_root(root, "mounts")?;
write_json_atomic(&mounts.join(format!("{}.json", input.mount_id)), &record)?;
Ok(())
}
pub(crate) fn read_mounted_remote_object_at(
root: &Path,
input: ReadMountedPnccRemoteObjectInput,
) -> Result<PnccRemoteObjectReceipt, String> {
read_mounted_remote_object_with_file_policy(root, input, false)
}
fn read_mounted_remote_object_with_file_policy(
root: &Path,
input: ReadMountedPnccRemoteObjectInput,
allow_file_remote: bool,
) -> Result<PnccRemoteObjectReceipt, String> {
validate_machine_id(&input.mount_id, "REMOTE_MOUNT_ID")?;
validate_relative_path(&input.relative_path)?;
let maximum = input.max_cache_bytes.unwrap_or(DEFAULT_CACHE_LIMIT_BYTES);
if !(MIN_CACHE_LIMIT_BYTES..=MAX_CACHE_LIMIT_BYTES).contains(&maximum) {
return Err("PNCC_REMOTE_CACHE_LIMIT_INVALID".into());
}
let expected_head = input
.expected_head
.map(|head| validate_head(&head))
.transpose()?;
let mounts = child_root(root, "mounts")?;
let record: RemoteMountRecord = read_json(&mounts.join(format!("{}.json", input.mount_id)))?;
if record.schema != REMOTE_MOUNT_SCHEMA || record.mount_id != input.mount_id {
return Err("PNCC_REMOTE_MOUNT_RECORD_INVALID".into());
}
validate_remote_url(&record.remote_url, allow_file_remote)?;
validate_branch(&record.branch)?;
let identity = remote_identity(&record.remote_url, &record.branch);
if identity != record.remote_identity_hash {
return Err("PNCC_REMOTE_MOUNT_IDENTITY_MISMATCH".into());
}
let locks = child_root(root, "locks")?;
let _lock = lock_file(&locks.join(format!("{identity}.lock")))?;
let remote_head = read_remote_head(&record.remote_url, &record.branch)?;
if expected_head
.as_deref()
.is_some_and(|head| head != remote_head)
{
return Err("PNCC_REMOTE_HEAD_MISMATCH".into());
}
let object_root = child_root(root, "objects")?;
let cursor_root = child_root(root, "continuity")?;
let cache_path = object_root.join(format!("{identity}.git"));
let cursor_path = cursor_root.join(format!("{identity}.json"));
let cursor = read_cursor(&cursor_path, &identity, &record.branch)?;
let previous = cursor.as_ref().map(|value| value.verified_head.clone());
ensure_bare_cache(&cache_path, &record.remote_url)?;
let remote_ref = format!("refs/remotes/origin/{}", record.branch);
let cached = optional_ref(&cache_path, &remote_ref)?;
match (previous.as_deref(), cached.as_deref()) {
(None, Some(_)) => return Err("PNCC_REMOTE_CACHE_WITHOUT_CONTINUITY_CURSOR".into()),
(Some(expected), Some(observed)) if expected != observed => {
return Err("PNCC_REMOTE_CACHE_CURSOR_MISMATCH".into())
}
_ => {}
}
let fetched_incremental_objects = cached.as_deref() != Some(remote_head.as_str());
let cache_rehydrated = cached.is_none() && previous.is_some();
if fetched_incremental_objects {
if let Err(error) = fetch_continuity(
&cache_path,
&record.branch,
previous.as_deref(),
cached.is_some(),
) {
if cache_path.exists() {
remove_exact_cache(&object_root, &cache_path)?;
}
return Err(error);
}
}
let fetched_head = required_ref(&cache_path, &remote_ref)?;
if fetched_head != remote_head {
return Err("PNCC_REMOTE_FETCH_HEAD_MISMATCH".into());
}
if let Some(previous_head) = previous.as_deref() {
if previous_head != remote_head && !is_ancestor(&cache_path, previous_head, &remote_head)? {
remove_exact_cache(&object_root, &cache_path)?;
return Err("PNCC_REMOTE_HISTORY_REWRITE_REJECTED".into());
}
}
let (object_id, content_bytes) = read_regular_blob(
&cache_path,
&remote_head,
&input.relative_path,
MAX_TEXT_OBJECT_BYTES,
)?;
let content =
String::from_utf8(content_bytes).map_err(|_| "PNCC_REMOTE_OBJECT_NOT_UTF8".to_string())?;
let content_sha256 = sha256_hex(content.as_bytes());
let cursor_advanced = previous.as_deref() != Some(remote_head.as_str());
let next = CursorRecord {
schema: CURSOR_SCHEMA.into(),
remote_identity_hash: identity.clone(),
branch: record.branch,
verified_head: remote_head.clone(),
previous_verified_head: previous.clone(),
generation: cursor
.as_ref()
.map(|value| value.generation.saturating_add(u64::from(cursor_advanced)))
.unwrap_or(1),
};
write_json_atomic(&cursor_path, &next)?;
let observed_cache_bytes = directory_size(&cache_path)?;
let (cache_bytes, cache_state) = if observed_cache_bytes > maximum {
remove_exact_cache(&object_root, &cache_path)?;
(0, "EVICTED_AFTER_BOUNDED_READ")
} else {
(observed_cache_bytes, "BOUNDED_PARTIAL_OBJECT_CACHE")
};
let receipt_id = sha256_hex(
format!(
"{}\n{}\n{}\n{}\n{}",
input.mount_id, remote_head, input.relative_path, object_id, content_sha256
)
.as_bytes(),
);
Ok(PnccRemoteObjectReceipt {
schema: "hololake.pncc-stage-one-remote-object-read/v1",
mount_id: input.mount_id,
remote_identity_hash: identity,
remote_head,
previous_verified_head: previous,
cursor_advanced,
continuity_verified: true,
fetched_incremental_objects,
cache_rehydrated,
relative_path: input.relative_path,
git_object_id: object_id,
content,
content_sha256,
cache_bytes,
cache_limit_bytes: maximum,
cache_state,
worktree_created: false,
full_history_requested: false,
model_inference_started: false,
reality_execution_allowed: false,
receipt_id,
})
}
fn validate_remote_url(remote_url: &str, allow_file_remote: bool) -> Result<(), String> {
if remote_url.len() > 4096 || remote_url.contains(['\n', '\r', '\0']) {
return Err("PNCC_REMOTE_URL_UNSUPPORTED".into());
}
if allow_file_remote && remote_url.starts_with("file://") {
return Ok(());
}
let url =
tauri::Url::parse(remote_url).map_err(|_| "PNCC_REMOTE_URL_UNSUPPORTED".to_string())?;
if url.scheme() != "https"
|| url.host_str().is_none()
|| !url.username().is_empty()
|| url.password().is_some()
|| url.query().is_some()
|| url.fragment().is_some()
{
return Err("PNCC_REMOTE_URL_MUST_BE_CREDENTIAL_FREE_HTTPS".into());
}
Ok(())
}
fn validate_branch(branch: &str) -> Result<(), String> {
if branch.is_empty() || branch.len() > 200 || branch.starts_with('-') {
return Err("PNCC_REMOTE_BRANCH_INVALID".into());
}
let output = trusted_git()
.args(["check-ref-format", "--branch", branch])
.output()
.map_err(|error| format!("PNCC_GIT_UNAVAILABLE: {error}"))?;
if output.status.success() {
Ok(())
} else {
Err("PNCC_REMOTE_BRANCH_INVALID".into())
}
}
fn validate_relative_path(relative: &str) -> Result<(), String> {
let path = Path::new(relative);
if relative.is_empty()
|| relative.len() > 1024
|| path.is_absolute()
|| path.components().any(|component| {
matches!(
component,
Component::ParentDir | Component::RootDir | Component::Prefix(_)
)
})
|| path
.components()
.any(|component| component.as_os_str() == ".git")
{
return Err("PNCC_REMOTE_PATH_INVALID".into());
}
Ok(())
}
fn validate_head(head: &str) -> Result<String, String> {
if head.len() == 40
&& head
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
{
Ok(head.into())
} else {
Err("PNCC_REMOTE_HEAD_INVALID".into())
}
}
fn validate_machine_id(value: &str, label: &str) -> Result<(), String> {
if value.is_empty()
|| value.len() > 128
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
{
return Err(format!("PNCC_{label}_INVALID"));
}
Ok(())
}
fn remote_identity(remote_url: &str, branch: &str) -> String {
sha256_hex(format!("{remote_url}\n{branch}").as_bytes())
}
fn read_remote_head(remote_url: &str, branch: &str) -> Result<String, String> {
let full_ref = format!("refs/heads/{branch}");
let output = trusted_git()
.env("GIT_TRACE_PACKET", "1")
.args([
"-c",
"protocol.version=2",
"ls-remote",
"--exit-code",
"--heads",
remote_url,
&full_ref,
])
.output()
.map_err(|error| format!("PNCC_REMOTE_HEAD_READ_FAILED: {error}"))?;
let output = require_success(output, "PNCC_REMOTE_HEAD_READ")?;
let trace = String::from_utf8_lossy(&output.stderr);
if !trace.lines().any(|line| {
line.split_whitespace().any(|word| word == "filter")
&& line
.split_whitespace()
.any(|word| word.starts_with("fetch="))
}) {
return Err("PNCC_REMOTE_PARTIAL_OBJECT_PROTOCOL_REQUIRED".into());
}
let response = String::from_utf8(output.stdout)
.map_err(|_| "PNCC_REMOTE_HEAD_RESPONSE_NOT_UTF8".to_string())?;
let mut lines = response.lines();
let first = lines
.next()
.ok_or_else(|| "PNCC_REMOTE_BRANCH_NOT_FOUND".to_string())?;
if lines.next().is_some() {
return Err("PNCC_REMOTE_HEAD_AMBIGUOUS".into());
}
let mut fields = first.split_whitespace();
let head = fields.next().unwrap_or_default().to_ascii_lowercase();
if fields.next() != Some(full_ref.as_str()) || fields.next().is_some() {
return Err("PNCC_REMOTE_HEAD_RESPONSE_INVALID".into());
}
validate_head(&head)
}
fn ensure_bare_cache(cache: &Path, remote_url: &str) -> Result<(), String> {
if cache.exists() {
if git_text(
cache,
&["rev-parse", "--is-bare-repository"],
"PNCC_CACHE_PROBE",
)?
.trim()
!= "true"
{
return Err("PNCC_REMOTE_CACHE_NOT_BARE".into());
}
if git_text(
cache,
&["remote", "get-url", "origin"],
"PNCC_CACHE_ORIGIN_READ",
)?
.trim()
!= remote_url
{
return Err("PNCC_REMOTE_CACHE_ORIGIN_MISMATCH".into());
}
return Ok(());
}
fs::create_dir(cache).map_err(|error| format!("PNCC_REMOTE_CACHE_CREATE_FAILED: {error}"))?;
git_status(
trusted_git().args(["init", "--bare", &cache.to_string_lossy()]),
"PNCC_REMOTE_CACHE_INIT",
)?;
git_status(
git_at(cache).args(["remote", "add", "origin", remote_url]),
"PNCC_REMOTE_CACHE_ORIGIN_ADD",
)?;
git_status(
git_at(cache).args(["config", "remote.origin.promisor", "true"]),
"PNCC_REMOTE_CACHE_PROMISOR_CONFIG",
)?;
git_status(
git_at(cache).args(["config", "remote.origin.partialclonefilter", "blob:none"]),
"PNCC_REMOTE_CACHE_FILTER_CONFIG",
)
}
fn fetch_continuity(
cache: &Path,
branch: &str,
previous: Option<&str>,
cache_has_previous: bool,
) -> Result<(), String> {
let refspec = format!("+refs/heads/{branch}:refs/remotes/origin/{branch}");
if previous.is_none() || !cache_has_previous {
fetch_depth(cache, &refspec, 1)?;
} else {
fetch_incremental(cache, &refspec)?;
}
let Some(previous) = previous else {
return Ok(());
};
let current = required_ref(cache, &format!("refs/remotes/origin/{branch}"))?;
if previous == current || is_ancestor(cache, previous, &current)? {
return Ok(());
}
if cache_has_previous {
return Err("PNCC_REMOTE_HISTORY_REWRITE_REJECTED".into());
}
let mut depth = 2;
while depth <= MAX_CONTINUITY_DEPTH {
fetch_depth(cache, &refspec, depth)?;
if is_ancestor(cache, previous, &current)? {
return Ok(());
}
depth = depth.saturating_mul(2);
}
Err("PNCC_REMOTE_CONTINUITY_WINDOW_EXCEEDED".into())
}
fn fetch_depth(cache: &Path, refspec: &str, depth: usize) -> Result<(), String> {
let depth = format!("--depth={depth}");
let output = git_at(cache)
.args([
"fetch",
"--no-tags",
&depth,
"--filter=blob:none",
"origin",
refspec,
])
.output()
.map_err(|error| format!("PNCC_REMOTE_INCREMENTAL_FETCH_FAILED: {error}"))?;
require_partial_fetch(output)
}
fn fetch_incremental(cache: &Path, refspec: &str) -> Result<(), String> {
let output = git_at(cache)
.args([
"fetch",
"--no-tags",
"--update-shallow",
"--filter=blob:none",
"origin",
refspec,
])
.output()
.map_err(|error| format!("PNCC_REMOTE_INCREMENTAL_FETCH_FAILED: {error}"))?;
require_partial_fetch(output)
}
fn require_partial_fetch(output: Output) -> Result<(), String> {
let output = require_success(output, "PNCC_REMOTE_INCREMENTAL_FETCH")?;
let diagnostic = String::from_utf8_lossy(&output.stderr);
if diagnostic.contains("filtering not recognized") || diagnostic.contains("filter-spec") {
Err("PNCC_REMOTE_PARTIAL_OBJECT_PROTOCOL_REQUIRED".into())
} else {
Ok(())
}
}
fn read_regular_blob(
cache: &Path,
head: &str,
relative: &str,
maximum: usize,
) -> Result<(String, Vec<u8>), String> {
validate_head(head)?;
validate_relative_path(relative)?;
let tree = git_text(
cache,
&["ls-tree", head, "--", relative],
"PNCC_REMOTE_OBJECT_MODE_READ",
)?;
let mut fields = tree.split_whitespace();
let mode = fields.next().unwrap_or_default();
let kind = fields.next().unwrap_or_default();
let object_id = fields.next().unwrap_or_default().to_ascii_lowercase();
if !matches!(mode, "100644" | "100755") || kind != "blob" {
return Err("PNCC_REMOTE_OBJECT_NOT_REGULAR_FILE".into());
}
validate_head(&object_id)?;
let bytes = git_bytes(
cache,
&["cat-file", "blob", &object_id],
"PNCC_REMOTE_OBJECT_READ",
)?;
if bytes.is_empty() || bytes.len() > maximum {
return Err("PNCC_REMOTE_OBJECT_SIZE_INVALID".into());
}
Ok((object_id, bytes))
}
fn is_ancestor(cache: &Path, ancestor: &str, descendant: &str) -> Result<bool, String> {
validate_head(ancestor)?;
validate_head(descendant)?;
let output = git_at(cache)
.args(["merge-base", "--is-ancestor", ancestor, descendant])
.output()
.map_err(|error| format!("PNCC_REMOTE_CONTINUITY_CHECK_FAILED: {error}"))?;
match output.status.code() {
Some(0) => Ok(true),
Some(1) => Ok(false),
_ => Err(format!(
"PNCC_REMOTE_CONTINUITY_CHECK_FAILED: {}",
String::from_utf8_lossy(&output.stderr).trim()
)),
}
}
fn optional_ref(cache: &Path, reference: &str) -> Result<Option<String>, String> {
let output = git_at(cache)
.args(["rev-parse", "--verify", reference])
.output()
.map_err(|error| format!("PNCC_REMOTE_CURSOR_READ_FAILED: {error}"))?;
if !output.status.success() {
return Ok(None);
}
let value = String::from_utf8(output.stdout)
.map_err(|_| "PNCC_REMOTE_CURSOR_NOT_UTF8".to_string())?
.trim()
.to_ascii_lowercase();
validate_head(&value).map(Some)
}
fn required_ref(cache: &Path, reference: &str) -> Result<String, String> {
optional_ref(cache, reference)?.ok_or_else(|| "PNCC_REMOTE_FETCH_HEAD_MISSING".into())
}
fn read_cursor(path: &Path, identity: &str, branch: &str) -> Result<Option<CursorRecord>, String> {
if !path.exists() {
return Ok(None);
}
let record: CursorRecord = read_json(path)?;
if record.schema != CURSOR_SCHEMA
|| record.remote_identity_hash != identity
|| record.branch != branch
|| record.generation == 0
{
return Err("PNCC_REMOTE_CURSOR_RECORD_INVALID".into());
}
validate_head(&record.verified_head)?;
if let Some(previous) = record.previous_verified_head.as_deref() {
validate_head(previous)?;
}
Ok(Some(record))
}
fn child_root(root: &Path, child: &str) -> Result<PathBuf, String> {
let canonical_root = root
.canonicalize()
.map_err(|error| format!("PNCC_REMOTE_STORAGE_UNAVAILABLE: {error}"))?;
let path = root.join(child);
fs::create_dir_all(&path)
.map_err(|error| format!("PNCC_REMOTE_STORAGE_UNAVAILABLE: {error}"))?;
let canonical = path
.canonicalize()
.map_err(|error| format!("PNCC_REMOTE_STORAGE_UNAVAILABLE: {error}"))?;
if canonical.parent() != Some(canonical_root.as_path()) || !canonical.is_dir() {
return Err("PNCC_REMOTE_STORAGE_BOUNDARY_INVALID".into());
}
Ok(canonical)
}
fn lock_file(path: &Path) -> Result<std::fs::File, String> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(path)
.map_err(|error| format!("PNCC_REMOTE_LOCK_FAILED: {error}"))?;
file.lock_exclusive()
.map_err(|error| format!("PNCC_REMOTE_LOCK_FAILED: {error}"))?;
Ok(file)
}
fn write_json_atomic<T: Serialize>(path: &Path, value: &T) -> Result<(), String> {
let parent = path
.parent()
.ok_or("PNCC_REMOTE_STORAGE_BOUNDARY_INVALID")?;
let temporary = parent.join(format!(".pncc-{}.tmp", Uuid::new_v4()));
let bytes = serde_json::to_vec_pretty(value)
.map_err(|error| format!("PNCC_REMOTE_RECORD_INVALID: {error}"))?;
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&temporary)
.map_err(|error| format!("PNCC_REMOTE_RECORD_WRITE_FAILED: {error}"))?;
file.write_all(&bytes)
.and_then(|_| file.sync_all())
.map_err(|error| format!("PNCC_REMOTE_RECORD_WRITE_FAILED: {error}"))?;
fs::rename(&temporary, path)
.map_err(|error| format!("PNCC_REMOTE_RECORD_WRITE_FAILED: {error}"))
}
fn read_json<T: for<'de> Deserialize<'de>>(path: &Path) -> Result<T, String> {
let metadata = fs::symlink_metadata(path).map_err(|error| {
if error.kind() == std::io::ErrorKind::NotFound {
"PNCC_REMOTE_RECORD_NOT_FOUND".to_string()
} else {
format!("PNCC_REMOTE_RECORD_READ_FAILED: {error}")
}
})?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
return Err("PNCC_REMOTE_RECORD_INVALID".into());
}
serde_json::from_slice(
&fs::read(path).map_err(|error| format!("PNCC_REMOTE_RECORD_READ_FAILED: {error}"))?,
)
.map_err(|error| format!("PNCC_REMOTE_RECORD_INVALID: {error}"))
}
fn directory_size(root: &Path) -> Result<u64, String> {
let mut total = 0_u64;
let mut pending = vec![root.to_path_buf()];
while let Some(path) = pending.pop() {
let metadata = fs::symlink_metadata(&path)
.map_err(|error| format!("PNCC_REMOTE_CACHE_MEASURE_FAILED: {error}"))?;
if metadata.file_type().is_symlink() {
return Err("PNCC_REMOTE_CACHE_SYMLINK_REJECTED".into());
}
if metadata.is_file() {
total = total.saturating_add(metadata.len());
} else if metadata.is_dir() {
for entry in fs::read_dir(&path)
.map_err(|error| format!("PNCC_REMOTE_CACHE_MEASURE_FAILED: {error}"))?
{
pending.push(
entry
.map_err(|error| format!("PNCC_REMOTE_CACHE_MEASURE_FAILED: {error}"))?
.path(),
);
}
}
}
Ok(total)
}
fn remove_exact_cache(root: &Path, cache: &Path) -> Result<(), String> {
if cache.parent() != Some(root) || cache == root || !cache.starts_with(root) {
return Err("PNCC_REMOTE_CACHE_BOUNDARY_INVALID".into());
}
fs::remove_dir_all(cache).map_err(|error| format!("PNCC_REMOTE_CACHE_EVICTION_FAILED: {error}"))
}
fn trusted_git() -> Command {
let mut command = Command::new("/usr/bin/git");
command
.env_clear()
.env("PATH", "/usr/bin:/bin")
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_TERMINAL_PROMPT", "0")
.env("GCM_INTERACTIVE", "Never")
.args(["-c", "protocol.file.allow=never"]);
if cfg!(test) {
command.args(["-c", "protocol.file.allow=always"]);
}
command
}
fn git_at(path: &Path) -> Command {
let mut command = trusted_git();
command.arg("-C").arg(path);
command
}
fn git_status(command: &mut Command, label: &str) -> Result<(), String> {
let output = command
.output()
.map_err(|error| format!("{label}_FAILED: {error}"))?;
require_success(output, label).map(|_| ())
}
fn git_text(path: &Path, args: &[&str], label: &str) -> Result<String, String> {
let output = git_at(path)
.args(args)
.output()
.map_err(|error| format!("{label}_FAILED: {error}"))?;
String::from_utf8(require_success(output, label)?.stdout)
.map_err(|_| format!("{label}_NOT_UTF8"))
}
fn git_bytes(path: &Path, args: &[&str], label: &str) -> Result<Vec<u8>, String> {
let output = git_at(path)
.args(args)
.output()
.map_err(|error| format!("{label}_FAILED: {error}"))?;
Ok(require_success(output, label)?.stdout)
}
fn require_success(output: Output, label: &str) -> Result<Output, String> {
if output.status.success() {
Ok(output)
} else {
Err(format!(
"{label}_FAILED: {}",
String::from_utf8_lossy(&output.stderr).trim()
))
}
}
fn sha256_hex(bytes: &[u8]) -> String {
digest(&SHA256, bytes)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn fixture() -> (TempDir, PathBuf, PathBuf, String) {
let temp = TempDir::new().unwrap();
let source = temp.path().join("source");
let remote = temp.path().join("remote.git");
let storage = temp.path().join("storage");
fs::create_dir_all(source.join("brain")).unwrap();
fs::create_dir(&storage).unwrap();
test_git(&source, &["init", "-b", "main"]);
fs::write(source.join("brain/CORE.hldp"), "first\n").unwrap();
test_git(&source, &["add", "."]);
test_git(
&source,
&[
"-c",
"user.name=PNCC Test",
"-c",
"user.email=pncc@test.invalid",
"commit",
"-m",
"first",
],
);
let output = trusted_git()
.args(["init", "--bare", &remote.to_string_lossy()])
.output()
.unwrap();
assert!(output.status.success());
test_git(&remote, &["config", "uploadpack.allowFilter", "true"]);
test_git(
&remote,
&["config", "uploadpack.allowAnySHA1InWant", "true"],
);
test_git(
&source,
&["remote", "add", "origin", &remote.to_string_lossy()],
);
test_git(&source, &["push", "-u", "origin", "main"]);
let remote_url = format!("file://{}", remote.display());
register_remote_mount_at(
&storage,
RegisterPnccRemoteMountInput {
mount_id: "persona-primary".into(),
remote_url: remote_url.clone(),
branch: None,
},
true,
)
.unwrap();
(temp, source, storage, remote_url)
}
fn test_git(path: &Path, args: &[&str]) -> String {
let mut command = trusted_git();
command
.env("GIT_ALLOW_PROTOCOL", "file")
.arg("-C")
.arg(path)
.args(args);
let output = command.output().unwrap();
assert!(
output.status.success(),
"git {args:?}: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout).trim().into()
}
fn read_input() -> ReadMountedPnccRemoteObjectInput {
ReadMountedPnccRemoteObjectInput {
mount_id: "persona-primary".into(),
relative_path: "brain/CORE.hldp".into(),
expected_head: None,
max_cache_bytes: None,
}
}
#[test]
fn reads_incrementally_without_worktree_or_model_execution() {
let (_temp, _source, storage, _url) = fixture();
let first =
read_mounted_remote_object_with_file_policy(&storage, read_input(), true).unwrap();
assert_eq!(first.content, "first\n");
assert!(first.cursor_advanced);
assert!(first.continuity_verified);
assert!(first.fetched_incremental_objects);
assert!(!first.worktree_created);
assert!(!first.full_history_requested);
assert!(!first.model_inference_started);
assert!(!first.reality_execution_allowed);
let second =
read_mounted_remote_object_with_file_policy(&storage, read_input(), true).unwrap();
assert!(!second.cursor_advanced);
assert!(!second.fetched_incremental_objects);
assert_eq!(second.previous_verified_head, Some(first.remote_head));
}
#[test]
fn appends_only_a_descendant_segment_and_rejects_rewritten_history() {
let (_temp, source, storage, _url) = fixture();
let first =
read_mounted_remote_object_with_file_policy(&storage, read_input(), true).unwrap();
fs::write(source.join("brain/CORE.hldp"), "second\n").unwrap();
test_git(&source, &["add", "."]);
test_git(
&source,
&[
"-c",
"user.name=PNCC Test",
"-c",
"user.email=pncc@test.invalid",
"commit",
"-m",
"second",
],
);
test_git(&source, &["push", "origin", "main"]);
let second =
read_mounted_remote_object_with_file_policy(&storage, read_input(), true).unwrap();
assert_eq!(second.previous_verified_head, Some(first.remote_head));
assert_eq!(second.content, "second\n");
test_git(&source, &["checkout", "--orphan", "rewrite"]);
test_git(&source, &["rm", "-rf", "."]);
fs::create_dir_all(source.join("brain")).unwrap();
fs::write(source.join("brain/CORE.hldp"), "rewrite\n").unwrap();
test_git(&source, &["add", "."]);
test_git(
&source,
&[
"-c",
"user.name=PNCC Test",
"-c",
"user.email=pncc@test.invalid",
"commit",
"-m",
"rewrite",
],
);
test_git(&source, &["branch", "-M", "main"]);
test_git(&source, &["push", "--force", "origin", "main"]);
assert_eq!(
read_mounted_remote_object_with_file_policy(&storage, read_input(), true).unwrap_err(),
"PNCC_REMOTE_HISTORY_REWRITE_REJECTED"
);
}
#[test]
fn public_registration_rejects_credentials_and_non_https_remotes() {
assert_eq!(
validate_remote_url("https://token@example.invalid/repo.git", false).unwrap_err(),
"PNCC_REMOTE_URL_MUST_BE_CREDENTIAL_FREE_HTTPS"
);
assert_eq!(
validate_remote_url("http://example.invalid/repo.git", false).unwrap_err(),
"PNCC_REMOTE_URL_MUST_BE_CREDENTIAL_FREE_HTTPS"
);
assert_eq!(
validate_relative_path("../brain.hldp").unwrap_err(),
"PNCC_REMOTE_PATH_INVALID"
);
}
#[test]
fn cache_eviction_keeps_the_durable_continuity_cursor() {
let (_temp, _source, storage, _url) = fixture();
let mut input = read_input();
input.max_cache_bytes = Some(MIN_CACHE_LIMIT_BYTES);
let objects = child_root(&storage, "objects").unwrap();
let first =
read_mounted_remote_object_with_file_policy(&storage, input.clone(), true).unwrap();
let cache = objects.join(format!("{}.git", first.remote_identity_hash));
fs::write(
cache.join("force-budget-overrun"),
vec![0_u8; MIN_CACHE_LIMIT_BYTES as usize],
)
.unwrap();
let second = read_mounted_remote_object_with_file_policy(&storage, input, true).unwrap();
assert_eq!(second.cache_state, "EVICTED_AFTER_BOUNDED_READ");
assert!(!cache.exists());
assert!(storage
.join("continuity")
.join(format!("{}.json", second.remote_identity_hash))
.is_file());
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ use tauri::{AppHandle, Runtime, Url};
use tauri_plugin_updater::UpdaterExt;
const EMBEDDED_RELEASE_TRUST: &str = include_str!("../release-trust.json");
const PUBLIC_RELEASE_ENDPOINT_PATH: &str = "/hololake/releases/latest.json";
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
@ -33,6 +34,31 @@ enum ValidatedReleaseTrust {
},
}
pub(crate) struct ProvisionedReleaseTrust {
pub endpoints: Vec<Url>,
pub allowed_release_host: String,
}
pub(crate) fn provisioned_release_trust() -> Result<Option<ProvisionedReleaseTrust>, String> {
match validate_release_trust(EMBEDDED_RELEASE_TRUST)? {
ValidatedReleaseTrust::Disabled => Ok(None),
ValidatedReleaseTrust::Enabled { endpoints, .. } => Ok(Some(ProvisionedReleaseTrust {
allowed_release_host: endpoints[0]
.host_str()
.ok_or("the HoloLake release endpoint host is missing")?
.to_owned(),
endpoints,
})),
}
}
pub(crate) fn release_trust_state() -> Result<&'static str, String> {
match validate_release_trust(EMBEDDED_RELEASE_TRUST)? {
ValidatedReleaseTrust::Disabled => Ok("UNPROVISIONED_FAIL_CLOSED"),
ValidatedReleaseTrust::Enabled { .. } => Ok("READY_HUMAN_CONFIRMATION_REQUIRED"),
}
}
fn validate_release_trust(raw: &str) -> Result<ValidatedReleaseTrust, String> {
let trust: ReleaseTrust = serde_json::from_str(raw)
.map_err(|error| format!("invalid HoloLake release trust document: {error}"))?;
@ -83,6 +109,12 @@ fn validate_release_trust(raw: &str) -> Result<ValidatedReleaseTrust, String> {
"the updater endpoint is not owned by the configured HoloLake host".into(),
);
}
if endpoint.path() != PUBLIC_RELEASE_ENDPOINT_PATH
|| endpoint.query().is_some()
|| endpoint.fragment().is_some()
{
return Err("the HoloLake release endpoint path is not registered".into());
}
Ok(ValidatedReleaseTrust::Enabled {
endpoints: vec![endpoint],
public_key: trust.public_key,
@ -104,7 +136,7 @@ pub fn install_updater_if_provisioned<R: Runtime>(
.pubkey(public_key)
.build();
app.plugin(updater)?;
app.updater_builder().endpoints(endpoints)?.build()?;
let _ = app.updater_builder().endpoints(endpoints)?.build()?;
Ok(())
}
}
@ -141,7 +173,7 @@ mod tests {
fn accepts_https_shape_but_rejects_insecure_endpoints() {
let owned_shape = document(
"PROVISIONED",
"\"https://releases.example.test/latest.json\"",
"\"https://releases.example.test/hololake/releases/latest.json\"",
"public",
"\"releases.example.test\"",
);
@ -151,18 +183,25 @@ mod tests {
);
let insecure = document(
"PROVISIONED",
"\"http://releases.example.test/latest.json\"",
"\"http://releases.example.test/hololake/releases/latest.json\"",
"public",
"\"releases.example.test\"",
);
assert!(validate_release_trust(&insecure).is_err());
let mismatched_host = document(
"PROVISIONED",
"\"https://upstream.example.test/latest.json\"",
"\"https://upstream.example.test/hololake/releases/latest.json\"",
"public",
"\"releases.example.test\"",
);
assert!(validate_release_trust(&mismatched_host).is_err());
let mismatched_path = document(
"PROVISIONED",
"\"https://releases.example.test/latest.json\"",
"public",
"\"releases.example.test\"",
);
assert!(validate_release_trust(&mismatched_path).is_err());
}
#[test]

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@
"bundle": {
"active": true,
"targets": "all",
"createUpdaterArtifacts": true,
"createUpdaterArtifacts": false,
"category": "Productivity"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -0,0 +1,167 @@
:root,
[data-theme='night'] {
/* Primitive tokens — GHS-014 five-lakes v1.3, night lake. */
--primitive-bg-a: #0a1020;
--primitive-bg-b: #0b1220;
--primitive-bg-c: #070c16;
--primitive-bg-d: #04070d;
--primitive-star-core: #f7ebc8;
--primitive-star-bright: #ffefc2;
--primitive-text-primary: rgba(234, 238, 248, 0.96);
--primitive-text-body: rgba(222, 228, 242, 0.88);
--primitive-text-aux: rgba(196, 206, 228, 0.62);
--primitive-text-faint: rgba(180, 192, 220, 0.4);
--primitive-nebula-a: rgba(91, 111, 196, 0.13);
--primitive-nebula-b: rgba(138, 111, 201, 0.05);
--primitive-glass: rgba(178, 198, 255, 0.045);
--primitive-glass-hover: rgba(178, 198, 255, 0.075);
--primitive-glass-edge: rgba(202, 216, 255, 0.13);
--primitive-glass-top: rgba(255, 255, 255, 0.06);
--primitive-mist-a: rgba(157, 180, 255, 0.1);
--primitive-mist-b: rgba(247, 235, 200, 0.04);
--primitive-cool-glow: rgba(157, 180, 255, 0.42);
--primitive-warm-glow: rgba(247, 235, 200, 0.5);
--primitive-shadow: rgba(0, 0, 0, 0.34);
--primitive-scene-opacity: 0.76;
/* Semantic tokens. */
--surface-sky: var(--primitive-bg-a);
--surface-horizon: var(--primitive-bg-b);
--surface-lake: var(--primitive-bg-c);
--surface-depth: var(--primitive-bg-d);
--content-primary: var(--primitive-text-primary);
--content-secondary: var(--primitive-text-body);
--content-muted: var(--primitive-text-aux);
--content-faint: var(--primitive-text-faint);
--accent-light: var(--primitive-star-core);
--accent-light-strong: var(--primitive-star-bright);
--state-ready: #b8d7c4;
--state-waiting: #cfbddf;
--state-quiet: var(--primitive-text-aux);
--focus-ring: rgba(247, 235, 200, 0.7);
/* Component tokens. */
--button-primary-bg: rgba(247, 235, 200, 0.92);
--button-primary-text: #15181f;
--button-primary-shadow: 0 12px 42px rgba(247, 235, 200, 0.14);
--button-secondary-bg: var(--primitive-glass);
--button-secondary-text: var(--content-secondary);
--button-secondary-edge: var(--primitive-glass-edge);
--panel-bg: rgba(8, 13, 25, 0.72);
--panel-edge: var(--primitive-glass-edge);
--status-dot: var(--accent-light);
}
[data-theme='dawn'] {
--primitive-bg-a: #f5f7fc;
--primitive-bg-b: #eaeff8;
--primitive-bg-c: #dfe8f4;
--primitive-bg-d: #d3e0ef;
--primitive-star-core: #c9a24b;
--primitive-star-bright: #a96f2c;
--primitive-text-primary: rgba(22, 33, 58, 0.94);
--primitive-text-body: rgba(36, 49, 76, 0.86);
--primitive-text-aux: rgba(72, 88, 120, 0.62);
--primitive-text-faint: rgba(98, 112, 142, 0.48);
--primitive-nebula-a: rgba(122, 144, 220, 0.12);
--primitive-nebula-b: rgba(240, 196, 150, 0.12);
--primitive-glass: rgba(255, 255, 255, 0.34);
--primitive-glass-hover: rgba(255, 255, 255, 0.52);
--primitive-glass-edge: rgba(30, 50, 90, 0.12);
--primitive-glass-top: rgba(255, 255, 255, 0.65);
--primitive-mist-a: rgba(255, 255, 255, 0.55);
--primitive-mist-b: rgba(250, 214, 150, 0.3);
--primitive-cool-glow: rgba(96, 124, 205, 0.35);
--primitive-warm-glow: rgba(201, 162, 75, 0.5);
--primitive-shadow: rgba(42, 66, 105, 0.16);
--primitive-scene-opacity: 0.11;
--state-ready: #3f7058;
--state-waiting: #725b82;
--panel-bg: rgba(247, 250, 255, 0.76);
--button-primary-bg: rgba(45, 59, 84, 0.92);
--button-primary-text: #f8f4e9;
--button-primary-shadow: 0 12px 36px rgba(45, 59, 84, 0.16);
}
[data-theme='nebula'] {
--primitive-bg-a: #150e26;
--primitive-bg-b: #160f28;
--primitive-bg-c: #0e0919;
--primitive-bg-d: #080510;
--primitive-star-core: #ede4f6;
--primitive-star-bright: #f8f1ff;
--primitive-text-primary: rgba(238, 234, 248, 0.96);
--primitive-text-body: rgba(226, 220, 240, 0.88);
--primitive-text-aux: rgba(202, 194, 226, 0.62);
--primitive-text-faint: rgba(186, 178, 214, 0.42);
--primitive-nebula-a: rgba(138, 111, 201, 0.24);
--primitive-nebula-b: rgba(91, 111, 196, 0.11);
--primitive-glass: rgba(196, 178, 255, 0.05);
--primitive-glass-hover: rgba(196, 178, 255, 0.09);
--primitive-glass-edge: rgba(210, 198, 255, 0.14);
--primitive-glass-top: rgba(255, 255, 255, 0.07);
--primitive-mist-a: rgba(170, 150, 255, 0.12);
--primitive-mist-b: rgba(239, 217, 168, 0.05);
--primitive-cool-glow: rgba(170, 150, 255, 0.45);
--primitive-warm-glow: rgba(239, 217, 168, 0.5);
--primitive-shadow: rgba(0, 0, 0, 0.38);
--primitive-scene-opacity: 0.44;
--panel-bg: rgba(18, 12, 34, 0.76);
}
[data-theme='candle'] {
--primitive-bg-a: #1b130b;
--primitive-bg-b: #181108;
--primitive-bg-c: #100b05;
--primitive-bg-d: #0a0703;
--primitive-star-core: #ffd9a0;
--primitive-star-bright: #ffe7be;
--primitive-text-primary: rgba(246, 238, 226, 0.96);
--primitive-text-body: rgba(236, 226, 210, 0.88);
--primitive-text-aux: rgba(214, 198, 176, 0.62);
--primitive-text-faint: rgba(196, 180, 158, 0.42);
--primitive-nebula-a: rgba(214, 150, 80, 0.13);
--primitive-nebula-b: rgba(160, 110, 70, 0.07);
--primitive-glass: rgba(255, 214, 160, 0.045);
--primitive-glass-hover: rgba(255, 214, 160, 0.08);
--primitive-glass-edge: rgba(255, 220, 170, 0.13);
--primitive-glass-top: rgba(255, 255, 255, 0.06);
--primitive-mist-a: rgba(255, 196, 110, 0.09);
--primitive-mist-b: rgba(255, 214, 140, 0.04);
--primitive-cool-glow: rgba(214, 178, 140, 0.4);
--primitive-warm-glow: rgba(255, 196, 110, 0.55);
--primitive-shadow: rgba(0, 0, 0, 0.38);
--primitive-scene-opacity: 0.4;
--panel-bg: rgba(24, 17, 9, 0.78);
}
[data-theme='clear'] {
--primitive-bg-a: #f1f5f8;
--primitive-bg-b: #e8eef3;
--primitive-bg-c: #dee7ed;
--primitive-bg-d: #d2dee7;
--primitive-star-core: #5f7e9f;
--primitive-star-bright: #41628a;
--primitive-text-primary: rgba(20, 36, 52, 0.94);
--primitive-text-body: rgba(34, 50, 68, 0.86);
--primitive-text-aux: rgba(66, 86, 106, 0.62);
--primitive-text-faint: rgba(92, 110, 128, 0.48);
--primitive-nebula-a: rgba(100, 140, 180, 0.1);
--primitive-nebula-b: rgba(140, 170, 200, 0.08);
--primitive-glass: rgba(255, 255, 255, 0.32);
--primitive-glass-hover: rgba(255, 255, 255, 0.5);
--primitive-glass-edge: rgba(20, 50, 80, 0.12);
--primitive-glass-top: rgba(255, 255, 255, 0.65);
--primitive-mist-a: rgba(255, 255, 255, 0.55);
--primitive-mist-b: rgba(190, 214, 234, 0.35);
--primitive-cool-glow: rgba(78, 112, 150, 0.35);
--primitive-warm-glow: rgba(170, 138, 70, 0.5);
--primitive-shadow: rgba(37, 66, 90, 0.14);
--primitive-scene-opacity: 0.08;
--state-ready: #3f7058;
--state-waiting: #6a5b7a;
--panel-bg: rgba(246, 250, 253, 0.76);
--button-primary-bg: rgba(45, 66, 85, 0.92);
--button-primary-text: #f6f2e9;
--button-primary-shadow: 0 12px 36px rgba(45, 66, 85, 0.15);
}

View file

@ -1,24 +1,489 @@
import { StrictMode } from 'react'
import { StrictMode, useCallback, useEffect, useMemo, useState } from 'react'
import { createRoot } from 'react-dom/client'
import { invoke } from '@tauri-apps/api/core'
import './design-tokens.css'
import './styles.css'
function FoundationScreen() {
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
type Panel = 'channel' | 'receipts' | 'updates' | 'settings' | null
interface HomeStatus {
schema: string
directLocalBrokerState: string
directConnectionCount: number
resumableSessionCount: number
codeRepositoryMountCount: number
pnccReceiptCount: number
updateState: string
releaseRecoveryState: string
automaticUpstreamUpdates: boolean
mcpRole: string
}
interface DiscoveryTicketReceipt {
schema: string
state: string
accountKey: string
laneId: string
clientInstanceId: string
discoveryTicket: string
issuedAtUnixMs: number
receiptId: string
}
interface ReceiptEvent {
sequence: number
kind: string
observedAtUnixMs: number
eventHash: string
}
interface ReceiptProjection {
events: ReceiptEvent[]
returnedEventCount: number
lastSequence: number
}
interface RepositoryInspection {
personaId: string
humanResponsibilitySubject: string
repositoryPath: string
gitHead: string
repositoryClean: boolean
currentCheckpoint: { relativePath: string; sha256: string }
modelInferenceStarted: boolean
realityExecutionAllowed: boolean
}
interface RepositoryCandidate {
schema: string
state: string
candidateId: string
confirmationToken: string
expiresAtUnixMs: number
inspection: RepositoryInspection
}
interface ReleaseCandidate {
candidateId: string
confirmationToken: string
releaseId: string
currentVersion: string
version: string
notes: string
features: string[]
fixes: string[]
minimumVersion: string
dataMigrationRequired: boolean
compatibilityNotes?: string
restartMessage?: string
rollbackSupported: boolean
previousVersion?: string
expiresAtUnixMs: number
}
interface ReleaseCheckReceipt {
state: string
candidate?: ReleaseCandidate
}
const themes: Array<{ id: ThemeId; name: string }> = [
{ id: 'night', name: '夜湖星光' },
{ id: 'dawn', name: '晨湖曦光' },
{ id: 'nebula', name: '星云紫夜' },
{ id: 'candle', name: '烛畔暖湖' },
{ id: 'clear', name: '清浅澄湖' },
]
const recoveryPreviewStates = [
'NONE',
'AWAITING_HUMAN_HEALTH_CONFIRMATION',
'ROLLED_BACK_AWAITING_HUMAN_ACKNOWLEDGEMENT',
'INSTALL_FAILED_BACKUP_RETAINED',
'INSTALL_INTERRUPTED_BACKUP_RETAINED',
'INSTALL_REPLACED_AWAITING_MANUAL_RESTART',
'ROLLBACK_REPLACED_AWAITING_MANUAL_RESTART',
'RECOVERY_STATE_UNREADABLE_ACTION_REQUIRED',
]
const requestedRecoveryPreview = import.meta.env.DEV
? new URLSearchParams(window.location.search).get('releaseRecoveryState')
: null
const recoveryPreviewState = requestedRecoveryPreview && recoveryPreviewStates.includes(requestedRecoveryPreview)
? requestedRecoveryPreview
: 'NONE'
const previewStatus: HomeStatus = {
schema: 'hololake.home-status/preview',
directLocalBrokerState: 'PREVIEW',
directConnectionCount: 0,
resumableSessionCount: 0,
codeRepositoryMountCount: 0,
pnccReceiptCount: 0,
updateState: 'UNPROVISIONED_FAIL_CLOSED',
releaseRecoveryState: recoveryPreviewState,
automaticUpstreamUpdates: false,
mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY',
}
function getOrCreateLocalId(key: string, prefix: string) {
const existing = window.localStorage.getItem(key)
if (existing) return existing
const generated = `${prefix}-${crypto.randomUUID()}`
window.localStorage.setItem(key, generated)
return generated
}
function Icon({ name }: { name: 'settings' | 'arrow' | 'receipt' | 'close' | 'copy' }) {
const paths = {
settings: <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-1.9 1.9-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.04 1.56V20h-2.7v-.08a1.7 1.7 0 0 0-1.05-1.56 1.7 1.7 0 0 0-1.88.34l-.06.06-1.9-1.9.06-.06A1.7 1.7 0 0 0 7.72 15a1.7 1.7 0 0 0-1.56-1.04H6v-2.7h.08A1.7 1.7 0 0 0 7.64 10a1.7 1.7 0 0 0-.34-1.88l-.06-.06 1.9-1.9.06.06a1.7 1.7 0 0 0 1.88.34A1.7 1.7 0 0 0 12.12 5V5h2.7v.08a1.7 1.7 0 0 0 1.04 1.56 1.7 1.7 0 0 0 1.88-.34l.06-.06 1.9 1.9-.06.06a1.7 1.7 0 0 0-.34 1.88 1.7 1.7 0 0 0 1.56 1.04H21v2.7h-.08A1.7 1.7 0 0 0 19.4 15Z"/></>,
arrow: <><path d="M5 12h13"/><path d="m14 7 5 5-5 5"/></>,
receipt: <><path d="M7 3h10v18l-2.5-1.5L12 21l-2.5-1.5L7 21Z"/><path d="M10 8h4M10 12h4"/></>,
close: <><path d="m7 7 10 10M17 7 7 17"/></>,
copy: <><rect x="8" y="8" width="10" height="11" rx="2"/><path d="M15 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2"/></>,
}
return <svg viewBox="0 0 24 24" aria-hidden="true">{paths[name]}</svg>
}
function HoloLakeApp() {
const [theme, setTheme] = useState<ThemeId>(() => (window.localStorage.getItem('hololake-theme') as ThemeId) || 'night')
const [status, setStatus] = useState<HomeStatus>(previewStatus)
const [panel, setPanel] = useState<Panel>(null)
const [ticket, setTicket] = useState<DiscoveryTicketReceipt | null>(null)
const [receipts, setReceipts] = useState<ReceiptEvent[]>([])
const [repositoryCandidate, setRepositoryCandidate] = useState<RepositoryCandidate | null>(null)
const [repositoryBusy, setRepositoryBusy] = useState(false)
const [repositoryMessage, setRepositoryMessage] = useState('')
const [releaseCandidate, setReleaseCandidate] = useState<ReleaseCandidate | null>(null)
const [releaseBusy, setReleaseBusy] = useState(false)
const [releaseMessage, setReleaseMessage] = useState('')
const [message, setMessage] = useState('')
const refreshStatus = useCallback(async () => {
try {
setStatus(await invoke<HomeStatus>('get_hololake_home_status'))
} catch {
setStatus(previewStatus)
}
}, [])
useEffect(() => {
void refreshStatus()
const timer = window.setInterval(() => void refreshStatus(), 3000)
return () => window.clearInterval(timer)
}, [refreshStatus])
useEffect(() => {
document.documentElement.dataset.theme = theme
window.localStorage.setItem('hololake-theme', theme)
}, [theme])
const connectionState = useMemo(() => {
if (status.directConnectionCount > 0) return { label: '已连接', detail: `${status.directConnectionCount} 个直连通道在线`, tone: 'ready' }
if (status.resumableSessionCount > 0) return { label: '可续接', detail: `${status.resumableSessionCount} 个会话等待续接`, tone: 'waiting' }
return { label: '等待连接', detail: '本机直连核心已就绪', tone: 'quiet' }
}, [status])
const releaseRecoveryIdle = useMemo(() => [
'NONE',
'HEALTH_CONFIRMED_BACKUP_REMOVED',
'ROLLBACK_CONFIRMED_BACKUP_REMOVED',
'INSTALL_FAILURE_ACKNOWLEDGED_BACKUP_REMOVED',
].includes(status.releaseRecoveryState), [status.releaseRecoveryState])
const releaseRecoveryNeedsAttention = !releaseRecoveryIdle
const openReceipts = async () => {
setPanel('receipts')
await loadReceipts()
}
const loadReceipts = async () => {
try {
const projection = await invoke<ReceiptProjection>('query_pncc_receipt_projection', { input: { afterSequence: 0, limit: 25 } })
setReceipts(projection.events)
} catch {
setReceipts([])
}
}
const chooseRepository = async () => {
setRepositoryBusy(true)
setRepositoryMessage('')
try {
const candidate = await invoke<RepositoryCandidate | null>('select_pncc_repository_candidate')
setRepositoryCandidate(candidate)
if (!candidate) setRepositoryMessage('已取消选择,没有写入任何绑定。')
} catch (error) {
setRepositoryCandidate(null)
setRepositoryMessage(`这个文件夹不能作为代码通道:${String(error)}`)
} finally {
setRepositoryBusy(false)
}
}
const confirmRepository = async () => {
if (!repositoryCandidate) return
setRepositoryBusy(true)
setRepositoryMessage('')
try {
await invoke('confirm_pncc_repository_mount', {
input: {
candidateId: repositoryCandidate.candidateId,
confirmationToken: repositoryCandidate.confirmationToken,
},
})
setRepositoryCandidate(null)
setRepositoryMessage('代码仓库已按当前固定提交绑定为只读通道。')
await Promise.all([refreshStatus(), loadReceipts()])
} catch (error) {
setRepositoryMessage(`绑定没有写入:${String(error)}`)
} finally {
setRepositoryBusy(false)
}
}
const checkUpdate = async () => {
setReleaseBusy(true)
setReleaseMessage('')
try {
const receipt = await invoke<ReleaseCheckReceipt>('check_hololake_update')
setReleaseCandidate(receipt.candidate || null)
if (!receipt.candidate) setReleaseMessage('当前已经是最新版本。')
} catch (error) {
const reason = String(error)
setReleaseCandidate(null)
setReleaseMessage(reason.includes('UNPROVISIONED_NO_NETWORK_REQUEST')
? '京东主控的正式发布地址和更新公钥还没有登记。HoloLake 保持离线,不会向任何上游检查更新。'
: `更新检查没有完成:${reason}`)
} finally {
setReleaseBusy(false)
}
}
const confirmUpdateInstall = async () => {
if (!releaseCandidate) return
setReleaseBusy(true)
setReleaseMessage('正在重新核对广播并验证安装包;完成后由你手动重启 HoloLake。')
try {
await invoke('confirm_hololake_update_install', {
input: {
candidateId: releaseCandidate.candidateId,
confirmationToken: releaseCandidate.confirmationToken,
},
})
setReleaseCandidate(null)
setReleaseMessage('签名安装包已验证并安装。请在准备好后手动重启 HoloLake。')
await refreshStatus()
} catch (error) {
setReleaseMessage(`更新没有安装:${String(error)}`)
} finally {
setReleaseBusy(false)
}
}
const resolveReleaseRecovery = async (action: 'keep' | 'rollback') => {
setReleaseBusy(true)
setReleaseMessage(action === 'rollback' ? '正在复核上一版签名并恢复;完成后仍由你手动重启。' : '正在确认当前版本健康并清理上一版副本。')
try {
if (action === 'rollback') {
await invoke('rollback_hololake_update')
setReleaseMessage('上一版已恢复到应用位置。请手动重启 HoloLake启动后再确认恢复正常。')
} else {
await invoke('confirm_hololake_release_health')
setReleaseMessage('当前运行版本已由你确认,临时回退副本已经清理。')
}
await refreshStatus()
} catch (error) {
setReleaseMessage(`没有改变当前应用:${String(error)}`)
} finally {
setReleaseBusy(false)
}
}
const issueInvitation = async () => {
setMessage('')
try {
const receipt = await invoke<DiscoveryTicketReceipt>('issue_direct_local_discovery_ticket', {
input: {
accountId: getOrCreateLocalId('hololake-local-account', 'human-local'),
laneId: 'personal-channel',
clientInstanceId: getOrCreateLocalId('hololake-external-ai-client', 'external-ai'),
},
})
setTicket(receipt)
setMessage('连接邀请已生成。它只在这台电脑上有效。')
} catch (error) {
setMessage(`暂时无法生成连接邀请:${String(error)}`)
}
}
const invitationText = ticket ? JSON.stringify({
schema: 'hololake.direct-local-invitation/v1',
transport: 'INSTALLED_HOLOLAKE_EXECUTABLE_WITH_CONNECTOR_FLAG',
connectorArgument: '--connector',
openSession: {
operation: 'OPEN_SESSION',
input: {
accountId: window.localStorage.getItem('hololake-local-account'),
laneId: ticket.laneId,
clientInstanceId: ticket.clientInstanceId,
discoveryTicket: ticket.discoveryTicket,
},
},
mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY',
}, null, 2) : ''
const copyInvitation = async () => {
try {
await navigator.clipboard.writeText(invitationText)
setMessage('已复制。把它交给桌面上的编程 AI 即可。')
} catch {
setMessage('复制没有成功,请选中下方邀请内容后复制。')
}
}
return (
<main>
<p className="eyebrow">HoloLake Native Desktop</p>
<h1>线</h1>
<p>
Tauri
</p>
<dl>
<div><dt></dt><dd></dd></div>
<div><dt>广</dt><dd></dd></div>
<div><dt></dt><dd></dd></div>
</dl>
</main>
<div className="world-shell">
<div className="scene-image" aria-hidden="true" />
<div className="nebula nebula-one" aria-hidden="true" />
<div className="nebula nebula-two" aria-hidden="true" />
<div className="mist" aria-hidden="true" />
<div className="stars" aria-hidden="true">{Array.from({ length: 18 }, (_, index) => <i key={index} style={{ '--star-index': index } as React.CSSProperties} />)}</div>
<div className="glimmers" aria-hidden="true">{Array.from({ length: 11 }, (_, index) => <i key={index} style={{ '--glimmer-index': index } as React.CSSProperties} />)}</div>
<header className="world-header">
<div className="world-mark">
<span className="world-name"></span>
<span className="world-route"></span>
</div>
<button className="icon-button" type="button" aria-label="打开设置" onClick={() => setPanel('settings')}><Icon name="settings" /></button>
</header>
<main className="home-content">
<section className="hero" aria-labelledby="home-title">
<p className="route-kicker">HOLOLAKE · PERSONAL CHANNEL</p>
<h1 id="home-title"><br /></h1>
<p className="hero-copy"> AI HoloLake 线</p>
<div className="hero-actions">
<button className="primary-action" type="button" onClick={() => setPanel('channel')}>
{status.directConnectionCount > 0 ? '已连接 · 查看通道' : '连接编程 AI'}
<Icon name="arrow" />
</button>
<button className="secondary-action" type="button" onClick={() => void openReceipts()}><Icon name="receipt" /></button>
</div>
</section>
<section className="status-cluster" aria-label="光湖运行状态">
<article className="status-item">
<span className={`status-light ${connectionState.tone}`} aria-hidden="true" />
<div><h2></h2><p>{connectionState.detail}</p></div>
</article>
<article className="status-item">
<span className={`status-light ${status.codeRepositoryMountCount > 0 ? 'ready' : 'quiet'}`} aria-hidden="true" />
<div><h2></h2><p>{status.codeRepositoryMountCount > 0 ? `已验证 · ${status.codeRepositoryMountCount} 个仓库` : '等待人类确认绑定'}</p></div>
</article>
<button className="status-item status-button" type="button" onClick={() => setPanel('updates')}>
<span className={`status-light ${releaseRecoveryNeedsAttention ? 'waiting' : status.updateState.startsWith('READY') ? 'ready' : 'quiet'}`} aria-hidden="true" />
<div><h2>广</h2><p>{releaseRecoveryNeedsAttention ? '更新结果等待你的确认' : status.updateState.startsWith('READY') ? '仅接收光湖签名广播' : '信任根未配置 · 已关闭联网'}</p></div>
</button>
</section>
</main>
<footer className="world-footer">
<p><b>GH-AIOS</b><span></span></p>
<p className="trust-note"><i /> · </p>
</footer>
{panel && <div className="panel-backdrop" role="presentation" onMouseDown={(event) => event.target === event.currentTarget && setPanel(null)}>
<section className="detail-panel" role="dialog" aria-modal="true" aria-labelledby="panel-title">
<button className="panel-close" type="button" aria-label="关闭" onClick={() => setPanel(null)}><Icon name="close" /></button>
{panel === 'channel' && <>
<p className="panel-kicker"></p>
<h2 id="panel-title"> AI </h2>
<p className="panel-intro">MCP AI 使 HoloLake </p>
<div className="connection-summary"><span className={`status-light ${connectionState.tone}`} /><div><b>{connectionState.label}</b><span>{connectionState.detail}</span></div></div>
{status.directConnectionCount > 0 ? <div className="session-guidance">
<b></b>
<span> AI 使</span>
</div> : status.resumableSessionCount > 0 ? <div className="session-guidance">
<b></b>
<span> AI 使HoloLake </span>
</div> : ticket ? <>
<button className="primary-action panel-action" type="button" onClick={() => void copyInvitation()}><Icon name="copy" /> AI</button>
<pre className="invitation-preview">{invitationText}</pre>
</> : <button className="primary-action panel-action" type="button" onClick={() => void issueInvitation()}><Icon name="arrow" /></button>}
{message && <p className="panel-message" aria-live="polite">{message}</p>}
<p className="boundary-note"></p>
</>}
{panel === 'receipts' && <>
<p className="panel-kicker"></p>
<h2 id="panel-title"></h2>
<p className="panel-intro">线</p>
{repositoryCandidate ? <div className="candidate-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span>HoloLake Git </span></div></div>
<dl className="candidate-facts">
<div><dt></dt><dd>{repositoryCandidate.inspection.personaId}</dd></div>
<div><dt></dt><dd>{repositoryCandidate.inspection.humanResponsibilitySubject}</dd></div>
<div><dt></dt><dd>{repositoryCandidate.inspection.gitHead.slice(0, 12)}</dd></div>
<div><dt></dt><dd>{repositoryCandidate.inspection.repositoryClean ? '干净' : '有未提交变化 · 不读取'}</dd></div>
<div><dt></dt><dd>{repositoryCandidate.inspection.currentCheckpoint.relativePath}</dd></div>
</dl>
<p></p>
<div className="candidate-actions"><button className="primary-action" type="button" disabled={repositoryBusy} onClick={() => void confirmRepository()}><Icon name="arrow" /></button><button className="text-action" type="button" disabled={repositoryBusy} onClick={() => setRepositoryCandidate(null)}></button></div>
</div> : <button className="repository-picker" type="button" disabled={repositoryBusy} onClick={() => void chooseRepository()}><span><b>{status.codeRepositoryMountCount > 0 ? '再绑定一个代码仓库' : '绑定代码通道仓库'}</b><small> macOS </small></span><Icon name="arrow" /></button>}
{repositoryMessage && <p className="panel-message" aria-live="polite">{repositoryMessage}</p>}
{receipts.length ? <ol className="receipt-list">{receipts.map((receipt) => <li key={receipt.sequence}><span>{receipt.sequence.toString().padStart(2, '0')}</span><div><b>{receipt.kind === 'REPOSITORY_BINDING_REVALIDATED' ? '仓库绑定已复核' : '远端对象读取已验证'}</b><small>{new Date(Number(receipt.observedAtUnixMs)).toLocaleString('zh-CN')} · {receipt.eventHash.slice(0, 12)}</small></div></li>)}</ol> : <div className="empty-state compact"><i /><b></b><span></span></div>}
</>}
{panel === 'updates' && <>
<p className="panel-kicker">广</p>
<h2 id="panel-title"></h2>
<p className="panel-intro">HoloLake </p>
{status.releaseRecoveryState === 'AWAITING_HUMAN_HEALTH_CONFIRMATION' ? <div className="recovery-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span></span></div></div>
<p></p>
<div className="recovery-actions"><button className="primary-action" type="button" disabled={releaseBusy} onClick={() => void resolveReleaseRecovery('keep')}></button><button className="secondary-action" type="button" disabled={releaseBusy} onClick={() => void resolveReleaseRecovery('rollback')}></button></div>
</div> : status.releaseRecoveryState === 'ROLLED_BACK_AWAITING_HUMAN_ACKNOWLEDGEMENT' ? <div className="recovery-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span></span></div></div>
<p>HoloLake 退</p>
<button className="primary-action panel-action" type="button" disabled={releaseBusy} onClick={() => void resolveReleaseRecovery('keep')}><Icon name="arrow" /></button>
</div> : ['INSTALL_FAILED_BACKUP_RETAINED', 'INSTALL_INTERRUPTED_BACKUP_RETAINED'].includes(status.releaseRecoveryState) ? <div className="recovery-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span></span></div></div>
<p> HoloLake </p>
<button className="primary-action panel-action" type="button" disabled={releaseBusy} onClick={() => void resolveReleaseRecovery('keep')}><Icon name="arrow" /></button>
</div> : ['INSTALL_REPLACED_AWAITING_MANUAL_RESTART', 'ROLLBACK_REPLACED_AWAITING_MANUAL_RESTART', 'LAST_KNOWN_GOOD_VERIFIED_INSTALLING'].includes(status.releaseRecoveryState) ? <div className="recovery-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span>HoloLake </span></div></div>
<p> HoloLake </p>
</div> : !releaseRecoveryIdle ? <div className="recovery-card">
<div className="candidate-heading"><span className="status-light waiting" /><div><b></b><span>使</span></div></div>
<p>HoloLake </p>
</div> : releaseCandidate ? <div className="release-card">
<div className="release-version"><span></span><b>{releaseCandidate.version}</b><small> {releaseCandidate.currentVersion}</small></div>
<p>{releaseCandidate.notes}</p>
<ul>{releaseCandidate.features.map((feature) => <li key={feature}>{feature}</li>)}{releaseCandidate.fixes.map((fix) => <li key={fix}>{fix}</li>)}</ul>
<dl className="candidate-facts">
<div><dt></dt><dd>{releaseCandidate.dataMigrationRequired ? '需要' : '不需要'}</dd></div>
<div><dt></dt><dd>{releaseCandidate.minimumVersion}</dd></div>
<div><dt></dt><dd></dd></div>
<div><dt></dt><dd>{releaseCandidate.rollbackSupported ? `支持 · ${releaseCandidate.previousVersion || '上一版本'}` : '不支持'}</dd></div>
</dl>
<p className="release-warning">广Tauri SHA-256</p>
<button className="primary-action panel-action" type="button" disabled={releaseBusy} onClick={() => void confirmUpdateInstall()}><Icon name="arrow" /></button>
</div> : <div className="update-state-card"><span className={`status-light ${status.updateState.startsWith('READY') ? 'ready' : 'quiet'}`} /><div><b>{status.updateState.startsWith('READY') ? '主控信任根已登记' : '更新网络保持关闭'}</b><span>{status.updateState.startsWith('READY') ? '可以由你手动检查签名广播' : '没有正式地址和公钥时,不发起网络请求'}</span></div></div>}
{!releaseCandidate && releaseRecoveryIdle && <button className="primary-action panel-action" type="button" disabled={releaseBusy} onClick={() => void checkUpdate()}>{releaseBusy ? '正在核对…' : '检查光湖更新'}<Icon name="arrow" /></button>}
{releaseMessage && <p className="panel-message" aria-live="polite">{releaseMessage}</p>}
<p className="boundary-note">退线</p>
</>}
{panel === 'settings' && <>
<p className="panel-kicker"></p>
<h2 id="panel-title"></h2>
<p className="panel-intro"></p>
<div className="theme-list">{themes.map((choice) => <button key={choice.id} type="button" className={theme === choice.id ? 'selected' : ''} onClick={() => setTheme(choice.id)}><i className={`theme-swatch ${choice.id}`} /><span><b>{choice.name}</b><small>{choice.id === 'night' ? '当前定版基色' : '五色湖定版主题'}</small></span><em>{theme === choice.id ? '已选择' : ''}</em></button>)}</div>
<p className="boundary-note"> HoloLake 广</p>
</>}
</section>
</div>}
</div>
)
}
createRoot(document.getElementById('root')!).render(
<StrictMode><FoundationScreen /></StrictMode>,
)
createRoot(document.getElementById('root')!).render(<StrictMode><HoloLakeApp /></StrictMode>)

View file

@ -1,16 +1,180 @@
* { box-sizing: border-box; }
:root {
font-family: ui-sans-serif, system-ui, sans-serif;
color: #eaf0ff;
background: #07101f;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "PingFang SC", "Microsoft YaHei UI", sans-serif;
color: var(--content-primary);
background: var(--surface-depth);
font-synthesis: none;
text-rendering: geometricPrecision;
}
body { margin: 0; min-width: 320px; min-height: 100vh; }
main { max-width: 720px; margin: 0 auto; padding: 12vh 32px; }
.eyebrow { color: #82a7ff; letter-spacing: .08em; text-transform: uppercase; }
h1 { font-size: clamp(2rem, 5vw, 4rem); line-height: 1.05; }
p { color: #b7c5e5; line-height: 1.7; }
dl { margin-top: 48px; border-top: 1px solid #263554; }
dl div { display: flex; justify-content: space-between; gap: 24px; padding: 16px 0; border-bottom: 1px solid #263554; }
dt { color: #8fa4cf; }
dd { margin: 0; text-align: right; }
body { margin: 0; min-width: 320px; min-height: 100vh; overflow: hidden; }
button { font: inherit; }
button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.world-shell {
position: relative;
min-height: 100vh;
overflow: hidden;
isolation: isolate;
background: linear-gradient(180deg, var(--surface-sky) 0%, var(--surface-horizon) 40%, var(--surface-lake) 65%, var(--surface-depth) 100%);
transition: background 450ms ease, color 300ms ease;
}
.scene-image {
position: absolute;
z-index: -4;
inset: 25% 0 0;
background-image: linear-gradient(180deg, transparent 0%, var(--surface-depth) 100%), url('./assets/hololake-night-lake.png');
background-size: cover;
background-position: center 42%;
opacity: var(--primitive-scene-opacity);
mix-blend-mode: screen;
transition: opacity 450ms ease;
}
.nebula { position: absolute; z-index: -3; border-radius: 50%; filter: blur(42px); pointer-events: none; }
.nebula-one { width: 58vw; height: 38vw; left: -12vw; top: -18vw; background: radial-gradient(circle, var(--primitive-nebula-a), transparent 68%); }
.nebula-two { width: 48vw; height: 32vw; right: -12vw; top: 4vh; background: radial-gradient(circle, var(--primitive-nebula-b), transparent 70%); }
.mist { position: absolute; z-index: -2; left: 0; right: 0; top: 38%; height: 36%; background: radial-gradient(ellipse at 50% 52%, var(--primitive-mist-a), var(--primitive-mist-b) 45%, transparent 72%); filter: blur(24px); pointer-events: none; }
.stars i, .glimmers i { position: absolute; display: block; border-radius: 50%; pointer-events: none; }
.stars i { width: calc(1px + (var(--star-index) % 3) * 1px); height: calc(1px + (var(--star-index) % 3) * 1px); left: calc(4% + (var(--star-index) * 5.2%)); top: calc(5% + (var(--star-index) % 5) * 5.4%); background: var(--accent-light); opacity: calc(.16 + (var(--star-index) % 4) * .09); box-shadow: 0 0 8px var(--primitive-warm-glow); animation: breathe calc(4s + (var(--star-index) % 5) * .7s) ease-in-out infinite; }
.glimmers i { width: calc(2px + (var(--glimmer-index) % 3) * 2px); height: 2px; left: calc(9% + (var(--glimmer-index) * 8.1%)); top: calc(52% + (var(--glimmer-index) % 4) * 6.4%); background: var(--accent-light); box-shadow: 0 0 12px var(--primitive-cool-glow); opacity: .18; animation: glimmer calc(5s + (var(--glimmer-index) % 4) * 1.2s) ease-in-out infinite; }
@keyframes breathe { 50% { opacity: .75; transform: scale(1.5); } }
@keyframes glimmer { 50% { opacity: .7; transform: translateX(16px) scaleX(2.4); } }
.world-header { height: 78px; display: flex; align-items: center; justify-content: space-between; padding: 0 clamp(28px, 5vw, 70px); }
.world-mark { display: flex; align-items: baseline; gap: 17px; }
.world-name { font-size: 21px; font-weight: 610; letter-spacing: .18em; }
.world-route { color: var(--content-muted); font-size: 12px; letter-spacing: .16em; }
.icon-button, .panel-close { border: 0; color: var(--content-muted); background: transparent; display: grid; place-items: center; cursor: pointer; border-radius: 50%; transition: color 180ms ease, background 180ms ease; }
.icon-button { width: 42px; height: 42px; }
.icon-button:hover, .panel-close:hover { color: var(--accent-light); background: var(--primitive-glass-hover); }
.icon-button svg, .panel-close svg, .hero-actions svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.45; stroke-linecap: round; stroke-linejoin: round; }
.home-content { min-height: calc(100vh - 156px); display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(285px, .45fr); align-items: center; gap: clamp(48px, 7vw, 112px); padding: 0 clamp(54px, 8vw, 126px) 28px; }
.hero { max-width: 680px; transform: translateY(-2vh); }
.route-kicker, .panel-kicker { margin: 0 0 21px; color: var(--accent-light); font-size: 11px; font-weight: 630; letter-spacing: .22em; }
.hero h1 { margin: 0; max-width: 700px; color: var(--content-primary); font-size: clamp(40px, 4.15vw, 58px); font-weight: 430; line-height: 1.22; letter-spacing: -.035em; text-wrap: balance; }
.hero-copy { max-width: 540px; margin: 27px 0 0; color: var(--content-muted); font-size: 14px; line-height: 1.9; letter-spacing: .035em; }
.hero-actions { display: flex; align-items: center; gap: 14px; margin-top: 37px; }
.primary-action, .secondary-action { min-height: 46px; border-radius: 999px; padding: 0 22px; display: inline-flex; align-items: center; justify-content: center; gap: 12px; cursor: pointer; transition: transform 180ms ease, background 180ms ease, box-shadow 180ms ease; }
.primary-action:disabled, .secondary-action:disabled, .repository-picker:disabled, .text-action:disabled { opacity: .55; cursor: wait; transform: none; }
.primary-action { border: 0; color: var(--button-primary-text); background: var(--button-primary-bg); box-shadow: var(--button-primary-shadow); font-weight: 620; white-space: nowrap; }
.secondary-action { color: var(--button-secondary-text); border: 1px solid var(--button-secondary-edge); background: var(--button-secondary-bg); backdrop-filter: blur(18px); }
.primary-action:hover, .secondary-action:hover { transform: translateY(-2px); }
.secondary-action:hover { background: var(--primitive-glass-hover); }
.status-cluster { display: grid; gap: 30px; width: min(100%, 330px); justify-self: end; }
.status-item { display: grid; grid-template-columns: 10px 1fr; align-items: start; gap: 15px; padding: 5px 0; }
.status-button { width: 100%; border: 0; color: inherit; background: transparent; text-align: left; cursor: pointer; }
.status-button:hover h2 { color: var(--accent-light); }
.status-item h2 { margin: 0 0 8px; color: var(--content-secondary); font-size: 13px; font-weight: 590; letter-spacing: .06em; }
.status-item p { margin: 0; color: var(--content-muted); opacity: .72; font-size: 11.5px; line-height: 1.5; letter-spacing: .04em; }
.status-light { width: 6px; height: 6px; margin-top: 5px; flex: 0 0 auto; border-radius: 50%; background: var(--state-quiet); box-shadow: 0 0 14px currentColor; }
.status-light.ready { color: var(--state-ready); background: currentColor; }
.status-light.waiting { color: var(--state-waiting); background: currentColor; }
.status-light.quiet { color: var(--state-quiet); background: currentColor; }
.world-footer { height: 78px; display: flex; align-items: center; justify-content: space-between; padding: 0 clamp(28px, 5vw, 70px); color: var(--content-faint); font-size: 10.5px; letter-spacing: .09em; }
.world-footer p { margin: 0; display: flex; align-items: center; gap: 13px; }
.world-footer b { color: var(--accent-light); font-size: 11.5px; letter-spacing: .14em; }
.trust-note i { width: 4px; height: 4px; border-radius: 50%; background: var(--accent-light); opacity: .65; }
.panel-backdrop { position: fixed; z-index: 20; inset: 0; display: flex; align-items: stretch; justify-content: flex-end; background: rgba(2, 4, 9, .28); backdrop-filter: blur(5px); }
.detail-panel { position: relative; width: min(500px, 92vw); height: 100%; overflow-y: auto; padding: 80px 54px 50px; color: var(--content-primary); background: var(--panel-bg); box-shadow: -26px 0 80px var(--primitive-shadow); backdrop-filter: blur(38px) saturate(115%); animation: panel-in 240ms ease-out; }
@keyframes panel-in { from { transform: translateX(24px); opacity: 0; } }
.panel-close { position: absolute; right: 34px; top: 30px; width: 38px; height: 38px; }
.detail-panel h2 { margin: 0; max-width: 380px; font-size: 31px; font-weight: 480; line-height: 1.28; letter-spacing: -.025em; }
.panel-intro { margin: 19px 0 30px; color: var(--content-muted); font-size: 13px; line-height: 1.85; }
.connection-summary { display: flex; align-items: flex-start; gap: 15px; margin: 32px 0; padding: 20px 22px; border-radius: 20px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.connection-summary div { display: grid; gap: 6px; }
.connection-summary b { color: var(--content-secondary); font-size: 13px; }
.connection-summary span { color: var(--content-faint); font-size: 11.5px; }
.session-guidance { display: grid; gap: 8px; margin: 0 0 20px; padding: 18px 20px; border: 1px solid var(--primitive-line); border-radius: 18px; background: rgba(255, 255, 255, .035); }
.session-guidance b { color: var(--content-secondary); font-size: 13px; }
.session-guidance span { color: var(--content-muted); font-size: 12px; line-height: 1.7; }
.panel-action { min-height: 46px; }
.panel-message { color: var(--content-muted); font-size: 11.5px; line-height: 1.6; }
.invitation-preview { max-height: 180px; overflow: auto; margin: 18px 0; padding: 17px; border-radius: 16px; color: var(--content-faint); background: rgba(0, 0, 0, .13); font: 10px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; word-break: break-all; }
.boundary-note { margin-top: 34px; color: var(--content-faint); font-size: 10.5px; line-height: 1.75; }
.receipt-list { list-style: none; display: grid; gap: 14px; margin: 30px 0 0; padding: 0; }
.receipt-list li { display: grid; grid-template-columns: 34px 1fr; gap: 14px; align-items: start; padding: 16px 18px; border-radius: 17px; background: var(--primitive-glass); }
.receipt-list li > span { color: var(--accent-light); font: 11px ui-monospace, monospace; }
.receipt-list li div { display: grid; gap: 7px; }
.receipt-list b { color: var(--content-secondary); font-size: 12px; font-weight: 580; }
.receipt-list small { color: var(--content-faint); font-size: 10px; }
.empty-state { min-height: 260px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.empty-state i { width: 7px; height: 7px; margin-bottom: 20px; border-radius: 50%; background: var(--accent-light); box-shadow: 0 0 32px 12px var(--primitive-warm-glow); }
.empty-state b { color: var(--content-secondary); font-size: 13px; font-weight: 570; }
.empty-state span { max-width: 260px; margin-top: 10px; color: var(--content-faint); font-size: 11px; line-height: 1.7; }
.empty-state.compact { min-height: 170px; }
.repository-picker { width: 100%; min-height: 74px; display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 13px 19px; border: 0; border-radius: 19px; color: var(--content-secondary); background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); text-align: left; cursor: pointer; }
.repository-picker:hover { background: var(--primitive-glass-hover); }
.repository-picker span { display: grid; gap: 6px; }
.repository-picker b { font-size: 12.5px; font-weight: 590; }
.repository-picker small { color: var(--content-faint); font-size: 10px; }
.repository-picker svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.45; stroke-linecap: round; stroke-linejoin: round; }
.candidate-card { margin-top: 26px; padding: 21px; border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.candidate-heading { display: flex; align-items: flex-start; gap: 14px; }
.candidate-heading div { display: grid; gap: 5px; }
.candidate-heading b { color: var(--content-secondary); font-size: 12.5px; }
.candidate-heading span { color: var(--content-faint); font-size: 10.5px; }
.candidate-facts { display: grid; gap: 10px; margin: 20px 0; }
.candidate-facts div { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 12px; }
.candidate-facts dt { color: var(--content-faint); font-size: 10px; }
.candidate-facts dd { margin: 0; overflow: hidden; color: var(--content-secondary); font: 10.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
.candidate-card > p { color: var(--content-faint); font-size: 10.5px; line-height: 1.7; }
.candidate-actions { display: flex; align-items: center; gap: 16px; margin-top: 18px; }
.candidate-actions .primary-action { min-height: 42px; padding-inline: 18px; font-size: 12px; }
.text-action { border: 0; color: var(--content-muted); background: transparent; font-size: 11px; cursor: pointer; }
.update-state-card { display: flex; align-items: flex-start; gap: 15px; margin: 30px 0; padding: 20px 22px; border-radius: 20px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.update-state-card div { display: grid; gap: 6px; }
.update-state-card b { color: var(--content-secondary); font-size: 12.5px; }
.update-state-card span { color: var(--content-faint); font-size: 10.5px; line-height: 1.6; }
.release-card { margin-top: 27px; }
.release-version { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 6px 16px; }
.release-version span { color: var(--content-faint); font-size: 10px; letter-spacing: .12em; }
.release-version b { grid-row: 1 / 3; grid-column: 2; color: var(--accent-light); font-size: 30px; font-weight: 480; }
.release-version small { color: var(--content-muted); font-size: 11px; }
.release-card > p { color: var(--content-muted); font-size: 11.5px; line-height: 1.7; }
.release-card ul { margin: 18px 0; padding-left: 18px; color: var(--content-secondary); font-size: 11px; line-height: 1.8; }
.release-card .release-warning { color: var(--content-faint); font-size: 10px; }
.recovery-card { display: grid; gap: 17px; margin-top: 27px; padding: 21px; border: 1px solid var(--primitive-line); border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.recovery-card > p { margin: 0; color: var(--content-muted); font-size: 11.5px; line-height: 1.7; }
.recovery-actions { display: flex; flex-wrap: wrap; gap: 11px; }
.theme-list { display: grid; gap: 9px; margin-top: 30px; }
.theme-list button { width: 100%; min-height: 66px; display: grid; grid-template-columns: 40px 1fr auto; align-items: center; gap: 14px; padding: 10px 14px; border: 0; border-radius: 17px; color: var(--content-secondary); background: transparent; text-align: left; cursor: pointer; }
.theme-list button:hover, .theme-list button.selected { background: var(--primitive-glass-hover); }
.theme-list span { display: grid; gap: 5px; }
.theme-list b { font-size: 12.5px; font-weight: 590; white-space: nowrap; }
.theme-list small, .theme-list em { color: var(--content-faint); font-size: 10px; font-style: normal; }
.theme-list button.selected em { color: var(--accent-light); }
.theme-swatch { width: 34px; height: 34px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(255,255,255,.18), 0 6px 18px var(--primitive-shadow); }
.theme-swatch.night { background: linear-gradient(145deg, #0a1020, #04070d 70%); }
.theme-swatch.dawn { background: linear-gradient(145deg, #f5f7fc, #d3e0ef 70%); }
.theme-swatch.nebula { background: linear-gradient(145deg, #281b47, #080510 70%); }
.theme-swatch.candle { background: linear-gradient(145deg, #4a2d13, #0a0703 70%); }
.theme-swatch.clear { background: linear-gradient(145deg, #f1f5f8, #b9ccda 70%); }
@media (max-width: 900px) {
.home-content { grid-template-columns: 1fr; align-content: center; gap: 50px; padding-inline: 62px; }
.hero { transform: none; }
.status-cluster { width: 100%; grid-template-columns: repeat(3, 1fr); justify-self: stretch; gap: 26px; }
}
@media (max-height: 640px) {
.world-header, .world-footer { height: 62px; }
.home-content { min-height: calc(100vh - 124px); }
.hero h1 { font-size: clamp(36px, 5vw, 56px); }
.hero-copy { margin-top: 18px; }
.hero-actions { margin-top: 25px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

View file

@ -0,0 +1,33 @@
# HoloLake 第一阶段桌面收束与能力审计
状态:`CURRENT_LOCAL_CONVERGENCE_CONTRACT · SOURCE_NOT_YET_PUBLISHED`
开发编号:`DEV-20260814-003`
机器投影:`routing/hololake-stage-one-desktop-convergence.json`
## 收束结论
HoloLake 桌面只保留一个未来正式产品主线:`product-source/hololake-native-desktop`,技术栈为 Tauri v2、Rust 和 React。桌面上后来出现的原生“第一阶段”应用是这条主线的本地候选验收壳不是第三条产品线也不代表第一阶段已经完成。
Electron 0.8.0 和旧 Tauri/平台实现不继续作为独立产品生长但不能直接删除。Electron 仍承载可验证的知识树、页面、搜索、历史、导入和本地 Git 知识数据;旧 Tauri/平台仍包含工程合同、测试和原生边界。它们只能作为只读供体和受保护迁移来源。
## 能力取舍
原生候选已经实现的签名壳、本机 broker/session、PNCC 只读投影、人类确认式仓库挂载、回执、更新与回滚底座继续保留。动态路由、发布广播和更新服务器下沉为受门控基础设施,不再占用第一阶段首页和开发优先级。
第一阶段当前缺失的主体按固定顺序实现:
1. 个人频道身份—任务—事件—回执内核;
2. 知识树、页面、搜索与本地持久化;
3. 人类审批中心与 Git 证据回读;
4. 小湖灯跨会话连续性投影;
5. 签名安装、人工选择更新、回滚与可逆数据迁移。
## 数据与退出门
旧应用和数据只有在物理清单、可恢复备份、逐文件哈希回读、可逆迁移演练、新主线回归测试、签名安装验收、人类确认清理范围和可恢复归档回执全部通过后,才可以退出当前机器。任何一步未知,删除和替换均为 0。
## 事实分层
本页和机器合同只证明收束判断已经写入本地源码。它不证明仓库发布、产品主体实现、制品构建、签名、公证、桌面安装、数据迁移、服务器部署或服务健康。每一层必须有独立回执。

View file

@ -0,0 +1,72 @@
{
"schema": "hololake.stage-one-desktop-convergence/v1",
"record_id": "HLP-STAGE-ONE-DESKTOP-CONVERGENCE-001",
"version": "2026-08-14.1",
"state": "CURRENT_LOCAL_CONVERGENCE_CONTRACT_SOURCE_NOT_YET_PUBLISHED",
"development_id": "DEV-20260814-003",
"architecture_page": "product-source/hololake-platform/architecture/HOLOLAKE-STAGE-ONE-DESKTOP-CONVERGENCE-AND-CAPABILITY-AUDIT-20260814.md",
"language_runtime_product_boundary": "product-source/hololake-native-desktop/contracts/language-runtime-product-update-boundary.json",
"installed_capability_audit": "product-source/hololake-native-desktop/audit/stage-one-installed-product-capability-audit.json",
"canonical_mainline": {
"source": "product-source/hololake-native-desktop",
"technology": "TAURI_V2_RUST_REACT",
"parallel_product_lines_allowed": false,
"installed_candidate_role": "LOCAL_ACCEPTANCE_CANDIDATE_NOT_SEPARATE_PRODUCT_LINE",
"formal_stage_one_product_complete": false
},
"donors": [
{
"source": "product-source/hololake-platform",
"role": "READ_ONLY_ENGINEERING_CONTRACT_TEST_AND_NATIVE_BOUNDARY_DONOR"
},
{
"source": "product-source/hololake-desktop",
"role": "READ_ONLY_KNOWLEDGE_UX_AND_BEHAVIOR_DONOR"
},
{
"source": "product-source/guanghu-knowledge-base",
"role": "READ_ONLY_KNOWLEDGE_UX_BEHAVIOR_AND_PROTECTED_DATA_DONOR"
}
],
"keep_foundation": [
"SIGNED_NATIVE_SHELL",
"LOCAL_BROKER_AND_RESUMABLE_SESSION",
"PNCC_READ_ONLY_PROJECTION",
"HUMAN_CONFIRMED_REPOSITORY_MOUNT",
"RECEIPTS",
"FAIL_CLOSED_UPDATE_AND_ROLLBACK"
],
"under_lake_foundation_not_product_completion": [
"DYNAMIC_CAPABILITY_ROUTING",
"RELEASE_BROADCAST",
"RELEASE_ACTIVATION_OPERATOR",
"UPDATE_SERVER_FOUNDATION"
],
"stage_one_implementation_order": [
"PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
"KNOWLEDGE_TREE_PAGE_SEARCH_AND_LOCAL_PERSISTENCE",
"HUMAN_APPROVAL_CENTER_AND_GIT_EVIDENCE_READBACK",
"LAKE_LAMP_CROSS_SESSION_CONTINUITY_PROJECTION",
"SIGNED_INSTALL_UPDATE_ROLLBACK_AND_REVERSIBLE_DATA_MIGRATION"
],
"donor_exit_requires": [
"PHYSICAL_USER_DATA_INVENTORY",
"RECOVERABLE_BACKUP",
"FILE_BY_FILE_HASH_READBACK",
"REVERSIBLE_DATA_MIGRATION_REHEARSAL",
"NEW_TAURI_REGRESSION_TESTS",
"SIGNED_INSTALLED_RUNTIME_ACCEPTANCE",
"HUMAN_VISIBLE_CLEANUP_SCOPE_CONFIRMATION",
"RECOVERABLE_ARCHIVE_RECEIPT"
],
"actions": {
"application_deletion": "NOT_AUTHORIZED_NOT_EXECUTED",
"application_replacement": "NOT_AUTHORIZED_NOT_EXECUTED",
"user_data_migration": "NOT_EXECUTED",
"repository_publication": "NOT_YET_PUBLISHED",
"desktop_artifact_build": "NOT_EXECUTED_FOR_THIS_CONTRACT",
"desktop_installation": "NOT_EXECUTED_FOR_THIS_CONTRACT",
"server_deployment": "NOT_EXECUTED_FOR_THIS_CONTRACT"
},
"truth_policy": "ARCHITECTURE_SOURCE_ARTIFACT_SIGNATURE_INSTALLATION_DATA_MIGRATION_DEPLOYMENT_AND_HEALTH_REQUIRE_SEPARATE_RECEIPTS"
}

View file

@ -0,0 +1,75 @@
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 readJson = (relative) =>
JSON.parse(fs.readFileSync(path.join(root, relative), "utf8"));
const convergence = readJson("routing/hololake-stage-one-desktop-convergence.json");
const foundation = readJson("product-source/hololake-native-desktop/foundation.json");
const boundary = readJson(
"product-source/hololake-native-desktop/contracts/language-runtime-product-update-boundary.json",
);
const audit = readJson(
"product-source/hololake-native-desktop/audit/stage-one-installed-product-capability-audit.json",
);
test("one Tauri mainline absorbs the candidate without creating a third product line", () => {
assert.equal(convergence.canonical_mainline.source, "product-source/hololake-native-desktop");
assert.equal(convergence.canonical_mainline.technology, "TAURI_V2_RUST_REACT");
assert.equal(convergence.canonical_mainline.parallel_product_lines_allowed, false);
assert.equal(convergence.canonical_mainline.formal_stage_one_product_complete, false);
assert.equal(foundation.parallel_product_line_created, false);
assert.equal(foundation.stage_one_visible_body_state, "NOT_IMPLEMENTED");
});
test("Electron and legacy sources remain protected donors until reversible migration passes", () => {
assert.ok(
convergence.donors.some(
(donor) => donor.source === "product-source/hololake-desktop" && donor.role.includes("READ_ONLY"),
),
);
assert.ok(convergence.donor_exit_requires.includes("FILE_BY_FILE_HASH_READBACK"));
assert.ok(convergence.donor_exit_requires.includes("REVERSIBLE_DATA_MIGRATION_REHEARSAL"));
assert.equal(audit.deletion_allowed, false);
assert.equal(audit.replacement_allowed, false);
assert.equal(audit.data_migration_executed, false);
});
test("stage-one visible body precedes routing and release infrastructure", () => {
assert.equal(
convergence.stage_one_implementation_order[0],
"PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL",
);
assert.equal(
convergence.stage_one_implementation_order[1],
"KNOWLEDGE_TREE_PAGE_SEARCH_AND_LOCAL_PERSISTENCE",
);
assert.ok(
convergence.under_lake_foundation_not_product_completion.includes(
"DYNAMIC_CAPABILITY_ROUTING",
),
);
assert.equal(foundation.supporting_infrastructure_role, "UNDER_LAKE_FOUNDATION_NOT_PRODUCT_COMPLETION");
});
test("language runtime and product engineering stay separate without sharing a formed private persona", () => {
assert.equal(boundary.upstream.protocol, "GLS-0263");
assert.equal(boundary.language_runtime_channel.private_formed_persona_shared, false);
assert.equal(boundary.product_engineering_channel.may_rewrite_zero_core, false);
assert.equal(boundary.product_engineering_channel.may_read_or_publish_private_persona_memory, false);
assert.equal(boundary.human_impact_gate.visible_confirmable_rejectable_reversible_receipted, true);
assert.equal(boundary.automatic_authority_granted, false);
});
test("public contracts contain no machine-private absolute paths or personal content", () => {
for (const value of [convergence, foundation, boundary, audit]) {
const serialized = JSON.stringify(value);
assert.equal(serialized.includes("/Users/"), false);
assert.equal(serialized.includes("/Volumes/"), false);
assert.equal(serialized.includes("bingshuolingdianyuanhe"), false);
}
});