docs: 将仓库代码注释统一为中文
CI / docs-check (push) Canceled after 0s
CI / backend-test (push) Canceled after 0s
CI / service-test (push) Canceled after 0s
CI / frontend-test (push) Canceled after 0s
CI / rust-core (push) Canceled after 0s
CI / docs-check (pull_request) Canceled after 0s
CI / backend-test (pull_request) Canceled after 0s
CI / service-test (pull_request) Canceled after 0s
CI / frontend-test (pull_request) Canceled after 0s
CI / rust-core (pull_request) Canceled after 0s

This commit is contained in:
2026-09-10 00:40:56 +08:00
parent 51c592841d
commit d703ab64e3
249 changed files with 707 additions and 900 deletions
+18 -26
View File
@@ -1,7 +1,7 @@
//! Device-local Stronghold broker. No public IPC returns secret bytes.
//! 设备本地 Stronghold 代理;任何公开 IPC 都不会返回机密字节。
//!
//! Stronghold Store contains AEAD ciphertext, including while unlocked. Snapshot
//! and salt are one atomic envelope, so password changes cannot tear two files.
//! Stronghold 存储即使在解锁期间也只包含 AEAD 密文。快照与盐构成一个原子整体,
//! 避免密码变更使两个文件处于不一致状态。
use argon2::{Algorithm, Argon2, Params, Version};
use chacha20poly1305::{
aead::{Aead, Payload},
@@ -157,8 +157,7 @@ pub struct CredentialId {
}
impl CredentialId {
/// Preserve opaque legacy references. Hashed Plugin/MCP IDs remain isolated
/// from Provider IDs; only the trusted Core adapter can use these aliases.
/// 保留不透明的遗留引用。散列 Plugin/MCP ID 与提供商 ID 保持隔离;只有受信任的 Core 适配器才能使用这些别名。
pub fn legacy(id: &str) -> Self {
let scope = if let Some(owner) = id.strip_prefix("plugin.") {
Scope::Plugin(owner.into())
@@ -348,16 +347,14 @@ fn verify_migration_backup(
pub struct CredentialBroker {
path: PathBuf,
unlocked: Option<Unlocked>,
// Separate stable inode: snapshots are atomically replaced, so locking the
// snapshot itself would not protect the next writer after replacement.
// 单独的稳定索引节点:快照被原子替换,因此锁定快照本身不会保护替换后的下一个写入者。
ownership: Option<fs::File>,
lock_epoch: Arc<AtomicU64>,
unlocked_epoch: u64,
}
impl CredentialBroker {
/// Source comes from the native file picker, never a raw WebView path.
/// Import is idempotent; conflicting IDs stop the entire transaction.
/// 源来自本机文件选择器,而不是原始 WebView 路径。导入是幂等的;冲突的 ID 会停止整个事务。
pub fn import_fernet(
&mut self,
directory: &Path,
@@ -459,8 +456,7 @@ impl CredentialBroker {
}
}
fs::create_dir_all(&backup).map_err(|_| "MIGRATION_BACKUP_FAILED")?;
// Backups contain ciphertext; the legacy key is sealed under the already
// unlocked device key, rather than adding another plaintext master.key.
// 备份包含密文;旧密钥被密封在已解锁的设备密钥下,而不是添加另一个明文 master.key。
let mut nonce = [0u8; 12];
rand::rngs::OsRng
.try_fill_bytes(&mut nonce)
@@ -506,7 +502,7 @@ impl CredentialBroker {
state.state = "copied".into();
persist_state(&journal, &state)?;
checkpoint("copied")?;
// Re-open the committed Stronghold snapshot, not the in-memory cache.
// 重新打开提交的 Stronghold 快照,而不是内存缓存。
let envelope = fs::read(&self.path).map_err(|_| "MIGRATION_VERIFY_FAILED")?;
let mut temporary =
tempfile::NamedTempFile::new_in(&backup).map_err(|_| "MIGRATION_VERIFY_FAILED")?;
@@ -552,8 +548,7 @@ impl CredentialBroker {
result
}
/// Deletes only the verified legacy files and this migration's encrypted backup.
/// The native Host must obtain explicit user confirmation for source_sha256 first.
/// 仅删除已验证的旧文件和此迁移的加密备份。本机 Host 必须首先获得 source_sha256 的明确用户确认。
pub fn cleanup_fernet(
&mut self,
directory: &Path,
@@ -733,7 +728,7 @@ impl CredentialBroker {
let source = CredentialId::legacy(old);
let target =
CredentialId::legacy(new.as_str().ok_or("HOST_REQUEST_INVALID")?);
// ID migrations cannot change Provider/Plugin/MCP families.
// ID 迁移无法更改提供商/Plugin/MCP 系列。
if std::mem::discriminant(&source.scope)
!= std::mem::discriminant(&target.scope)
{
@@ -750,8 +745,7 @@ impl CredentialBroker {
moves.push((source_key, target_key, value));
}
}
// Reject cycles/overlapping source+destination rather than deleting
// a newly written value midway through a multi-ID migration.
// 拒绝循环/重叠源+目标,而不是在多 ID 迁移中途删除新写入的值。
if moves.iter().any(|(old, new, _)| {
old != new && moves.iter().any(|(source, _, _)| source == new)
}) {
@@ -879,7 +873,7 @@ impl CredentialBroker {
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
options.share_mode(0x1 | 0x2); // Do not allow replacing the held lock file.
options.share_mode(0x1 | 0x2); // 不允许替换保留的锁定文件。
}
#[cfg(unix)]
{
@@ -906,7 +900,7 @@ impl CredentialBroker {
let mut salt = [0u8; 32];
salt.copy_from_slice(&data[8..40]);
let session = Unlocked::derive(password, salt)?;
// Backups can be on read-only media. This temporary file contains ciphertext only.
// 备份可以位于只读介质上。该临时文件仅包含密文。
let mut temp = tempfile::NamedTempFile::new().map_err(|_| "CREDENTIAL_IO_FAILED")?;
temp.write_all(&data[40..])
.map_err(|_| "CREDENTIAL_IO_FAILED")?;
@@ -921,7 +915,7 @@ impl CredentialBroker {
Ok(session)
}
/// Native picker selected destination; backup is encrypted and never overwrites.
/// 本机选择器选择的目的地;备份已加密并且永远不会覆盖。
pub fn backup(&self, destination: &Path) -> Result<()> {
self.session()?;
let parent = destination.parent().ok_or("CREDENTIAL_PATH_INVALID")?;
@@ -938,8 +932,7 @@ impl CredentialBroker {
Ok(())
}
/// Validate every record before atomic replacement; preserve the previous encrypted file.
/// Caller must obtain explicit confirmation through the native dialog.
/// 在原子替换之前验证每条记录;保留之前的加密文件。调用者必须通过本机对话框获得明确的确认。
pub fn restore(&mut self, source: &Path, password: Zeroizing<Vec<u8>>) -> Result<usize> {
if !self.is_locked() {
return Err("CREDENTIALS_MUST_LOCK".into());
@@ -976,7 +969,7 @@ impl CredentialBroker {
previous.keep().map_err(|_| "CREDENTIAL_IO_FAILED")?;
}
session.persist(&self.path)?;
// Restoration deliberately leaves the vault locked; no implicit permission grant.
// 恢复特意将金库锁定;没有隐式许可授予。
Ok(keys.len())
}
@@ -996,7 +989,7 @@ impl CredentialBroker {
.and_then(|_| session.persist(&self.path));
if result.is_err() {
self.lock();
} // Never serve uncommitted memory after disk failure.
} // 磁盘故障后切勿服务未提交的内存。
result
}
pub fn delete(&mut self, id: &CredentialId) -> Result<()> {
@@ -1011,8 +1004,7 @@ impl CredentialBroker {
}
result
}
/// Internal consumers must supply the scope established by the Host dispatcher.
/// This method must never be registered as a Tauri command.
/// 内部消费者必须提供 Host 调度程序建立的范围。此方法绝不能注册为 Tauri 命令。
pub fn resolve(&self, caller: &Scope, id: &CredentialId) -> Result<Option<Zeroizing<Vec<u8>>>> {
if caller != &id.scope {
return Err("CREDENTIAL_SCOPE_DENIED".into());