pub struct KeyStore { /* private fields */ }Expand description
KeyStore:热可替换的 keys 集合
Implementations§
Source§impl KeyStore
impl KeyStore
Sourcepub fn reload(&self) -> Result<(), KeyStoreError>
pub fn reload(&self) -> Result<(), KeyStoreError>
SIGHUP 热重载:用同一路径重新读文件
Sourcepub fn expand_allowed_card_nums<R, FU, FA>(
&self,
resolver: R,
unresolved_callback: FU,
ambiguous_callback: FA,
) -> (usize, usize, usize)
pub fn expand_allowed_card_nums<R, FU, FA>( &self, resolver: R, unresolved_callback: FU, ambiguous_callback: FA, ) -> (usize, usize, usize)
v1.4.103 (B10): 把每条 key 的 allowed_card_nums (string format) 通过
resolver 解析成 acc_id, 合并进 allowed_acc_ids (in-memory only,
不写回 keys.json — 文件源不变, 重载后再 expand).
resolver(card_num) -> Vec<u64> 由 caller 提供 (典型 closure 持
Arc<TrdCache> 调 find_acc_ids_by_card_num).
行为:
- resolver 返 1 个 acc_id → 加入 allowed_acc_ids (resolved)
- 返 0 个 → 通过
unresolved_callback通知 caller (e.g. log warn) - 返 ≥ 2 个 → 通过
ambiguous_callback通知 caller (loud, skip 该条)
返 (resolved_count, unresolved_count, ambiguous_count).
典型调用 (daemon 启动 GetAccList 成功后):
let cache_clone = trd_cache.clone();
key_store.expand_allowed_card_nums(
|cn: &str| cache_clone.find_acc_ids_by_card_num(cn),
|key_id, cn| tracing::warn!(key_id, card_num=cn, "card_num not found"),
|key_id, cn, candidates| tracing::warn!(key_id, card_num=cn, ?candidates, "ambiguous card_num"),
);Sourcepub fn verify(&self, plaintext: &str) -> Option<Arc<KeyRecord>>
pub fn verify(&self, plaintext: &str) -> Option<Arc<KeyRecord>>
明文校验:遍历所有未过期 key,匹配则返回 KeyRecord 快照
如果 key 设置了 allowed_machines 且本机不在白名单,会打 warn 日志并视为未匹配。
这样做法的代价:攻击者可以通过“能不能过“侧信道区分 key 是否存在 — 我们接受,
因为 plaintext 空间是 256 bit 随机 hex,侧信道没意义。
Sourcepub fn is_configured(&self) -> bool
pub fn is_configured(&self) -> bool
是否已加载 keys 文件(非 empty)
pub fn path(&self) -> Option<&Path>
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool
Sourcepub fn has_any_card_num_restrictions(&self) -> bool
pub fn has_any_card_num_restrictions(&self) -> bool
v1.4.105 eli #4 fix: 当前 KeyStore 是否有任意 key 配置了
allowed_card_nums 限制. 用于 standalone MCP / gRPC / 任何不持
TrdCache 的 keystore consumer 在启动时判断:
false→ 没有 card_num 限制, 跳过 daemonGetAccList+ expand 全流程 (避免无意义的 daemon 请求)true→ 必须连 daemon, 调GetAccList, 通过Self::expand_allowed_card_nums把 card_num resolve 成 acc_id; 否则 fail-closed sentinel{0}会让所有真账户 reject (eli BUG v1.4.104-002: standalone MCP 漏调 expand 导致0757配置的 key 全 reject).
注意: 本方法只检查 raw allowed_card_nums 是否非空 — load_file 阶段
注入的 sentinel allowed_acc_ids = {0} 不算“已 expand“; 只有
caller 真跑过 Self::expand_allowed_card_nums 后才会用 resolved
acc_ids 覆盖 sentinel.
Sourcepub fn get_by_id(&self, id: &str) -> Option<Arc<KeyRecord>>
pub fn get_by_id(&self, id: &str) -> Option<Arc<KeyRecord>>
按 id 查询当前快照中的 key(不做 expiry / machine 校验,调用方自己做)
典型用法:MCP 在启动时 verify(plaintext) 拿到 id,后续每个请求用
get_by_id 取最新记录,这样 SIGHUP 重载 keys.json 后 scope / 限额 /
expires_at 的变更能立刻生效(不用重启进程)。
返回 None 表示 id 在当前文件里不存在(被 remove_key 删掉了),调用方应 视为“key 已吊销“直接拒绝。
注意:此方法不做 machine binding 校验。对于跨 SIGHUP 的 per-msg /
per-tool 复检场景应改用 Self::get_by_id_for_current_machine,确保
SIGHUP 后新加的 allowed_machines 限制立即生效(避免 startup 验过 →
SIGHUP 收紧 → 仍按老 record 放行的语义漂移)。
Sourcepub fn get_by_id_for_current_machine(&self, id: &str) -> Option<Arc<KeyRecord>>
pub fn get_by_id_for_current_machine(&self, id: &str) -> Option<Arc<KeyRecord>>
按 id 查询当前快照中的 key + 立即校验本机 machine binding。
统一生命周期入口: 任何 surface (WS / MCP / REST / gRPC) 在
已 verify-once → 跨 SIGHUP 复检场景下应使用此方法替代裸 Self::get_by_id,
避免如下漂移:
- startup 时
verify(plaintext)检查 machine binding ✅ - SIGHUP reload 把该 key 的
allowed_machines收紧(移除本机指纹) - 后续 per-msg / per-tool 仅调
get_by_id→ 绕过 machine binding → silent unrestricted (反模式 D / pitfall #45 silent-success 同模式)
行为:
- id 不存在 →
None(key 已被 remove_key 吊销,caller 视为吊销拒绝) - id 存在 + machine 校验通过 →
Some(rec) - id 存在 + machine 校验失败 →
None+ warn log(与verify同语义)
不做 expiry 校验 —— pipeline.rs Step 1.5 / caller 自己做(与
get_by_id 行为对齐,仅差 machine 一层)。