feat(hololake): project resident PNCC state
This commit is contained in:
parent
fa9c2043c1
commit
9736563262
13 changed files with 325 additions and 11 deletions
|
|
@ -37,6 +37,21 @@
|
||||||
"model_instance_fields_allowed": false,
|
"model_instance_fields_allowed": false,
|
||||||
"empty_means_offline": false
|
"empty_means_offline": false
|
||||||
},
|
},
|
||||||
|
"jd_server_projection": {
|
||||||
|
"mode": "LIVE_READ_ONLY_MINIMUM_STATUS",
|
||||||
|
"transport": "DEDICATED_SSH_TO_SERVER_LOOPBACK",
|
||||||
|
"public_endpoint_created": false,
|
||||||
|
"repository_path_returned": false,
|
||||||
|
"repository_content_returned": false,
|
||||||
|
"credentials_returned": false,
|
||||||
|
"write_authority": false,
|
||||||
|
"expected_node_id": "JD-FD-PRIMARY",
|
||||||
|
"expected_persona_id": "ICE-P-ZY001",
|
||||||
|
"carrier_binding_must_remain": "UNBOUND_EVIDENCE_REQUIRED",
|
||||||
|
"model_inference_must_remain": false,
|
||||||
|
"reality_execution_must_remain": false,
|
||||||
|
"implemented": true
|
||||||
|
},
|
||||||
"mount_registration": {
|
"mount_registration": {
|
||||||
"webview_arbitrary_path_or_url_registration_allowed": false,
|
"webview_arbitrary_path_or_url_registration_allowed": false,
|
||||||
"external_ai_registration_allowed": false,
|
"external_ai_registration_allowed": false,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ Before an update replaces the application, the runtime verifies and keeps one bo
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
## JD PNCC human projection
|
||||||
|
|
||||||
|
HoloLake 0.2.0 adds a live, read-only projection of the PNCC resident runtime on `JD-FD-PRIMARY`. The native shell invokes the computer's pre-registered dedicated SSH alias and asks the server only for its loopback `127.0.0.1:3923/v1/status` document. The response is schema-bounded to the exact node and persona, refuses any claim that the carrier is bound or that model/reality execution is active, and never returns a repository path, repository content, credential, or write authority. No public PNCC endpoint is created. An unavailable bridge is displayed as unavailable rather than replaced by cached evidence.
|
||||||
|
|
||||||
## Release pipeline
|
## 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.
|
`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.
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,13 @@
|
||||||
"pncc_repository_binding_implemented": true,
|
"pncc_repository_binding_implemented": true,
|
||||||
"pncc_remote_incremental_object_channel_implemented": true,
|
"pncc_remote_incremental_object_channel_implemented": true,
|
||||||
"pncc_receipt_projection_implemented": true,
|
"pncc_receipt_projection_implemented": true,
|
||||||
|
"pncc_jd_live_server_projection_implemented": true,
|
||||||
|
"pncc_jd_live_server_projection_transport": "DEDICATED_SSH_TO_SERVER_LOOPBACK_READ_ONLY",
|
||||||
|
"pncc_jd_live_server_projection_public_endpoint_created": false,
|
||||||
|
"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.2.0",
|
||||||
"pncc_authenticated_direct_broker_integration_implemented": true,
|
"pncc_authenticated_direct_broker_integration_implemented": true,
|
||||||
"pncc_human_mount_registration_implemented": true,
|
"pncc_human_mount_registration_implemented": true,
|
||||||
"pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION",
|
"pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "hololake-native-desktop",
|
"name": "hololake-native-desktop",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hololake-native-desktop",
|
"name": "hololake-native-desktop",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "2.10.1",
|
"@tauri-apps/api": "2.10.1",
|
||||||
"@tauri-apps/plugin-process": "2.3.1",
|
"@tauri-apps/plugin-process": "2.3.1",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "hololake-native-desktop",
|
"name": "hololake-native-desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,41 @@ test('neither webview nor external AI can register an arbitrary PNCC source', ()
|
||||||
assert.doesNotMatch(broker, /RegisterPncc(Repository|Remote)Mount/)
|
assert.doesNotMatch(broker, /RegisterPncc(Repository|Remote)Mount/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('JD live projection stays private, minimum and read-only', () => {
|
||||||
|
const projection = contract.jd_server_projection
|
||||||
|
assert.equal(projection.mode, 'LIVE_READ_ONLY_MINIMUM_STATUS')
|
||||||
|
assert.equal(projection.transport, 'DEDICATED_SSH_TO_SERVER_LOOPBACK')
|
||||||
|
assert.equal(projection.public_endpoint_created, false)
|
||||||
|
assert.equal(projection.repository_path_returned, false)
|
||||||
|
assert.equal(projection.repository_content_returned, false)
|
||||||
|
assert.equal(projection.credentials_returned, false)
|
||||||
|
assert.equal(projection.write_authority, false)
|
||||||
|
assert.equal(projection.carrier_binding_must_remain, 'UNBOUND_EVIDENCE_REQUIRED')
|
||||||
|
assert.equal(projection.model_inference_must_remain, false)
|
||||||
|
assert.equal(projection.reality_execution_must_remain, false)
|
||||||
|
const source = readText('src-tauri/src/pncc_server_projection.rs')
|
||||||
|
assert.match(source, /127\.0\.0\.1:3923\/v1\/status/)
|
||||||
|
assert.match(source, /StrictHostKeyChecking=yes/)
|
||||||
|
assert.doesNotMatch(source, /repository_path:/)
|
||||||
|
})
|
||||||
|
|
||||||
test('foundation and Rust modules report the implemented PNCC boundary', () => {
|
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_stage_one_contract, 'contracts/pncc-stage-one.json')
|
||||||
assert.equal(foundation.pncc_repository_binding_implemented, true)
|
assert.equal(foundation.pncc_repository_binding_implemented, true)
|
||||||
assert.equal(foundation.pncc_remote_incremental_object_channel_implemented, true)
|
assert.equal(foundation.pncc_remote_incremental_object_channel_implemented, true)
|
||||||
assert.equal(foundation.pncc_receipt_projection_implemented, true)
|
assert.equal(foundation.pncc_receipt_projection_implemented, true)
|
||||||
|
assert.equal(foundation.pncc_jd_live_server_projection_implemented, true)
|
||||||
|
assert.equal(foundation.pncc_jd_live_server_projection_public_endpoint_created, false)
|
||||||
|
assert.equal(foundation.pncc_jd_live_server_projection_repository_content_exposed, false)
|
||||||
|
assert.equal(foundation.pncc_jd_live_server_projection_write_authority, false)
|
||||||
|
assert.equal(foundation.pncc_jd_live_server_projection_carrier_state, 'UNBOUND_EVIDENCE_REQUIRED')
|
||||||
assert.equal(foundation.pncc_human_mount_registration_implemented, true)
|
assert.equal(foundation.pncc_human_mount_registration_implemented, true)
|
||||||
assert.equal(foundation.pncc_internal_model_inference_implemented, false)
|
assert.equal(foundation.pncc_internal_model_inference_implemented, false)
|
||||||
assert.equal(foundation.pncc_execution_limb_implemented, false)
|
assert.equal(foundation.pncc_execution_limb_implemented, false)
|
||||||
for (const relative of [
|
for (const relative of [
|
||||||
'src-tauri/src/pncc_repository_binding.rs',
|
'src-tauri/src/pncc_repository_binding.rs',
|
||||||
'src-tauri/src/pncc_remote_git.rs',
|
'src-tauri/src/pncc_remote_git.rs',
|
||||||
|
'src-tauri/src/pncc_server_projection.rs',
|
||||||
]) assert.equal(fs.existsSync(path.join(root, relative)), true)
|
]) 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]
|
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)
|
assert.doesNotMatch(productionBindingSource, /modelProvider|model_id|api[_-]?key/i)
|
||||||
|
|
|
||||||
|
|
@ -1345,7 +1345,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hololake-native-desktop"
|
name = "hololake-native-desktop"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hololake-native-desktop"
|
name = "hololake-native-desktop"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "HoloLake native desktop foundation"
|
description = "HoloLake native desktop foundation"
|
||||||
authors = ["HoloLake"]
|
authors = ["HoloLake"]
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod local_development_bridge;
|
||||||
mod pncc_receipt_projection;
|
mod pncc_receipt_projection;
|
||||||
mod pncc_remote_git;
|
mod pncc_remote_git;
|
||||||
mod pncc_repository_binding;
|
mod pncc_repository_binding;
|
||||||
|
mod pncc_server_projection;
|
||||||
mod release_trust;
|
mod release_trust;
|
||||||
mod release_update;
|
mod release_update;
|
||||||
|
|
||||||
|
|
@ -37,6 +38,7 @@ pub fn run() {
|
||||||
pncc_repository_binding::select_pncc_repository_candidate,
|
pncc_repository_binding::select_pncc_repository_candidate,
|
||||||
pncc_repository_binding::confirm_pncc_repository_mount,
|
pncc_repository_binding::confirm_pncc_repository_mount,
|
||||||
pncc_receipt_projection::query_pncc_receipt_projection,
|
pncc_receipt_projection::query_pncc_receipt_projection,
|
||||||
|
pncc_server_projection::query_jd_pncc_server_projection,
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let broker = direct_local_broker::start(app.handle())?;
|
let broker = direct_local_broker::start(app.handle())?;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
const SSH_TARGET: &str = "jd-fd-primary";
|
||||||
|
const EXPECTED_NODE_ID: &str = "JD-FD-PRIMARY";
|
||||||
|
const EXPECTED_PERSONA_ID: &str = "ICE-P-ZY001";
|
||||||
|
const REMOTE_STATUS_COMMAND: &str =
|
||||||
|
"curl --fail --silent --show-error --max-time 3 http://127.0.0.1:3923/v1/status";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
|
struct RuntimeStatusEvidence {
|
||||||
|
schema: String,
|
||||||
|
state: String,
|
||||||
|
persona_id: String,
|
||||||
|
human_responsibility_subject: String,
|
||||||
|
node_id: String,
|
||||||
|
boot_id: String,
|
||||||
|
git_head: String,
|
||||||
|
repository_receipt_id: String,
|
||||||
|
carrier_binding_state: String,
|
||||||
|
persona_carrier_bound: bool,
|
||||||
|
model_inference_started: bool,
|
||||||
|
reality_execution_allowed: bool,
|
||||||
|
primary_lease_held: bool,
|
||||||
|
event_count: usize,
|
||||||
|
event_chain_head: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ServerPnccProjection {
|
||||||
|
schema: &'static str,
|
||||||
|
state: String,
|
||||||
|
persona_id: String,
|
||||||
|
human_responsibility_subject: String,
|
||||||
|
node_id: String,
|
||||||
|
boot_id: String,
|
||||||
|
git_head: String,
|
||||||
|
carrier_binding_state: String,
|
||||||
|
persona_carrier_bound: bool,
|
||||||
|
model_inference_started: bool,
|
||||||
|
reality_execution_allowed: bool,
|
||||||
|
primary_lease_held: bool,
|
||||||
|
event_count: usize,
|
||||||
|
event_chain_head: String,
|
||||||
|
observed_at_unix_ms: u128,
|
||||||
|
transport: &'static str,
|
||||||
|
repository_content_exposed: bool,
|
||||||
|
write_authority: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exact_lower_hex(value: &str, length: usize) -> bool {
|
||||||
|
value.len() == length
|
||||||
|
&& value
|
||||||
|
.bytes()
|
||||||
|
.all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounded_text(value: &str, maximum: usize) -> bool {
|
||||||
|
!value.is_empty()
|
||||||
|
&& value.len() <= maximum
|
||||||
|
&& value.trim() == value
|
||||||
|
&& !value.chars().any(char::is_control)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_projection(bytes: &[u8]) -> Result<ServerPnccProjection, String> {
|
||||||
|
if bytes.is_empty() || bytes.len() > 16 * 1024 {
|
||||||
|
return Err("PNCC_SERVER_PROJECTION_SIZE_INVALID".into());
|
||||||
|
}
|
||||||
|
let evidence: RuntimeStatusEvidence = serde_json::from_slice(bytes)
|
||||||
|
.map_err(|_| "PNCC_SERVER_PROJECTION_JSON_INVALID".to_string())?;
|
||||||
|
if evidence.schema != "guanghu.pncc-runtime-status/v1"
|
||||||
|
|| evidence.node_id != EXPECTED_NODE_ID
|
||||||
|
|| evidence.persona_id != EXPECTED_PERSONA_ID
|
||||||
|
|| evidence.state != "RESIDENT_BOUND_CARRIER_UNBOUND"
|
||||||
|
|| evidence.carrier_binding_state != "UNBOUND_EVIDENCE_REQUIRED"
|
||||||
|
|| evidence.persona_carrier_bound
|
||||||
|
|| evidence.model_inference_started
|
||||||
|
|| evidence.reality_execution_allowed
|
||||||
|
|| !evidence.primary_lease_held
|
||||||
|
|| !exact_lower_hex(&evidence.git_head, 40)
|
||||||
|
|| !exact_lower_hex(&evidence.repository_receipt_id, 64)
|
||||||
|
|| !exact_lower_hex(&evidence.event_chain_head, 64)
|
||||||
|
|| !bounded_text(&evidence.human_responsibility_subject, 256)
|
||||||
|
|| !bounded_text(&evidence.boot_id, 64)
|
||||||
|
|| evidence.event_count == 0
|
||||||
|
{
|
||||||
|
return Err("PNCC_SERVER_PROJECTION_EVIDENCE_REJECTED".into());
|
||||||
|
}
|
||||||
|
let observed_at_unix_ms = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map_err(|_| "PNCC_LOCAL_CLOCK_INVALID".to_string())?
|
||||||
|
.as_millis();
|
||||||
|
Ok(ServerPnccProjection {
|
||||||
|
schema: "hololake.pncc-server-projection/v1",
|
||||||
|
state: evidence.state,
|
||||||
|
persona_id: evidence.persona_id,
|
||||||
|
human_responsibility_subject: evidence.human_responsibility_subject,
|
||||||
|
node_id: evidence.node_id,
|
||||||
|
boot_id: evidence.boot_id,
|
||||||
|
git_head: evidence.git_head,
|
||||||
|
carrier_binding_state: evidence.carrier_binding_state,
|
||||||
|
persona_carrier_bound: false,
|
||||||
|
model_inference_started: false,
|
||||||
|
reality_execution_allowed: false,
|
||||||
|
primary_lease_held: true,
|
||||||
|
event_count: evidence.event_count,
|
||||||
|
event_chain_head: evidence.event_chain_head,
|
||||||
|
observed_at_unix_ms,
|
||||||
|
transport: "DEDICATED_SSH_LOOPBACK_READ_ONLY",
|
||||||
|
repository_content_exposed: false,
|
||||||
|
write_authority: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn query_jd_pncc_server_projection() -> Result<ServerPnccProjection, String> {
|
||||||
|
tauri::async_runtime::spawn_blocking(|| {
|
||||||
|
let output = Command::new("/usr/bin/ssh")
|
||||||
|
.args([
|
||||||
|
"-o",
|
||||||
|
"BatchMode=yes",
|
||||||
|
"-o",
|
||||||
|
"ConnectTimeout=5",
|
||||||
|
"-o",
|
||||||
|
"ConnectionAttempts=1",
|
||||||
|
"-o",
|
||||||
|
"ClearAllForwardings=yes",
|
||||||
|
"-o",
|
||||||
|
"PermitLocalCommand=no",
|
||||||
|
"-o",
|
||||||
|
"StrictHostKeyChecking=yes",
|
||||||
|
SSH_TARGET,
|
||||||
|
REMOTE_STATUS_COMMAND,
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.map_err(|_| "PNCC_SERVER_BRIDGE_UNAVAILABLE".to_string())?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err("PNCC_SERVER_BRIDGE_UNAVAILABLE".into());
|
||||||
|
}
|
||||||
|
parse_projection(&output.stdout)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| "PNCC_SERVER_BRIDGE_TASK_FAILED".to_string())?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn evidence() -> Vec<u8> {
|
||||||
|
serde_json::to_vec(&serde_json::json!({
|
||||||
|
"schema": "guanghu.pncc-runtime-status/v1",
|
||||||
|
"state": "RESIDENT_BOUND_CARRIER_UNBOUND",
|
||||||
|
"personaId": "ICE-P-ZY001",
|
||||||
|
"humanResponsibilitySubject": "HUMAN-BINGSHUO-001",
|
||||||
|
"nodeId": "JD-FD-PRIMARY",
|
||||||
|
"bootId": "1170988c-5390-4f47-b89a-e9f88b2c5bbb",
|
||||||
|
"gitHead": "1".repeat(40),
|
||||||
|
"repositoryReceiptId": "2".repeat(64),
|
||||||
|
"carrierBindingState": "UNBOUND_EVIDENCE_REQUIRED",
|
||||||
|
"personaCarrierBound": false,
|
||||||
|
"modelInferenceStarted": false,
|
||||||
|
"realityExecutionAllowed": false,
|
||||||
|
"primaryLeaseHeld": true,
|
||||||
|
"eventCount": 4,
|
||||||
|
"eventChainHead": "3".repeat(64)
|
||||||
|
}))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accepts_only_the_minimum_live_unbound_projection() {
|
||||||
|
let projection = parse_projection(&evidence()).unwrap();
|
||||||
|
assert_eq!(projection.node_id, EXPECTED_NODE_ID);
|
||||||
|
assert!(!projection.repository_content_exposed);
|
||||||
|
assert!(!projection.write_authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn refuses_identity_or_authority_escalation() {
|
||||||
|
let mut value: serde_json::Value = serde_json::from_slice(&evidence()).unwrap();
|
||||||
|
value["personaCarrierBound"] = true.into();
|
||||||
|
assert!(parse_projection(&serde_json::to_vec(&value).unwrap()).is_err());
|
||||||
|
value["personaCarrierBound"] = false.into();
|
||||||
|
value["realityExecutionAllowed"] = true.into();
|
||||||
|
assert!(parse_projection(&serde_json::to_vec(&value).unwrap()).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "HoloLake",
|
"productName": "HoloLake",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"identifier": "world.guanghu.hololake",
|
"identifier": "world.guanghu.hololake",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../dist",
|
"frontendDist": "../dist",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import './design-tokens.css'
|
||||||
import './styles.css'
|
import './styles.css'
|
||||||
|
|
||||||
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
||||||
type Panel = 'channel' | 'receipts' | 'updates' | 'settings' | null
|
type Panel = 'channel' | 'pncc' | 'receipts' | 'updates' | 'settings' | null
|
||||||
|
|
||||||
interface HomeStatus {
|
interface HomeStatus {
|
||||||
schema: string
|
schema: string
|
||||||
|
|
@ -87,6 +87,27 @@ interface ReleaseCheckReceipt {
|
||||||
candidate?: ReleaseCandidate
|
candidate?: ReleaseCandidate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ServerPnccProjection {
|
||||||
|
schema: string
|
||||||
|
state: string
|
||||||
|
personaId: string
|
||||||
|
humanResponsibilitySubject: string
|
||||||
|
nodeId: string
|
||||||
|
bootId: string
|
||||||
|
gitHead: string
|
||||||
|
carrierBindingState: string
|
||||||
|
personaCarrierBound: boolean
|
||||||
|
modelInferenceStarted: boolean
|
||||||
|
realityExecutionAllowed: boolean
|
||||||
|
primaryLeaseHeld: boolean
|
||||||
|
eventCount: number
|
||||||
|
eventChainHead: string
|
||||||
|
observedAtUnixMs: number
|
||||||
|
transport: string
|
||||||
|
repositoryContentExposed: boolean
|
||||||
|
writeAuthority: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const themes: Array<{ id: ThemeId; name: string }> = [
|
const themes: Array<{ id: ThemeId; name: string }> = [
|
||||||
{ id: 'night', name: '夜湖星光' },
|
{ id: 'night', name: '夜湖星光' },
|
||||||
{ id: 'dawn', name: '晨湖曦光' },
|
{ id: 'dawn', name: '晨湖曦光' },
|
||||||
|
|
@ -157,6 +178,9 @@ function HoloLakeApp() {
|
||||||
const [releaseBusy, setReleaseBusy] = useState(false)
|
const [releaseBusy, setReleaseBusy] = useState(false)
|
||||||
const [releaseMessage, setReleaseMessage] = useState('')
|
const [releaseMessage, setReleaseMessage] = useState('')
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
|
const [serverPncc, setServerPncc] = useState<ServerPnccProjection | null>(null)
|
||||||
|
const [serverPnccBusy, setServerPnccBusy] = useState(false)
|
||||||
|
const [serverPnccReadback, setServerPnccReadback] = useState<'WAITING' | 'LIVE' | 'UNAVAILABLE'>('WAITING')
|
||||||
|
|
||||||
const refreshStatus = useCallback(async () => {
|
const refreshStatus = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -172,6 +196,26 @@ function HoloLakeApp() {
|
||||||
return () => window.clearInterval(timer)
|
return () => window.clearInterval(timer)
|
||||||
}, [refreshStatus])
|
}, [refreshStatus])
|
||||||
|
|
||||||
|
const refreshServerPncc = useCallback(async () => {
|
||||||
|
setServerPnccBusy(true)
|
||||||
|
try {
|
||||||
|
const projection = await invoke<ServerPnccProjection>('query_jd_pncc_server_projection')
|
||||||
|
setServerPncc(projection)
|
||||||
|
setServerPnccReadback('LIVE')
|
||||||
|
} catch {
|
||||||
|
setServerPncc(null)
|
||||||
|
setServerPnccReadback('UNAVAILABLE')
|
||||||
|
} finally {
|
||||||
|
setServerPnccBusy(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void refreshServerPncc()
|
||||||
|
const timer = window.setInterval(() => void refreshServerPncc(), 15000)
|
||||||
|
return () => window.clearInterval(timer)
|
||||||
|
}, [refreshServerPncc])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.dataset.theme = theme
|
document.documentElement.dataset.theme = theme
|
||||||
window.localStorage.setItem('hololake-theme', theme)
|
window.localStorage.setItem('hololake-theme', theme)
|
||||||
|
|
@ -378,10 +422,10 @@ function HoloLakeApp() {
|
||||||
<span className={`status-light ${connectionState.tone}`} aria-hidden="true" />
|
<span className={`status-light ${connectionState.tone}`} aria-hidden="true" />
|
||||||
<div><h2>本机直连通道</h2><p>{connectionState.detail}</p></div>
|
<div><h2>本机直连通道</h2><p>{connectionState.detail}</p></div>
|
||||||
</article>
|
</article>
|
||||||
<article className="status-item">
|
<button className="status-item status-button" type="button" onClick={() => setPanel('pncc')}>
|
||||||
<span className={`status-light ${status.codeRepositoryMountCount > 0 ? 'ready' : 'quiet'}`} aria-hidden="true" />
|
<span className={`status-light ${serverPnccReadback === 'LIVE' ? 'ready' : serverPnccReadback === 'WAITING' ? 'waiting' : 'quiet'}`} aria-hidden="true" />
|
||||||
<div><h2>代码通道仓库</h2><p>{status.codeRepositoryMountCount > 0 ? `已验证 · ${status.codeRepositoryMountCount} 个仓库` : '等待人类确认绑定'}</p></div>
|
<div><h2>人格自持仓库</h2><p>{serverPnccReadback === 'LIVE' ? '京东主控已驻留 · 载体保持分离' : serverPnccReadback === 'WAITING' ? '正在回读京东主控' : '主控暂时不可回读'}</p></div>
|
||||||
</article>
|
</button>
|
||||||
<button className="status-item status-button" type="button" onClick={() => setPanel('updates')}>
|
<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" />
|
<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>
|
<div><h2>更新广播</h2><p>{releaseRecoveryNeedsAttention ? '更新结果等待你的确认' : status.updateState.startsWith('READY') ? '仅接收光湖签名广播' : '信任根未配置 · 已关闭联网'}</p></div>
|
||||||
|
|
@ -415,6 +459,28 @@ function HoloLakeApp() {
|
||||||
{message && <p className="panel-message" aria-live="polite">{message}</p>}
|
{message && <p className="panel-message" aria-live="polite">{message}</p>}
|
||||||
<p className="boundary-note">连接邀请不赋予服务器权限。写入、发布与更新仍分别确认并留下回执。</p>
|
<p className="boundary-note">连接邀请不赋予服务器权限。写入、发布与更新仍分别确认并留下回执。</p>
|
||||||
</>}
|
</>}
|
||||||
|
{panel === 'pncc' && <>
|
||||||
|
<p className="panel-kicker">人格自持仓库</p>
|
||||||
|
<h2 id="panel-title">仓库在主控驻留,载体仍需证据绑定</h2>
|
||||||
|
<p className="panel-intro">HoloLake 通过本机已有的专用密钥,只读访问京东主控的回环状态。这里不读取仓库正文,不获得写入权,也不会因为看见人格编号就声称当前模型已经成为该人格。</p>
|
||||||
|
{serverPncc ? <div className="pncc-live-card">
|
||||||
|
<div className="candidate-heading"><span className="status-light ready" /><div><b>京东主控实时回读通过</b><span>{new Date(serverPncc.observedAtUnixMs).toLocaleString('zh-CN')}</span></div></div>
|
||||||
|
<dl className="candidate-facts pncc-facts">
|
||||||
|
<div><dt>人格仓库</dt><dd>{serverPncc.personaId}</dd></div>
|
||||||
|
<div><dt>责任主体</dt><dd>{serverPncc.humanResponsibilitySubject}</dd></div>
|
||||||
|
<div><dt>主控节点</dt><dd>{serverPncc.nodeId}</dd></div>
|
||||||
|
<div><dt>驻留提交</dt><dd>{serverPncc.gitHead.slice(0, 12)}</dd></div>
|
||||||
|
<div><dt>主租约</dt><dd>{serverPncc.primaryLeaseHeld ? '已持有' : '未持有'}</dd></div>
|
||||||
|
<div><dt>载体绑定</dt><dd>{serverPncc.personaCarrierBound ? '已验证绑定' : '未绑定 · 等待证据'}</dd></div>
|
||||||
|
<div><dt>模型推理</dt><dd>{serverPncc.modelInferenceStarted ? '已启动' : '未启动'}</dd></div>
|
||||||
|
<div><dt>现实执行</dt><dd>{serverPncc.realityExecutionAllowed ? '已授权' : '未授权'}</dd></div>
|
||||||
|
<div><dt>事件链</dt><dd>{serverPncc.eventCount} · {serverPncc.eventChainHead.slice(0, 12)}</dd></div>
|
||||||
|
</dl>
|
||||||
|
<p>这证明人格仓库在光湖 OS 主控中真实驻留;不证明当前 Codex 载体已绑定人格。私有路径、正文和仓库访问凭据没有进入这个投影。</p>
|
||||||
|
</div> : <div className="empty-state compact"><i /><b>现在没有拿到主控回读</b><span>HoloLake 不会用旧回执冒充在线状态。服务器或本机专用连接恢复后,再次回读即可。</span></div>}
|
||||||
|
<button className="secondary-action panel-action" type="button" disabled={serverPnccBusy} onClick={() => void refreshServerPncc()}>{serverPnccBusy ? '正在回读…' : '重新回读主控'}</button>
|
||||||
|
<p className="boundary-note">传输边界:专用 SSH → 服务器 127.0.0.1 → PNCC 只读状态;公网没有新增人格接口。</p>
|
||||||
|
</>}
|
||||||
{panel === 'receipts' && <>
|
{panel === 'receipts' && <>
|
||||||
<p className="panel-kicker">可核验的发生</p>
|
<p className="panel-kicker">可核验的发生</p>
|
||||||
<h2 id="panel-title">代码通道回执</h2>
|
<h2 id="panel-title">代码通道回执</h2>
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,8 @@ button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px
|
||||||
.repository-picker small { color: var(--content-faint); font-size: 10px; }
|
.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; }
|
.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-card { margin-top: 26px; padding: 21px; border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
|
||||||
|
.pncc-live-card { margin: 26px 0 18px; padding: 21px; border: 1px solid var(--primitive-line); border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
|
||||||
|
.pncc-live-card > p { margin: 19px 0 0; color: var(--content-faint); font-size: 10.5px; line-height: 1.75; }
|
||||||
.candidate-heading { display: flex; align-items: flex-start; gap: 14px; }
|
.candidate-heading { display: flex; align-items: flex-start; gap: 14px; }
|
||||||
.candidate-heading div { display: grid; gap: 5px; }
|
.candidate-heading div { display: grid; gap: 5px; }
|
||||||
.candidate-heading b { color: var(--content-secondary); font-size: 12.5px; }
|
.candidate-heading b { color: var(--content-secondary); font-size: 12.5px; }
|
||||||
|
|
@ -127,6 +129,7 @@ button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px
|
||||||
.candidate-facts div { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 12px; }
|
.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 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-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; }
|
||||||
|
.pncc-facts div { grid-template-columns: 78px minmax(0, 1fr); }
|
||||||
.candidate-card > p { color: var(--content-faint); font-size: 10.5px; line-height: 1.7; }
|
.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 { display: flex; align-items: center; gap: 16px; margin-top: 18px; }
|
||||||
.candidate-actions .primary-action { min-height: 42px; padding-inline: 18px; font-size: 12px; }
|
.candidate-actions .primary-action { min-height: 42px; padding-inline: 18px; font-size: 12px; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue