feat(hololake): project resident PNCC state

This commit is contained in:
冰朔 2026-08-16 02:52:55 +08:00
commit 9736563262
13 changed files with 325 additions and 11 deletions

View file

@ -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,

View file

@ -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.

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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)

View file

@ -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",

View file

@ -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"

View file

@ -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())?;

View file

@ -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());
}
}

View file

@ -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",

View file

@ -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>

View file

@ -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; }