193 lines
6.8 KiB
Rust
193 lines
6.8 KiB
Rust
|
|
// 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,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn query_jd_pncc_server_projection() -> Result<ServerPnccProjection, String> {
|
||
|
|
tauri::async_runtime::spawn_blocking(|| {
|
||
|
|
let executable = if cfg!(windows) { "ssh" } else { "/usr/bin/ssh" };
|
||
|
|
let output = Command::new(executable)
|
||
|
|
.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());
|
||
|
|
}
|
||
|
|
}
|