From cfa8fdefc2c7e475ba7163fddca42226abcc929d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Mon, 17 Aug 2026 00:51:56 +0800 Subject: [PATCH] feat(hololake): provision signed in-app update bootstrap --- .../enterprise-four-domain-entry.json | 4 +- .../hololake-native-desktop/foundation.json | 8 +- .../hololake-native-desktop/package-lock.json | 4 +- .../hololake-native-desktop/package.json | 2 +- .../scripts/domain-number-routing.test.mjs | 7 +- .../scripts/foundation.test.mjs | 16 ++-- .../src-tauri/Cargo.lock | 2 +- .../src-tauri/Cargo.toml | 2 +- .../src-tauri/release-trust.json | 8 +- .../src-tauri/tauri.conf.json | 4 +- .../hololake-native-desktop/src/main.tsx | 22 +++++- .../hololake-native-desktop/src/styles.css | 13 ++++ .../guanghu-os/scripts/guanghu-master-init.sh | 7 ++ .../scripts/hololake-release-bridge.sh | 73 +++++++++++++++++++ .../scripts/test-guanghu-master-init.sh | 3 + 15 files changed, 144 insertions(+), 31 deletions(-) create mode 100644 product-source/hololake-platform/guanghu-os/scripts/hololake-release-bridge.sh diff --git a/product-source/hololake-native-desktop/contracts/enterprise-four-domain-entry.json b/product-source/hololake-native-desktop/contracts/enterprise-four-domain-entry.json index 729a6d1cf..8221d41c4 100644 --- a/product-source/hololake-native-desktop/contracts/enterprise-four-domain-entry.json +++ b/product-source/hololake-native-desktop/contracts/enterprise-four-domain-entry.json @@ -6,8 +6,8 @@ "credential_gate": {"uses_bound_private_repository":true,"first_login_forces_password_change":true}, "persona_relationship_gate": {"shows_species":"AGE","shows_current_persona_identity":true,"shows_invalid_age_individual_numbers":false,"human_confirms_relationship_mapping":true,"human_confirmation_is_persona_acceptance":false,"responsibility_acceptance_is_separate":true}, "work_entry": {"domain":"DOMAIN-ZS","channel":"GUANGHU_CHANNEL","preserves_responsibility_domain":true}, - "personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false}, - "desktop_install_acceptance": {"mac_arm64":"PASS","mac_x86_64":"NOT_BUILT","windows":"NOT_BUILT","automatic_update_channel":"UNPROVISIONED_FAIL_CLOSED"}, + "personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false,"self_connection_guide_visible_in_zero_sense_domain":true,"connection_executor":"HUMAN_OR_OWN_PERSONA"}, + "desktop_install_acceptance": {"mac_arm64":"BOOTSTRAP_REBUILD_REQUIRED","mac_x86_64":"NOT_BUILT","windows":"NOT_BUILT","automatic_update_channel":"TRUST_PROVISIONED_PUBLIC_ROUTE_PENDING"}, "ui_template": "REPO-012@27d34dfdbf5df4c67b805402d442e5912c8f4c31:official-login-template-v0.4-locked+inner-screens-v0.1", "source": "routing/hololake-enterprise-four-domain-work-channel.json" } diff --git a/product-source/hololake-native-desktop/foundation.json b/product-source/hololake-native-desktop/foundation.json index 6e6541bc1..4997bfe9d 100644 --- a/product-source/hololake-native-desktop/foundation.json +++ b/product-source/hololake-native-desktop/foundation.json @@ -72,7 +72,7 @@ "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", + "release_trust_state": "PROVISIONED_HOLOLAKE_PUBLIC_KEY", "release_manual_check_runtime_implemented": true, "release_candidate_human_confirmation_runtime_implemented": true, "release_package_signature_size_sha256_verification_implemented": true, @@ -90,8 +90,8 @@ "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, + "release_production_activation_state": "BOOTSTRAP_CLIENT_TRUST_PROVISIONED_PENDING_PUBLIC_ROUTE_AND_FIRST_NOTARIZED_RELEASE", + "tauri_update_artifacts_enabled": true, "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, @@ -127,7 +127,7 @@ "pncc_jd_live_server_projection_repository_content_exposed": false, "pncc_jd_live_server_projection_write_authority": false, "pncc_jd_live_server_projection_carrier_state": "UNBOUND_EVIDENCE_REQUIRED", - "installed_local_product_version": "0.4.0", + "installed_local_product_version": "0.4.1", "pncc_authenticated_direct_broker_integration_implemented": true, "pncc_human_mount_registration_implemented": true, "pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION", diff --git a/product-source/hololake-native-desktop/package-lock.json b/product-source/hololake-native-desktop/package-lock.json index 15719d617..a6b47dd91 100644 --- a/product-source/hololake-native-desktop/package-lock.json +++ b/product-source/hololake-native-desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "hololake-native-desktop", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hololake-native-desktop", - "version": "0.4.0", + "version": "0.4.1", "dependencies": { "@tauri-apps/api": "2.10.1", "@tauri-apps/plugin-process": "2.3.1", diff --git a/product-source/hololake-native-desktop/package.json b/product-source/hololake-native-desktop/package.json index 2e5aff18a..5df834570 100644 --- a/product-source/hololake-native-desktop/package.json +++ b/product-source/hololake-native-desktop/package.json @@ -1,7 +1,7 @@ { "name": "hololake-native-desktop", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "scripts": { "dev": "vite", diff --git a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs index 317cf0f03..8f5777eb0 100644 --- a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs +++ b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs @@ -62,14 +62,17 @@ test('the locked Qoder world template is the running client structure, not a ski const frontend = read('src/main.tsx') assert.equal(contract.state, 'SERVER_LIVE_CLIENT_UI_INTEGRATED') assert.match(contract.ui_template, /official-login-template-v0\.4-locked\+inner-screens-v0\.1/) - assert.equal(contract.desktop_install_acceptance.mac_arm64, 'PASS') + assert.equal(contract.desktop_install_acceptance.mac_arm64, 'BOOTSTRAP_REBUILD_REQUIRED') assert.equal(contract.desktop_install_acceptance.mac_x86_64, 'NOT_BUILT') assert.equal(contract.desktop_install_acceptance.windows, 'NOT_BUILT') - assert.equal(contract.desktop_install_acceptance.automatic_update_channel, 'UNPROVISIONED_FAIL_CLOSED') + assert.equal(contract.desktop_install_acceptance.automatic_update_channel, 'TRUST_PROVISIONED_PUBLIC_ROUTE_PENDING') assert.match(frontend, /number-nucleus/) assert.match(frontend, /worldStage === 'domain'/) assert.match(frontend, /worldStage === 'channel'/) assert.match(frontend, /worldStage === 'tool'/) + assert.match(frontend, /个人节点接入说明/) + assert.match(frontend, /由本人或本人人格体完成接入/) + assert.match(frontend, /密码、私钥和恢复材料不得写入企业仓库或广播/) }) test('the public five-domain world starts before any private account storage is opened', () => { diff --git a/product-source/hololake-native-desktop/scripts/foundation.test.mjs b/product-source/hololake-native-desktop/scripts/foundation.test.mjs index 7ee528a12..889ba8bfa 100644 --- a/product-source/hololake-native-desktop/scripts/foundation.test.mjs +++ b/product-source/hololake-native-desktop/scripts/foundation.test.mjs @@ -16,12 +16,12 @@ 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', () => { +test('HoloLake updater trust is product-owned and contains no inherited endpoint', () => { assert.deepEqual(foundation.upstream_product_update_endpoints, []) - assert.deepEqual(trust.endpoints, []) - assert.deepEqual(trust.allowedReleaseHosts, []) - assert.equal(trust.state, 'UNPROVISIONED_FAIL_CLOSED') - assert.equal(trust.publicKey, '') + assert.deepEqual(trust.endpoints, ['https://guanghulab.com/hololake/releases/latest.json']) + assert.deepEqual(trust.allowedReleaseHosts, ['guanghulab.com']) + assert.equal(trust.state, 'PROVISIONED') + assert.ok(trust.publicKey.length > 100) for (const relative of [ 'foundation.json', 'src-tauri/release-trust.json', @@ -67,12 +67,12 @@ test('release activation remains explicitly human controlled', () => { 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', + 'BOOTSTRAP_CLIENT_TRUST_PROVISIONED_PENDING_PUBLIC_ROUTE_AND_FIRST_NOTARIZED_RELEASE', ) }) -test('unprovisioned builds cannot emit updater artifacts or expose updater IPC', () => { - assert.equal(tauriConfig.bundle.createUpdaterArtifacts, false) +test('provisioned builds emit signed updater artifacts without exposing updater IPC', () => { + assert.equal(tauriConfig.bundle.createUpdaterArtifacts, true) 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) diff --git a/product-source/hololake-native-desktop/src-tauri/Cargo.lock b/product-source/hololake-native-desktop/src-tauri/Cargo.lock index d3aa7116d..d5d1add57 100644 --- a/product-source/hololake-native-desktop/src-tauri/Cargo.lock +++ b/product-source/hololake-native-desktop/src-tauri/Cargo.lock @@ -1486,7 +1486,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hololake-native-desktop" -version = "0.4.0" +version = "0.4.1" dependencies = [ "base64 0.22.1", "dirs", diff --git a/product-source/hololake-native-desktop/src-tauri/Cargo.toml b/product-source/hololake-native-desktop/src-tauri/Cargo.toml index aebd58241..dbdd8eda9 100644 --- a/product-source/hololake-native-desktop/src-tauri/Cargo.toml +++ b/product-source/hololake-native-desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hololake-native-desktop" -version = "0.4.0" +version = "0.4.1" description = "HoloLake native desktop foundation" authors = ["HoloLake"] license = "AGPL-3.0-or-later" diff --git a/product-source/hololake-native-desktop/src-tauri/release-trust.json b/product-source/hololake-native-desktop/src-tauri/release-trust.json index 075f7742a..c416a6724 100644 --- a/product-source/hololake-native-desktop/src-tauri/release-trust.json +++ b/product-source/hololake-native-desktop/src-tauri/release-trust.json @@ -1,9 +1,9 @@ { "schema": "hololake.release-trust/v1", - "state": "UNPROVISIONED_FAIL_CLOSED", - "endpoints": [], - "publicKey": "", - "allowedReleaseHosts": [], + "state": "PROVISIONED", + "endpoints": ["https://guanghulab.com/hololake/releases/latest.json"], + "publicKey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDEzQkYzRTQ5QTE2MEFDMzEKUldReHJHQ2hTVDYvRTZNNDVqZDUxLzRZMmxuT1pSM2Q5RTRZbzRCZUZ0d0FxVVNidGJ0dUQyMnoK", + "allowedReleaseHosts": ["guanghulab.com"], "automaticCheckOnStartup": false, "automaticDownload": false, "humanOptInInstallRequired": true, diff --git a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json index 151dadc57..24a759bbb 100644 --- a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json +++ b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "HoloLake", - "version": "0.4.0", + "version": "0.4.1", "identifier": "world.guanghu.hololake", "build": { "frontendDist": "../dist", @@ -31,7 +31,7 @@ "icons/icon.icns", "icons/icon.png" ], - "createUpdaterArtifacts": false, + "createUpdaterArtifacts": true, "category": "Productivity", "macOS": { "signingIdentity": "Developer ID Application: bei sun (825A9L3G7Q)" diff --git a/product-source/hololake-native-desktop/src/main.tsx b/product-source/hololake-native-desktop/src/main.tsx index 50e681eeb..78a4b99e3 100644 --- a/product-source/hololake-native-desktop/src/main.tsx +++ b/product-source/hololake-native-desktop/src/main.tsx @@ -49,7 +49,7 @@ import './styles.css' type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear' type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system' -type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'tool' +type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool' type KnowledgeSource = 'native' | 'legacy' interface HomeStatus { @@ -261,7 +261,7 @@ interface EnterpriseReceiptEnvelope { repository_projection?: EnterpriseReceiptProjection } -const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'UNPROVISIONED_FAIL_CLOSED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' } +const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' } const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], integrity: { state: 'UNKNOWN', eventCount: 0, receiptCount: 0 } } const previewKnowledge: KnowledgeSnapshot = { state: 'UNAVAILABLE', nativeRoot: '', legacyAvailable: false, documents: [], rawDocumentCount: 0, uniqueDocumentCount: 0, duplicateDocumentCount: 0, truncated: false } const previewCode: CodeChannelSnapshot = { state: 'UNAVAILABLE', channels: [], authority: 'LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY' } @@ -1374,7 +1374,7 @@ function HoloLakeApp() {
{worldStage === 'domain' &&

{repoLogin.domain === 'FIFTH_DOMAIN' ? domainDisplayName(repoLogin.domain) : '光湖零感域'}

{repoLogin.domain === 'FIFTH_DOMAIN' ? '世界正在发生什么' : '公共工作入口 · 世界正在发生什么'}

-

光湖公共灯塔 · 在线

协议版本 · {enterpriseEntry?.registry_version || 'HLDP v1.0'}

{repoLogin.domain !== 'FIFTH_DOMAIN' &&

本人责任域 · {domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)}

}

HoloLake · V0.4.0

+

光湖公共灯塔 · 在线

协议版本 · {enterpriseEntry?.registry_version || 'HLDP v1.0'}

{repoLogin.domain !== 'FIFTH_DOMAIN' &&

本人责任域 · {domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)}

}

