feat(edge/media): scanner fast-path + stateless HMAC media token
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.key,OpenOptions::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
两个生产级痛点:
-
import scanner 卡死风险:单
ffprobe子进程同步调用、无 timeout,一个坏视频卡死整个扫描循环,后续文件再也不入库。 -
重启窗口 URL 失效:每次 systemd 重启 / updater 升级(1–10s 窗口),iOS 客户端持有的
download_url集体 401。
Verification
-
cargo fmt --all --check干净 -
cargo clippy --workspace --all-targets -- -D warnings0 警告 -
cargo test --workspace:98 passed / 27 suites(含新增 token boundary、scanner tick cap、metadata_state legacy compat、grant_key 权限校验测试) - 远端
lucy-npc(Linux x86_64,hostnameainpc)smoke test:daemon 启动 →/healthz+/v1/local/info200 →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 + chmodrace window -
测试设备:首次启动 grant.key应为 0o600,重启后复用同一 key