feat: verify signed persona control receipts natively

This commit is contained in:
冰朔 2026-08-12 05:50:53 +08:00
commit 75b6262c91
11 changed files with 562 additions and 98 deletions

View file

@ -121,8 +121,9 @@ REVISE | REFUSE`。
不能证明回执确由 `GUANGHU_OS` 签发。独立的 v2 回执、REPO-012 精确提交签名者注册表、规范签名字节、
人格/责任主体/范围约束、吊销和 Ed25519 验签源码契约现为 `100`。REPO-012 main
`21aec6f5042e32e34c892a7a212d8dec5f758437` 已发布锚定的空注册表Tauri 原生只读加载器源码也为
`100`:它只接受同一精确提交的空注册表,遇到任何签名者都失败关闭且保持授权禁用。真实可信签名者、
密钥托管、原生签名回执验签与人格主控运行集成仍为 `0`;普通本地 JSON 不得升级人格主控,桌面继续系统直控。
`100`。原生 v2 签名回执解析、规范签名字节、Ed25519 SPKI 验签、签名者范围/吊销、请求绑定和有效期
校验源码也为 `100`。但真实可信签名者、密钥托管以及验签结果到人格语言控制器的运行接线仍为 `0`
当前空注册表只能产生拒绝,普通本地 JSON 不得升级人格主控,桌面继续系统直控。
- 完整 HoloLake Runtime 与单 AGE 纵向闭环仍为 `0`:真实人格仓库 manifest 绑定和桌面运行验收尚未完成,
因此不能用本轮源码测试冒充可用产品。
- Mirror runner、制品、部署和运行健康`0`

View file

@ -27,8 +27,8 @@ of a receipt that claims `GUANGHU_OS` verification. That renderer-side validatio
and cannot turn a local JSON object into authority. A native loader may be connected only after it verifies a
signed receipt against a registered Guanghu OS trust source and returns the already-verified projection. REPO-012
now publishes the exact registry source with zero signers, and the Tauri command loads only that exact commit-bound
empty state. It keeps authorization disabled and rejects any signer until native cryptographic verification exists,
so planning remains in system-direct mode. The independent server
empty state. The native verifier now validates the complete signed v2 contract, but no verified projection is wired
into the language controller and the authoritative registry has zero signers, so planning remains in system-direct mode. The independent server
contract in `product-source/guanghu-knowledge-base/server/persona-control-authorization.ts` defines the v2 signed
receipt, exact REPO-012 signer-registry source, canonical signing bytes, signer scope checks, revocation handling,
and Ed25519 verification. Its tested code does not appoint a signer or enable the desktop persona-primary path.

View file

@ -190,9 +190,10 @@ cryptographic source contract: v2 signed receipt fields, deterministic signing b
signer-registry source, persona/human/scope-limited Ed25519 signers, revocation, time and request binding, and
signature verification. The current REPO-012 main does not publish that registry path, so no signer is trusted and
the Tauri desktop now exposes a native read-only loader for the exact REPO-012 commit-bound registry. It accepts
the current empty registry only and keeps authorization disabled; any signer fails closed until the native
cryptographic receipt verifier and an authoritative signer/key-custody process exist. Tested loader and verifier
source are not runtime authority, deployment, or desktop acceptance.
the current empty registry and keeps authorization disabled. The native verifier now implements strict v2 receipt
parsing, canonical signing bytes, Ed25519 SPKI verification, signer scope/revocation checks, exact request bindings,
and validity windows. It is not wired into the language controller, and the authoritative registry still has zero
signers, so tested loader/verifier source is not runtime authority, deployment, or desktop acceptance.
Tolaria is a personal knowledge and life management desktop app. It reads a vault of markdown files with YAML frontmatter and presents them in a four-panel UI inspired by Bear Notes.

View file

@ -526,6 +526,7 @@ macro_rules! app_invoke_handler {
guanghu_enterprise::guanghu_enterprise_status,
guanghu_living_system::guanghu_living_system_plan,
persona_control_authorization::load_persona_control_authorization_registry,
persona_control_authorization::verify_persona_control_authorization,
persona_code_channel::prepare_persona_code_channel_wake,
persona_code_channel::inspect_persona_code_channel_manifest,
persona_code_channel::discover_persona_code_channel_repositories,

View file

@ -1,13 +1,21 @@
use base64::{engine::general_purpose, Engine as _};
use reqwest::Client;
use serde::Serialize;
use ring::signature::{UnparsedPublicKey, ED25519};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::Duration;
const ANCHOR_URL: &str = "https://guanghulab.com/api/ai/v1/anchor";
const CODE_URL: &str = "https://guanghulab.com/code/bingshuo/guanghu-ice-heart";
const RECEIPT_SCHEMA: &str = "hololake.persona-control-authorization/v2";
const REGISTRY_ID: &str = "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001";
const REGISTRY_PATH: &str = "routing/persona-control-authorization-signers.json";
const REGISTRY_SCHEMA: &str = "gh-aios.persona-control-authorization-signers/v1";
const REQUIRED_SCOPE: &str = "PERSONA_PRIMARY_LANGUAGE_PLANNING";
const SIGNING_CONTEXT: &str = "hololake.persona-control-authorization/signing/v1";
const ED25519_SPKI_PREFIX: [u8; 12] = [
0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00,
];
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
@ -19,6 +27,76 @@ pub struct NativePersonaAuthorizationRegistryReceipt {
authorization_enabled: bool,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NativePersonaAuthorizationVerificationReceipt {
status: &'static str,
reason: Option<&'static str>,
source_commit: Option<String>,
signer_id: Option<String>,
authorization_enabled: bool,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NativePersonaAuthorizationExpectation {
persona_id: String,
human_responsibility_subject: String,
repository_head: String,
model_instance_id: String,
request_id: String,
source_language_anchor: String,
observed_at_milliseconds: i64,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct PersonaAuthorizationSigner {
algorithm: String,
human_responsibility_subjects: Vec<String>,
persona_ids: Vec<String>,
public_key_pem: String,
scopes: Vec<String>,
signer_id: String,
status: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct PersonaAuthorizationRegistry {
schema: String,
registry_id: String,
state: String,
signers: Vec<PersonaAuthorizationSigner>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct PersonaAuthorizationReceipt {
schema: String,
outcome: String,
authorization_id: String,
verifier: String,
scope: String,
persona_id: String,
human_responsibility_subject: String,
repository_head: String,
model_instance_id: String,
request_id: String,
source_language_anchor: String,
issued_at: String,
valid_until: String,
evidence_digest: String,
signer_id: String,
signature_algorithm: String,
signature: String,
}
struct LoadedRegistry {
source_commit: String,
registry: PersonaAuthorizationRegistry,
}
fn unavailable(reason: &'static str) -> NativePersonaAuthorizationRegistryReceipt {
NativePersonaAuthorizationRegistryReceipt {
status: "UNAVAILABLE",
@ -29,13 +107,93 @@ fn unavailable(reason: &'static str) -> NativePersonaAuthorizationRegistryReceip
}
}
fn is_commit(value: &str) -> bool {
matches!(value.len(), 40 | 64)
fn denied(
reason: &'static str,
source_commit: Option<String>,
) -> NativePersonaAuthorizationVerificationReceipt {
NativePersonaAuthorizationVerificationReceipt {
status: "DENIED",
reason: Some(reason),
source_commit,
signer_id: None,
authorization_enabled: false,
}
}
fn is_lower_hex(value: &str, length: usize) -> bool {
value.len() == length
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}
fn is_commit(value: &str) -> bool {
is_lower_hex(value, 40) || is_lower_hex(value, 64)
}
fn is_identifier(value: &str) -> bool {
let mut bytes = value.bytes();
let Some(first) = bytes.next() else {
return false;
};
(2..=160).contains(&value.len())
&& (first.is_ascii_uppercase() || first.is_ascii_digit())
&& bytes.all(|byte| {
byte.is_ascii_uppercase()
|| byte.is_ascii_digit()
|| matches!(byte, b'.' | b'_' | b':' | b'@' | b'-')
})
}
fn exact_identifier_list(values: &[String]) -> bool {
!values.is_empty()
&& values.iter().all(|value| is_identifier(value))
&& values
.iter()
.enumerate()
.all(|(index, value)| !values[..index].contains(value))
}
fn ed25519_public_key(public_key_pem: &str) -> Result<Vec<u8>, &'static str> {
if public_key_pem.contains("PRIVATE KEY")
|| !public_key_pem.starts_with("-----BEGIN PUBLIC KEY-----\n")
|| !public_key_pem.ends_with("-----END PUBLIC KEY-----\n")
|| public_key_pem.len() > 4096
{
return Err("REGISTRY_INVALID");
}
let encoded = public_key_pem
.trim_start_matches("-----BEGIN PUBLIC KEY-----\n")
.trim_end_matches("-----END PUBLIC KEY-----\n")
.chars()
.filter(|character| !character.is_ascii_whitespace())
.collect::<String>();
let der = general_purpose::STANDARD
.decode(encoded)
.map_err(|_| "REGISTRY_INVALID")?;
if der.len() != ED25519_SPKI_PREFIX.len() + 32
|| der[..ED25519_SPKI_PREFIX.len()] != ED25519_SPKI_PREFIX
{
return Err("REGISTRY_INVALID");
}
Ok(der[ED25519_SPKI_PREFIX.len()..].to_vec())
}
fn validate_signer(signer: &PersonaAuthorizationSigner) -> Result<(), &'static str> {
if signer.algorithm != "Ed25519"
|| !is_identifier(&signer.signer_id)
|| !matches!(signer.status.as_str(), "ACTIVE" | "REVOKED")
|| !exact_identifier_list(&signer.human_responsibility_subjects)
|| !exact_identifier_list(&signer.persona_ids)
|| !exact_identifier_list(&signer.scopes)
|| !signer.scopes.iter().any(|scope| scope == REQUIRED_SCOPE)
{
return Err("REGISTRY_INVALID");
}
ed25519_public_key(&signer.public_key_pem)?;
Ok(())
}
fn parse_anchor(
input: &Value,
expected_anchor_url: &str,
@ -76,23 +234,22 @@ fn parse_anchor(
Ok(source_commit.to_string())
}
fn validate_empty_registry(input: &Value) -> Result<(), &'static str> {
let registry = input.as_object().ok_or("REGISTRY_INVALID")?;
let signers = registry
.get("signers")
.and_then(Value::as_array)
.ok_or("REGISTRY_INVALID")?;
if registry.len() != 4
|| registry.get("schema").and_then(Value::as_str) != Some(REGISTRY_SCHEMA)
|| registry.get("registryId").and_then(Value::as_str) != Some(REGISTRY_ID)
|| registry.get("state").and_then(Value::as_str) != Some("CURRENT")
fn parse_registry(input: Value) -> Result<PersonaAuthorizationRegistry, &'static str> {
let registry: PersonaAuthorizationRegistry =
serde_json::from_value(input).map_err(|_| "REGISTRY_INVALID")?;
if registry.schema != REGISTRY_SCHEMA
|| registry.registry_id != REGISTRY_ID
|| registry.state != "CURRENT"
|| registry.signers.iter().enumerate().any(|(index, signer)| {
validate_signer(signer).is_err()
|| registry.signers[..index]
.iter()
.any(|prior| prior.signer_id == signer.signer_id)
})
{
return Err("REGISTRY_INVALID");
}
if !signers.is_empty() {
return Err("TRUSTED_SIGNER_REQUIRES_NATIVE_VERIFIER");
}
Ok(())
Ok(registry)
}
fn registry_url(code_url: &str, source_commit: &str) -> String {
@ -112,56 +269,207 @@ async fn fetch_json(client: &Client, url: &str) -> Result<Value, ()> {
response.json::<Value>().await.map_err(|_| ())
}
async fn load_snapshot(
client: &Client,
anchor_url: &str,
code_url: &str,
) -> Result<LoadedRegistry, &'static str> {
let anchor = fetch_json(client, anchor_url)
.await
.map_err(|_| "SOURCE_UNAVAILABLE")?;
let source_commit = parse_anchor(&anchor, anchor_url, code_url)?;
let registry = fetch_json(client, &registry_url(code_url, &source_commit))
.await
.map_err(|_| "SOURCE_UNAVAILABLE")?;
Ok(LoadedRegistry {
source_commit,
registry: parse_registry(registry)?,
})
}
async fn load_with_urls(
client: &Client,
anchor_url: &str,
code_url: &str,
) -> NativePersonaAuthorizationRegistryReceipt {
let anchor = match fetch_json(client, anchor_url).await {
Ok(value) => value,
Err(()) => return unavailable("SOURCE_UNAVAILABLE"),
};
let source_commit = match parse_anchor(&anchor, anchor_url, code_url) {
Ok(value) => value,
Err(reason) => return unavailable(reason),
};
let registry = match fetch_json(client, &registry_url(code_url, &source_commit)).await {
Ok(value) => value,
Err(()) => return unavailable("SOURCE_UNAVAILABLE"),
};
if let Err(reason) = validate_empty_registry(&registry) {
return unavailable(reason);
match load_snapshot(client, anchor_url, code_url).await {
Ok(snapshot) => NativePersonaAuthorizationRegistryReceipt {
status: if snapshot.registry.signers.is_empty() {
"CURRENT_EMPTY"
} else {
"CURRENT"
},
reason: None,
source_commit: Some(snapshot.source_commit),
signer_count: snapshot.registry.signers.len(),
authorization_enabled: false,
},
Err(reason) => unavailable(reason),
}
NativePersonaAuthorizationRegistryReceipt {
status: "CURRENT_EMPTY",
}
fn parse_receipt(input: Value) -> Result<PersonaAuthorizationReceipt, &'static str> {
let receipt: PersonaAuthorizationReceipt =
serde_json::from_value(input).map_err(|_| "RECEIPT_INVALID")?;
if receipt.schema != RECEIPT_SCHEMA
|| receipt.outcome != "VERIFIED"
|| receipt.verifier != "GUANGHU_OS"
|| receipt.scope != REQUIRED_SCOPE
|| receipt.signature_algorithm != "Ed25519"
|| !is_identifier(&receipt.authorization_id)
|| !is_identifier(&receipt.persona_id)
|| !is_identifier(&receipt.human_responsibility_subject)
|| !is_commit(&receipt.repository_head)
|| !is_identifier(&receipt.model_instance_id)
|| !is_identifier(&receipt.request_id)
|| !is_identifier(&receipt.signer_id)
|| !is_lower_hex(&receipt.evidence_digest, 64)
|| !(80..=128).contains(&receipt.signature.len())
{
return Err("RECEIPT_INVALID");
}
Ok(receipt)
}
fn signing_bytes(receipt: &PersonaAuthorizationReceipt) -> Result<Vec<u8>, &'static str> {
let fields = [
SIGNING_CONTEXT,
&receipt.schema,
&receipt.outcome,
&receipt.authorization_id,
&receipt.verifier,
&receipt.scope,
&receipt.persona_id,
&receipt.human_responsibility_subject,
&receipt.repository_head,
&receipt.model_instance_id,
&receipt.request_id,
&receipt.source_language_anchor,
&receipt.issued_at,
&receipt.valid_until,
&receipt.evidence_digest,
&receipt.signer_id,
&receipt.signature_algorithm,
];
serde_json::to_string(&fields)
.map(|json| format!("{json}\n").into_bytes())
.map_err(|_| "RECEIPT_INVALID")
}
fn verify_receipt(
input: Value,
expectation: &NativePersonaAuthorizationExpectation,
snapshot: LoadedRegistry,
) -> NativePersonaAuthorizationVerificationReceipt {
let source_commit = Some(snapshot.source_commit.clone());
let receipt = match parse_receipt(input) {
Ok(receipt) => receipt,
Err(reason) => return denied(reason, source_commit),
};
let issued_at = match chrono::DateTime::parse_from_rfc3339(&receipt.issued_at) {
Ok(value) => value.timestamp_millis(),
Err(_) => return denied("RECEIPT_TIME_INVALID", source_commit),
};
let valid_until = match chrono::DateTime::parse_from_rfc3339(&receipt.valid_until) {
Ok(value) => value.timestamp_millis(),
Err(_) => return denied("RECEIPT_TIME_INVALID", source_commit),
};
if receipt.persona_id != expectation.persona_id
|| receipt.human_responsibility_subject != expectation.human_responsibility_subject
|| receipt.repository_head != expectation.repository_head
|| receipt.model_instance_id != expectation.model_instance_id
|| receipt.request_id != expectation.request_id
|| receipt.source_language_anchor != expectation.source_language_anchor
|| issued_at >= valid_until
|| issued_at > expectation.observed_at_milliseconds
|| expectation.observed_at_milliseconds > valid_until
{
return denied("RECEIPT_BINDING_INVALID", source_commit);
}
let Some(signer) = snapshot.registry.signers.iter().find(|signer| {
signer.status == "ACTIVE"
&& signer.signer_id == receipt.signer_id
&& signer.persona_ids.contains(&receipt.persona_id)
&& signer
.human_responsibility_subjects
.contains(&receipt.human_responsibility_subject)
&& signer.scopes.contains(&receipt.scope)
}) else {
return denied("NO_TRUSTED_SIGNER", source_commit);
};
let public_key = match ed25519_public_key(&signer.public_key_pem) {
Ok(value) => value,
Err(_) => return denied("REGISTRY_INVALID", source_commit),
};
let signature = match general_purpose::URL_SAFE_NO_PAD.decode(&receipt.signature) {
Ok(value) if value.len() == 64 => value,
_ => return denied("SIGNATURE_INVALID", source_commit),
};
let message = match signing_bytes(&receipt) {
Ok(value) => value,
Err(reason) => return denied(reason, source_commit),
};
if UnparsedPublicKey::new(&ED25519, public_key)
.verify(&message, &signature)
.is_err()
{
return denied("SIGNATURE_INVALID", source_commit);
}
NativePersonaAuthorizationVerificationReceipt {
status: "VERIFIED",
reason: None,
source_commit: Some(source_commit),
signer_count: 0,
authorization_enabled: false,
source_commit,
signer_id: Some(receipt.signer_id),
authorization_enabled: true,
}
}
fn native_client() -> Result<Client, ()> {
Client::builder()
.connect_timeout(Duration::from_secs(8))
.timeout(Duration::from_secs(15))
.redirect(reqwest::redirect::Policy::none())
.build()
.map_err(|_| ())
}
#[tauri::command]
pub async fn load_persona_control_authorization_registry(
) -> NativePersonaAuthorizationRegistryReceipt {
let client = match Client::builder()
.connect_timeout(Duration::from_secs(8))
.timeout(Duration::from_secs(15))
.redirect(reqwest::redirect::Policy::none())
.build()
{
let client = match native_client() {
Ok(client) => client,
Err(_) => return unavailable("CLIENT_UNAVAILABLE"),
Err(()) => return unavailable("CLIENT_UNAVAILABLE"),
};
load_with_urls(&client, ANCHOR_URL, CODE_URL).await
}
#[tauri::command]
pub async fn verify_persona_control_authorization(
receipt: Value,
expectation: NativePersonaAuthorizationExpectation,
) -> NativePersonaAuthorizationVerificationReceipt {
let client = match native_client() {
Ok(client) => client,
Err(()) => return denied("CLIENT_UNAVAILABLE", None),
};
let snapshot = match load_snapshot(&client, ANCHOR_URL, CODE_URL).await {
Ok(snapshot) => snapshot,
Err(reason) => return denied(reason, None),
};
verify_receipt(receipt, &expectation, snapshot)
}
#[cfg(test)]
mod tests {
use super::{
load_with_urls, parse_anchor, registry_url, validate_empty_registry, REGISTRY_PATH,
load_snapshot, load_with_urls, parse_anchor, parse_registry, registry_url, signing_bytes,
verify_receipt, LoadedRegistry, NativePersonaAuthorizationExpectation,
PersonaAuthorizationReceipt, REGISTRY_PATH,
};
use base64::{engine::general_purpose, Engine as _};
use reqwest::Client;
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, KeyPair};
use serde_json::{json, Value};
use std::io::{Read, Write};
use std::net::TcpListener;
@ -178,14 +486,12 @@ mod tests {
"branch": "main",
"public_entry": anchor_url,
"code_entry": code_url,
"maps": {
"persona_control_authorization_signers": {
"path": REGISTRY_PATH,
"id": "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001",
"schema": "gh-aios.persona-control-authorization-signers/v1",
"state": "CURRENT_EMPTY_NO_TRUSTED_SIGNER"
}
},
"maps": { "persona_control_authorization_signers": {
"path": REGISTRY_PATH,
"id": "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001",
"schema": "gh-aios.persona-control-authorization-signers/v1",
"state": "CURRENT_EMPTY_NO_TRUSTED_SIGNER"
}},
"navigation_source": {
"anchor_id": "GLW-PUBLIC-NAV-ANCHOR-001",
"source_commit": COMMIT,
@ -195,22 +501,88 @@ mod tests {
})
}
fn empty_registry() -> Value {
fn registry(signers: Value) -> Value {
json!({
"schema": "gh-aios.persona-control-authorization-signers/v1",
"registryId": "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001",
"state": "CURRENT",
"signers": []
"signers": signers
})
}
fn serve_snapshot() -> (String, String) {
fn public_key_pem(pair: &Ed25519KeyPair) -> String {
let mut der = super::ED25519_SPKI_PREFIX.to_vec();
der.extend_from_slice(pair.public_key().as_ref());
format!(
"-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----\n",
general_purpose::STANDARD.encode(der)
)
}
fn signer(pair: &Ed25519KeyPair, status: &str) -> Value {
json!({
"algorithm": "Ed25519",
"humanResponsibilitySubjects": ["BINGSHUO"],
"personaIds": ["ICE-P-ZY001"],
"publicKeyPem": public_key_pem(pair),
"scopes": ["PERSONA_PRIMARY_LANGUAGE_PLANNING"],
"signerId": "GH-AIOS-AUTHORIZER-001",
"status": status
})
}
fn unsigned_receipt() -> PersonaAuthorizationReceipt {
PersonaAuthorizationReceipt {
schema: "hololake.persona-control-authorization/v2".into(),
outcome: "VERIFIED".into(),
authorization_id: "AUTH-001".into(),
verifier: "GUANGHU_OS".into(),
scope: "PERSONA_PRIMARY_LANGUAGE_PLANNING".into(),
persona_id: "ICE-P-ZY001".into(),
human_responsibility_subject: "BINGSHUO".into(),
repository_head: "a".repeat(40),
model_instance_id: "MODEL-INSTANCE-001".into(),
request_id: "REQ-001".into(),
source_language_anchor: "HLP-CURRENT-ARCH-001@2026-08-12.14".into(),
issued_at: "2026-08-12T05:00:00+08:00".into(),
valid_until: "2026-08-12T05:10:00+08:00".into(),
evidence_digest: "b".repeat(64),
signer_id: "GH-AIOS-AUTHORIZER-001".into(),
signature_algorithm: "Ed25519".into(),
signature: String::new(),
}
}
fn expectation() -> NativePersonaAuthorizationExpectation {
NativePersonaAuthorizationExpectation {
persona_id: "ICE-P-ZY001".into(),
human_responsibility_subject: "BINGSHUO".into(),
repository_head: "a".repeat(40),
model_instance_id: "MODEL-INSTANCE-001".into(),
request_id: "REQ-001".into(),
source_language_anchor: "HLP-CURRENT-ARCH-001@2026-08-12.14".into(),
observed_at_milliseconds: chrono::DateTime::parse_from_rfc3339(
"2026-08-12T05:05:00+08:00",
)
.unwrap()
.timestamp_millis(),
}
}
fn signed_receipt(pair: &Ed25519KeyPair) -> Value {
let mut receipt = unsigned_receipt();
receipt.signature = general_purpose::URL_SAFE_NO_PAD
.encode(pair.sign(&signing_bytes(&receipt).unwrap()).as_ref());
serde_json::to_value(receipt).unwrap()
}
fn serve_snapshot(registry_body: Value) -> (String, String) {
let listener = TcpListener::bind("127.0.0.1:0").expect("bind test snapshot server");
let address = listener.local_addr().expect("snapshot server address");
let anchor_url = format!("http://{address}/anchor");
let code_url = format!("http://{address}/code");
let anchor_body = anchor(&anchor_url, &code_url).to_string();
let registry_body = empty_registry().to_string();
let registry_body = registry_body.to_string();
thread::spawn(move || {
for _ in 0..2 {
let (mut stream, _) = listener.accept().expect("accept snapshot request");
@ -223,19 +595,19 @@ mod tests {
assert!(request.contains(&format!("/raw/commit/{COMMIT}/{REGISTRY_PATH}")));
&registry_body
};
write!(
stream,
"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}",
body.len()
)
.expect("write snapshot response");
write!(stream, "HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}", body.len()).unwrap();
}
});
(anchor_url, code_url)
}
fn key_pair() -> Ed25519KeyPair {
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()).unwrap();
Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap()
}
#[test]
fn exact_anchor_and_registry_are_bound_to_one_commit() {
fn exact_anchor_and_empty_registry_are_bound_to_one_commit() {
let anchor_url = "https://example.test/anchor";
let code_url = "https://example.test/code";
assert_eq!(
@ -246,11 +618,14 @@ mod tests {
registry_url(code_url, COMMIT),
format!("{code_url}/raw/commit/{COMMIT}/{REGISTRY_PATH}")
);
assert_eq!(validate_empty_registry(&empty_registry()), Ok(()));
assert!(parse_registry(registry(json!([])))
.unwrap()
.signers
.is_empty());
}
#[test]
fn degraded_or_changed_sources_and_nonempty_registries_fail_closed() {
fn degraded_changed_or_malformed_sources_fail_closed() {
let anchor_url = "https://example.test/anchor";
let code_url = "https://example.test/code";
let mut degraded = anchor(anchor_url, code_url);
@ -266,39 +641,120 @@ mod tests {
parse_anchor(&wrong_path, anchor_url, code_url),
Err("ANCHOR_INVALID")
);
let mut nonempty = empty_registry();
nonempty["signers"] = json!([{"signerId": "UNVERIFIED"}]);
assert_eq!(parse_registry(json!([])).unwrap_err(), "REGISTRY_INVALID");
assert_eq!(
validate_empty_registry(&nonempty),
Err("TRUSTED_SIGNER_REQUIRES_NATIVE_VERIFIER")
parse_registry(registry(json!([{"signerId":"UNKNOWN"}]))).unwrap_err(),
"REGISTRY_INVALID"
);
}
#[test]
fn native_ed25519_verifier_accepts_only_exact_current_scoped_evidence() {
let pair = key_pair();
let snapshot = LoadedRegistry {
source_commit: COMMIT.into(),
registry: parse_registry(registry(json!([signer(&pair, "ACTIVE")]))).unwrap(),
};
let verified = serde_json::to_value(verify_receipt(
signed_receipt(&pair),
&expectation(),
snapshot,
))
.unwrap();
assert_eq!(verified["status"], "VERIFIED");
assert_eq!(verified["authorizationEnabled"], true);
assert_eq!(verified["sourceCommit"], COMMIT);
let empty = LoadedRegistry {
source_commit: COMMIT.into(),
registry: parse_registry(registry(json!([]))).unwrap(),
};
let denied =
serde_json::to_value(verify_receipt(signed_receipt(&pair), &expectation(), empty))
.unwrap();
assert_eq!(denied["reason"], "NO_TRUSTED_SIGNER");
assert_eq!(denied["authorizationEnabled"], false);
}
#[test]
fn tampering_revocation_time_and_signature_fail_closed() {
let pair = key_pair();
let other = key_pair();
let registry_for = |status| LoadedRegistry {
source_commit: COMMIT.into(),
registry: parse_registry(registry(json!([signer(&pair, status)]))).unwrap(),
};
let mut tampered = signed_receipt(&pair);
tampered["requestId"] = json!("REQ-OTHER");
assert_eq!(
serde_json::to_value(verify_receipt(
tampered,
&expectation(),
registry_for("ACTIVE")
))
.unwrap()["reason"],
"RECEIPT_BINDING_INVALID"
);
assert_eq!(
serde_json::to_value(verify_receipt(
signed_receipt(&pair),
&expectation(),
registry_for("REVOKED")
))
.unwrap()["reason"],
"NO_TRUSTED_SIGNER"
);
assert_eq!(
serde_json::to_value(verify_receipt(
signed_receipt(&other),
&expectation(),
registry_for("ACTIVE")
))
.unwrap()["reason"],
"SIGNATURE_INVALID"
);
let mut stale = expectation();
stale.observed_at_milliseconds += 600_001;
assert_eq!(
serde_json::to_value(verify_receipt(
signed_receipt(&pair),
&stale,
registry_for("ACTIVE")
))
.unwrap()["reason"],
"RECEIPT_BINDING_INVALID"
);
assert_eq!(validate_empty_registry(&json!([])), Err("REGISTRY_INVALID"));
}
#[tokio::test]
async fn native_transport_loads_the_exact_empty_registry_without_enabling_authorization() {
let (anchor_url, code_url) = serve_snapshot();
let client = Client::builder().build().expect("test client");
let receipt = load_with_urls(&client, &anchor_url, &code_url).await;
let projection = serde_json::to_value(receipt).expect("serialize receipt");
assert_eq!(projection["status"], "CURRENT_EMPTY");
assert_eq!(projection["sourceCommit"], COMMIT);
assert_eq!(projection["signerCount"], 0);
assert_eq!(projection["authorizationEnabled"], false);
assert!(projection["reason"].is_null());
let (anchor_url, code_url) = serve_snapshot(registry(json!([])));
let client = Client::builder().build().unwrap();
let receipt =
serde_json::to_value(load_with_urls(&client, &anchor_url, &code_url).await).unwrap();
assert_eq!(receipt["status"], "CURRENT_EMPTY");
assert_eq!(receipt["sourceCommit"], COMMIT);
assert_eq!(receipt["signerCount"], 0);
assert_eq!(receipt["authorizationEnabled"], false);
}
#[tokio::test]
async fn source_and_anchor_failures_return_bounded_unavailable_receipts() {
let listener = TcpListener::bind("127.0.0.1:0").expect("reserve test port");
async fn source_failures_return_bounded_unavailable_receipts() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let unavailable = format!("http://{}/anchor", listener.local_addr().unwrap());
drop(listener);
let client = Client::builder().build().expect("test client");
let source_failure =
let client = Client::builder().build().unwrap();
let failure =
serde_json::to_value(load_with_urls(&client, &unavailable, "http://unused").await)
.unwrap();
assert_eq!(source_failure["status"], "UNAVAILABLE");
assert_eq!(source_failure["reason"], "SOURCE_UNAVAILABLE");
assert_eq!(source_failure["authorizationEnabled"], false);
assert_eq!(failure["status"], "UNAVAILABLE");
assert_eq!(failure["reason"], "SOURCE_UNAVAILABLE");
assert_eq!(failure["authorizationEnabled"], false);
assert_eq!(
load_snapshot(&client, &unavailable, "http://unused")
.await
.err(),
Some("SOURCE_UNAVAILABLE")
);
}
}

View file

@ -56,7 +56,7 @@ describe('PersonaLanguageShellPanel', () => {
repositoryPaths: ['/persona'],
providers: [provider],
developmentId: 'DEV-20260811-010',
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.13',
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.14',
}))
expect(await screen.findByText('我还不能执行:需要当前证据。')).toBeInTheDocument()
expect(screen.getByText('核验当前事实后规划下一步')).toBeInTheDocument()

View file

@ -13,7 +13,7 @@ import {
type LanguageShellViewState,
} from './HotPluggableLanguageShell'
const CURRENT_ARCHITECTURE_ANCHOR = 'HLP-CURRENT-ARCH-001@2026-08-12.13'
const CURRENT_ARCHITECTURE_ANCHOR = 'HLP-CURRENT-ARCH-001@2026-08-12.14'
const DEVELOPMENT_ID = 'DEV-20260811-010'
type Planner = typeof planPersonaLanguageShellGoal
@ -103,9 +103,9 @@ export function PersonaLanguageShellPanel({
personaId,
repositoryPaths,
providers,
// The native registry loader currently proves only an exact empty trust
// source. No native signed-receipt verifier is wired to this controller,
// so it remains system-direct even after deliberation succeeds.
// Native registry loading and signed-receipt verification exist, but
// no verified projection is wired into this controller. The published
// registry is also empty, so this remains system-direct.
requestId: requestId(),
modelInstanceId: `hololake-desktop:${personaId}`,
developmentId: DEVELOPMENT_ID,

View file

@ -11,7 +11,7 @@ const expected = {
repositoryHead: 'a'.repeat(40),
modelInstanceId: 'MODEL-INSTANCE-001',
requestId: 'REQ-001',
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.13',
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.14',
observedAt: Date.parse('2026-08-12T04:35:00+08:00'),
}