Skip to content

feat(edge/media): scanner fast-path + stateless HMAC media token

陈厚宏 requested to merge feat/media-resilience into main

Summary

  • Scanner fast-path:import scanner 拆 fast/slow,assets 立即可见;ffprobe 由后台 worker 慢补,强制 15s timeout,单坏视频不再卡死整个扫描循环。
  • Stateless HMAC media token:进程重启(systemd / updater)不再让 iOS 客户端在飞的 download/upload URL 集体失效。Token 验证 constant-time、有 size cap、HMAC 验证早于任何 payload decode。

What changed

  • 新增 crates/lucy-edge/src/grant_key.rs:32 字节 HMAC 密钥持久化到 <data_dir>/grant.keyOpenOptions::create_new + mode(0o600) 原子创建;启动检测到现有文件权限不为 0600 直接拒绝启动。
  • crates/lucy-edge/src/media.rs:
    • 删除 grants: HashMap<String, MediaGrant>,改用 sign_grant / verify_grant(HMAC-SHA256 over base64url(JSON(payload)) ASCII bytes,签名 32 字节 constant-time 校验)。
    • build_import_descriptor 为 fast path(mime/sha256/size)+ 后台 enricher worker;StoredMediaRecord metadata_state: Pending|Done|Failed(serde default = Done,向后兼容旧 manifest)。
    • probe_media_metadata 改 async + tokio::process::Command + tokio::time::timeout(15s);spawn 后所有错误路径显式 kill().await + wait().await
    • 新增 probe_semaphore(K=1)和 hash_semaphore(K=2);scanner 主循环 take(MAX_IMPORTS_PER_TICK=8),与 helper 共用同一份代码(生产 / 测试都调 run_one_scanner_tick)。
  • crates/lucy-edge/Cargo.toml:加 hmac = \"0.12\"
  • crates/lucy-edge/src/lib.rs:启动 build_media_state load_or_create_grant_key
  • 文档 docs/progress.md 加韧性改造完成条目; docs/deployment.md identity.key 旁补充 grant.key 运维警告(不要删 / 0600 必需 / 删除会让在飞 URL 集体失效)。
  • .omc/plans/media-resilience/:归档 4 份 Plan 设计文档(与生产路径解耦,仅参考)。

Why

两个生产级痛点:

  1. import scanner 卡死风险:单 ffprobe 子进程同步调用、无 timeout,一个坏视频卡死整个扫描循环,后续文件再也不入库。
  2. 重启窗口 URL 失效:每次 systemd 重启 / updater 升级(1–10s 窗口),iOS 客户端持有的 download_url 集体 401。

Verification

  • cargo fmt --all --check 干净
  • cargo clippy --workspace --all-targets -- -D warnings 0 警告
  • cargo test --workspace98 passed / 27 suites(含新增 token boundary、scanner tick cap、metadata_state legacy compat、grant_key 权限校验测试)
  • 远端 lucy-npc(Linux x86_64,hostname ainpc)smoke test:daemon 启动 → /healthz + /v1/local/info 200 → grant.key 创建 mode 0o600 size 32 → 重启复用同 key 健康
  • Codex review 4 轮,BLOCKER × 2 + HIGH × 1 + MEDIUM × 3 + NIT × 1 全部修复

Test plan

  • Reviewer: git fetch origin feat/media-resilience && git checkout feat/media-resilience
  • cargo test --workspace 期望 98 pass
  • 检查 media.rs::verify_grant:HMAC verify 必须早于 payload base64 decode
  • 检查 grant_key.rs OpenOptions::create_new + mode(0o600),没有 fs::write + chmod race window
  • 测试设备:首次启动 grant.key 应为 0o600,重启后复用同一 key

Merge request reports