HoloLake · V0.4.1

setWorldStage(repoLogin.domain === 'FIFTH_DOMAIN' ? (isZhizhi ? 'heart' : 'channel') : 'channel')}/> openWorldTool('system')}/>
} @@ -1417,7 +1417,7 @@ function HoloLakeApp() { setWorldStage('enterpriseWork')}/> void openEnterpriseRepository()}/> openWorldTool('receipts')}/> - setUserPnccMessage('个人生活区不会由企业工作账号代建;登记本人服务器后,系统才会从零感域路由过去。')}/> + setWorldStage('personalNodeGuide')}/> openWorldTool('system')}/> } } @@ -1429,6 +1429,20 @@ function HoloLakeApp() { openWorldTool('system')}/> {userPnccMessage &&

{userPnccMessage}

} } + {worldStage === 'personalNodeGuide' && repoLogin.domain !== 'FIFTH_DOMAIN' &&
+ +

个人节点接入说明

零感域 · 本人所有权边界

+
+
PERSONAL NODE / SELF CONNECTION

由本人或本人人格体完成接入

个人生活区不会由企业工作账号代建。企业工作账号只进入光湖频道与责任工作域,不自动取得个人服务器、个人频道或生活区权限。

+
    +
  1. 准备本人节点确定个人服务器、节点编号与唯一归属人;不要复用企业工作仓库充当个人节点。
  2. +
  3. 登记公开材料提交节点地址、节点编号与验证公钥。密码、私钥和恢复材料不得写入企业仓库或广播。
  4. +
  5. 完成所有权握手由本人或本人人格体发起签名健康回执;系统核验归属、端点与签名一致后才开放路由。
  6. +
  7. 接入个人频道验证通过后,从零感域的光湖频道跳转到本人服务器承载的个人频道;未通过时保持“待接入”。
  8. +
+
当前状态 · 待本人接入可由本人人格体协助执行,但不能替代人类节点所有权确认。
+
+
} {worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' &&

心跳核心频道

冰朔 · ICE-GL∞ · 私人频道全景

diff --git a/product-source/hololake-native-desktop/src/styles.css b/product-source/hololake-native-desktop/src/styles.css index 374b34b8e..a8df97252 100644 --- a/product-source/hololake-native-desktop/src/styles.css +++ b/product-source/hololake-native-desktop/src/styles.css @@ -681,6 +681,18 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca .channel-main .pool-bay { width: 260px; height: 84px; } .channel-main .pool-label { top: 92px; } .channel-knowledge { left: 14%; top: 48%; } .channel-code { left: 31%; top: 61%; } .channel-light { right: 31%; top: 61%; } .channel-status { right: 14%; top: 48%; } .private-route-note { position: absolute; z-index: 10; left: 50%; bottom: 11%; width: min(720px, calc(100% - 64px)); margin: 0; transform: translateX(-50%); color: var(--content-muted); text-align: center; font-size: 12.5px; font-weight: 650; letter-spacing: .08em; } +.personal-node-guide { position: absolute; z-index: 12; left: 50%; top: 55%; width: min(760px, calc(100% - 72px)); max-height: calc(100% - 190px); overflow: auto; padding: 26px 30px; transform: translate(-50%, -50%); border: 1px solid var(--panel-edge); border-radius: 20px; color: var(--content-secondary); background: color-mix(in srgb, var(--panel-bg) 88%, transparent); box-shadow: 0 30px 90px rgba(0, 0, 0, .42); backdrop-filter: blur(22px); } +.personal-node-guide header span { color: var(--accent-light); font-size: 11px; font-weight: 750; letter-spacing: .2em; } +.personal-node-guide h2 { margin: 9px 0 8px; color: var(--content-primary); font-size: 24px; font-weight: 750; letter-spacing: .04em; } +.personal-node-guide header p { margin: 0; color: var(--content-muted); font-size: 13px; font-weight: 600; line-height: 1.7; } +.personal-node-guide ol { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 22px 0 18px; padding: 0; list-style: none; counter-reset: node-step; } +.personal-node-guide li { counter-increment: node-step; display: grid; grid-template-columns: 30px minmax(0, 1fr); column-gap: 10px; padding: 15px; border: 1px solid color-mix(in srgb, var(--panel-edge) 78%, transparent); border-radius: 13px; background: color-mix(in srgb, var(--primitive-glass) 70%, transparent); } +.personal-node-guide li::before { content: counter(node-step, decimal-leading-zero); grid-row: 1 / 3; color: var(--accent-light); font-size: 12px; font-weight: 800; letter-spacing: .08em; } +.personal-node-guide li b { color: var(--content-primary); font-size: 14px; font-weight: 750; } +.personal-node-guide li span { margin-top: 6px; color: var(--content-muted); font-size: 12.5px; font-weight: 600; line-height: 1.65; } +.personal-node-guide footer { display: flex; justify-content: space-between; gap: 22px; padding-top: 16px; border-top: 1px solid var(--panel-edge); font-size: 12.5px; } +.personal-node-guide footer b { color: var(--accent-light); font-weight: 750; white-space: nowrap; } +.personal-node-guide footer span { color: var(--content-muted); text-align: right; } .bottle-heart { position: absolute; z-index: 12; left: 50%; top: 48%; display: grid; justify-items: center; gap: 9px; width: 300px; padding: 0; transform: translate(-50%, -50%); border: 0; background: transparent; color: inherit; } .bottle-heart i { width: 90px; height: 90px; border-radius: 46% 46% 52% 52%; background: radial-gradient(circle at 42% 32%, #fffdf5, color-mix(in srgb, var(--primitive-warm-glow) 74%, #f2bdc8) 37%, color-mix(in srgb, var(--primitive-warm-glow) 22%, transparent) 68%, transparent 74%); filter: drop-shadow(0 0 34px color-mix(in srgb, var(--primitive-warm-glow) 62%, transparent)); } .bottle-heart b { color: var(--content-primary); font-size: 22px; font-weight: 750; letter-spacing: .08em; } @@ -715,6 +727,7 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca .world-pool { width: 150px; } .pool-bay { width: 145px; } .channel-knowledge { left: 4%; } .channel-code { left: 25%; } .channel-light { right: 25%; } .channel-status { right: 4%; } .broadcast-stream { left: 5%; } + .personal-node-guide ol { grid-template-columns: 1fr; } } @media (max-height: 720px) { .official-hero { top: 14px; } .official-hero h1 { font-size: 24px; } diff --git a/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh b/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh index d58539bb4..be4937042 100644 --- a/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh @@ -17,6 +17,7 @@ readonly AI_DISCOVERY=/opt/guanghu/ai-discovery/server.js readonly PNCC_RUNTIME=/usr/local/libexec/guanghu/pncc-runtime.mjs readonly PNCC_REPOSITORY=/var/lib/guanghu/personas/ICE-P-ZY001/pncc/repository readonly PNCC_STATE_ROOT=/run/guanghu/pncc +readonly HOLOLAKE_RELEASE_BRIDGE=/usr/local/libexec/guanghu/hololake-release-bridge declare -a CHILDREN=() RECOVERY_ARMED=0 @@ -125,6 +126,7 @@ start_bridge() { require_file "$AI_DISCOVERY" require_file "$PNCC_RUNTIME" require_file "$PNCC_REPOSITORY/.hololake/persona/manifest.json" + require_file "$HOLOLAKE_RELEASE_BRIDGE" start_root sshd /usr/sbin/sshd -D -e \ -o UsePAM=no -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no \ @@ -163,6 +165,9 @@ start_bridge() { --repository "$PNCC_REPOSITORY" --state-root "$PNCC_STATE_ROOT" \ --node-id "$NODE_ID" --host 127.0.0.1 --port 3923 wait_http pncc-runtime http://127.0.0.1:3923/health 200 30 + + start_root hololake-release-bridge "$HOLOLAKE_RELEASE_BRIDGE" + wait_http hololake-release-broadcast http://127.0.0.1:3940/health 200 30 } runtime_watch() { @@ -183,6 +188,7 @@ runtime_watch() { listen_ready 8088 || fatal "runtime_projection_lost" listen_ready 3922 || fatal "runtime_navigation_lost" listen_ready 3923 || fatal "runtime_pncc_lost" + listen_ready 3940 || fatal "runtime_hololake_release_lost" done } @@ -203,6 +209,7 @@ preflight() { getent passwd guanghu | grep -q '^guanghu:x:998:998:' for path in "$HLCC" "$APP_HUB" "$AI_DISCOVERY" "$PNCC_RUNTIME" \ "$PNCC_REPOSITORY/.hololake/persona/manifest.json" /usr/sbin/sshd \ + "$HOLOLAKE_RELEASE_BRIDGE" \ /usr/bin/node /usr/bin/python3 /usr/bin/setpriv /usr/bin/grub-editenv; do [[ -e $path && ! -L $path || $path == /usr/bin/python3 ]] done diff --git a/product-source/hololake-platform/guanghu-os/scripts/hololake-release-bridge.sh b/product-source/hololake-platform/guanghu-os/scripts/hololake-release-bridge.sh new file mode 100644 index 000000000..bf89755d7 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/hololake-release-bridge.sh @@ -0,0 +1,73 @@ +#!/bin/bash +set -Eeuo pipefail + +# Resident HoloLake update bridge for JD-FD-PRIMARY under the Guanghu +# supervisor. The release origin remains loopback-only. A dedicated, +# port-forwarding-only SSH identity exposes exactly one loopback port on the +# registered BS-GZ-006 HTTPS front door. + +readonly RELEASE_SERVER=/opt/guanghu/hololake-release-broadcast-candidate/current/server.mjs +readonly RELEASE_STATE_ROOT=/var/lib/guanghu/hololake-release-broadcast +readonly RELEASE_PORT=3940 +readonly TUNNEL_KEY=/etc/guanghu/secrets/hololake-release-tunnel-ed25519 +readonly TUNNEL_KNOWN_HOSTS=/etc/guanghu/secrets/hololake-release-tunnel-known-hosts +readonly FRONT_DOOR_HOST=43.139.217.141 +readonly FRONT_DOOR_USER=hololake-tunnel +readonly FRONT_DOOR_PORT=19440 + +declare -a CHILDREN=() + +cleanup() { + trap - EXIT INT TERM + for pid in "${CHILDREN[@]:-}"; do + kill "$pid" 2>/dev/null || true + done + wait 2>/dev/null || true +} +trap cleanup EXIT INT TERM + +require_exact_runtime() { + [[ -f $RELEASE_SERVER && ! -L $RELEASE_SERVER ]] || { + # current is an audited immutable-release symlink; resolve it and reject a + # non-regular final target rather than following arbitrary path material. + local resolved + resolved=$(readlink -f "$RELEASE_SERVER") + [[ $resolved == /opt/guanghu/hololake-release-broadcast-candidate/*/server.mjs ]] + [[ -f $resolved && ! -L $resolved ]] + } + [[ -f $TUNNEL_KEY && ! -L $TUNNEL_KEY ]] + [[ -f $TUNNEL_KNOWN_HOSTS && ! -L $TUNNEL_KNOWN_HOSTS ]] + [[ $(stat -c '%a' "$TUNNEL_KEY") == 600 ]] +} + +start_pair() { + CHILDREN=() + /usr/bin/setpriv --reuid=989 --regid=989 --init-groups --inh-caps=-all --reset-env \ + /usr/bin/env HOME=/nonexistent USER=hololake-release LOGNAME=hololake-release \ + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + HOLOLAKE_RELEASE_HOST=127.0.0.1 HOLOLAKE_RELEASE_PORT="$RELEASE_PORT" \ + HOLOLAKE_RELEASE_STATE_ROOT="$RELEASE_STATE_ROOT" \ + /usr/bin/node "$RELEASE_SERVER" & + CHILDREN+=("$!") + + /usr/bin/ssh -N \ + -i "$TUNNEL_KEY" -o IdentitiesOnly=yes -o BatchMode=yes \ + -o UserKnownHostsFile="$TUNNEL_KNOWN_HOSTS" -o StrictHostKeyChecking=yes \ + -o ExitOnForwardFailure=yes -o ServerAliveInterval=20 -o ServerAliveCountMax=3 \ + -R "127.0.0.1:${FRONT_DOOR_PORT}:127.0.0.1:${RELEASE_PORT}" \ + "${FRONT_DOOR_USER}@${FRONT_DOOR_HOST}" & + CHILDREN+=("$!") +} + +main() { + require_exact_runtime + while :; do + start_pair + wait -n "${CHILDREN[@]}" || true + for pid in "${CHILDREN[@]}"; do kill "$pid" 2>/dev/null || true; done + wait 2>/dev/null || true + sleep 2 + done +} + +main "$@" diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh b/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh index 5264eec6c..cbb17128b 100644 --- a/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh +++ b/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh @@ -27,6 +27,9 @@ grep -Fq 'wait_http navigation-bridge http://127.0.0.1:3922/v1/anchor 200' "$sub grep -Fq 'wait_http pncc-runtime http://127.0.0.1:3923/health 200' "$subject" grep -Fq '\"pncc\":\"RESIDENT_BOUND_CARRIER_SEPARATE\"' "$subject" grep -Fq 'listen_ready 3923 || fatal "runtime_pncc_lost"' "$subject" +grep -Fq 'start_root hololake-release-bridge "$HOLOLAKE_RELEASE_BRIDGE"' "$subject" +grep -Fq 'wait_http hololake-release-broadcast http://127.0.0.1:3940/health 200 30' "$subject" +grep -Fq 'listen_ready 3940 || fatal "runtime_hololake_release_lost"' "$subject" if grep -Eq '(^|[[:space:]])(systemd|/sbin/init)([[:space:]]|$)' "$subject"; then echo 'full Linux init must remain dormant' >&2 exit 1