# FileDrop - 安全无痕文件传输 Web App ## 产品定位 免费、无注册、无历史记录的安全文件传输工具。 - 优先局域网,兼顾公网 - WebRTC 直连优先,TURN 兜底 - 浏览器端端到端加密 - 传完即销毁,不留痕迹 --- ## 第一期范围 ### 要做 - 扫码/短码配对 - WebRTC DataChannel 传输 - 浏览器端 AES-GCM 分块加密 - TURN 回退 - 会话超时自动清理 - 小文件传输(<100MB) ### 不做 - 用户注册/登录 - 历史记录 - 大文件/断点续传 - 付费/套餐 - Postgres - WASM - 对象存储中转 --- ## 技术栈 | 层 | 技术 | |---|---| | 前端 | Lit + TypeScript + Vite | | 后端 | Rust + axum + tokio | | 临时状态 | Redis | | NAT 穿透 | coturn | | 反向代理 | Caddy | | 部署 | Debian 12 + systemd | --- ## 系统架构 ``` Browser A Browser B | | |---- HTTPS / WSS -------------| | | Rust Signaling Server | Redis | coturn ``` ### 职责划分 **浏览器** - 选文件 - 生成会话密钥 - 二维码/短码配对 - WebRTC 建连 - 文件分块加密 - DataChannel 发送/接收 **Rust 服务端** - 创建/加入会话 - WebSocket 信令转发 - 临时会话状态 - 过期清理 - 下发 ICE 配置 **Redis** - 会话临时状态 - 在线状态 - 一次性加入令牌 - TTL 自动过期 **coturn** - STUN/TURN 服务 --- ## 核心流程 ### 1. 创建会话 1. 发送端选择文件 2. 浏览器本地生成 `session_secret` 3. 请求 `POST /api/sessions` 4. 服务端返回 `room_id`、`join_token`、`ws_url`、`ice_servers` 5. 前端生成分享信息: - 短码:`room_id` - 链接:`/join/{room_id}#k={session_secret}&t={join_token}` > `#k=` 在 URL fragment 中,不会发给服务端 ### 2. 加入会话 1. 接收端扫码进入 2. 前端从 fragment 取出 `session_secret` 和 `join_token` 3. 请求 `POST /api/sessions/{room_id}/join` 4. 服务端校验会话状态和 token 5. 成功后建立 WebSocket ### 3. 信令协商 1. 双方连上 `WS /ws` 2. 发送端创建 offer 3. 接收端返回 answer 4. 双方交换 ICE candidate 5. 建立 `RTCDataChannel` ### 4. 文件传输 1. 发送端发送 `file_manifest` 2. 文件切块(64KB ~ 256KB) 3. 每块独立 AES-GCM 加密 4. 通过 DataChannel 发送 5. 接收端逐块解密并缓存 6. 完成后生成下载文件 7. 双方发送完成确认并关闭会话 ### 5. 会话销毁 触发条件: - 传输完成 - 用户主动取消 - 超时(15 分钟) - WebSocket/RTC 长时间断开 --- ## 协议设计 ### HTTP API #### `POST /api/sessions` 创建会话 **请求** ```json { "file_count": 2, "total_size": 1834201 } ``` **响应** ```json { "room_id": "8F4K2P", "join_token": "opaque-token", "ws_url": "wss://app.example.com/ws", "expires_at": "2026-04-09T12:00:00Z", "ice_servers": [ { "urls": ["stun:turn.example.com:3478"] }, { "urls": ["turn:turn.example.com:3478?transport=udp"], "username": "u", "credential": "p" } ] } ``` #### `POST /api/sessions/{room_id}/join` 加入会话 **请求** ```json { "join_token": "opaque-token" } ``` **响应** ```json { "ws_url": "wss://app.example.com/ws", "expires_at": "2026-04-09T12:00:00Z", "ice_servers": [...] } ``` #### `GET /health` 健康检查 ### WebSocket 消息 统一格式: ```json { "type": "offer|answer|ice|ready|cancel|error|ping|pong", "room_id": "8F4K2P", "role": "sender|receiver", "payload": {} } ``` ### DataChannel 消息 控制消息(JSON): - `file_manifest` - 文件清单 - `chunk_ack` - 分块确认 - `transfer_complete` - 传输完成 数据面:`ArrayBuffer`(加密分块) --- ## 安全设计 1. 会话密钥只在浏览器生成和持有 2. 文件名和文件内容都加密 3. 服务端只看到 room_id、连接状态、粗粒度元信息 4. 不保存文件内容 5. 不保存传输历史 6. Redis 全部使用 TTL 7. 所有页面和 WS 强制 HTTPS/WSS 8. TURN 使用临时凭证 9. 前端在完成或取消后清空密钥和缓存 **加密方案** - 会话密钥:32 字节随机 - 密钥派生:HKDF-SHA-256 - 分块加密:AES-GCM - 摘要校验:SHA-256 --- ## 前端页面 | 路由 | 说明 | |---|---| | `/` | 首页:发送/接收入口 | | `/send` | 选文件、创建会话、展示二维码 | | `/join/:roomId` | 扫码进入、确认接收、下载 | | `/expired` | 会话失效提示 | --- ## Redis 设计 ### Key 结构 - `session:{room_id}` - 会话元数据 - `presence:{room_id}:sender` - 发送端在线状态 - `presence:{room_id}:receiver` - 接收端在线状态 - `join_token:{room_id}` - 一次性加入令牌 - `ws:{connection_id}` - WebSocket 连接映射 ### TTL - 会话:15 分钟 - 已完成会话:立即删除或保留 1 分钟缓冲 - WebSocket presence:30-60 秒心跳续期 --- ## 开发里程碑 ### P0 原型 - 创建/加入会话 - WS 信令 - DataChannel 建立 - 文本消息互通 ### P1 小文件传输 - 文件 manifest - 分块发送 - 接收后下载 - 进度显示 ### P2 安全闭环 - 会话密钥生成 - 文件名/内容加密 - 会话销毁 - Redis TTL 清理 ### P3 稳定性 - TURN 兜底验证 - 异常断开处理 - 超时与取消 - Chrome / Safari / iOS 测试 ### P4 上线 - Debian 部署 - HTTPS/WSS - 基础限流 - 基础日志与监控 --- ## 项目结构 ``` filedrop/ ├── Cargo.toml ├── plans/ │ └── phase1-architecture.md ├── server/ │ ├── src/ │ │ ├── main.rs │ │ ├── config.rs │ │ ├── api/ │ │ │ ├── mod.rs │ │ │ ├── sessions.rs │ │ │ └── health.rs │ │ ├── signaling/ │ │ │ ├── mod.rs │ │ │ ├── ws_handler.rs │ │ │ └── room.rs │ │ ├── session/ │ │ │ ├── mod.rs │ │ │ ├── service.rs │ │ │ └── store.rs │ │ └── ice/ │ │ ├── mod.rs │ │ └── config.rs │ └── Cargo.toml ├── web/ │ ├── package.json │ ├── tsconfig.json │ ├── vite.config.ts │ ├── index.html │ └── src/ │ ├── main.ts │ ├── api/ │ │ ├── session-api.ts │ │ └── signaling.ts │ ├── webrtc/ │ │ ├── transport.ts │ │ └── ice.ts │ ├── crypto/ │ │ └── crypto-client.ts │ ├── transfer/ │ │ ├── file-transfer.ts │ │ └── state.ts │ └── ui/ │ ├── pages/ │ │ ├── home-page.ts │ │ ├── send-page.ts │ │ ├── join-page.ts │ │ └── expired-page.ts │ └── components/ │ ├── qr-display.ts │ ├── file-picker.ts │ ├── progress-bar.ts │ └── status-indicator.ts ├── deploy/ │ ├── docker-compose.yml │ ├── Caddyfile │ ├── coturn.conf │ └── filedrop.service └── README.md ``` --- ## 部署拓扑 最小部署: - Caddy/Nginx - axum app - Redis - coturn 域名建议: - `app.example.com`:Web App + API + WS - `turn.example.com`:TURN 服务 --- ## 风险与注意事项 1. Safari 对 WebRTC/DataChannel、后台标签页较敏感 2. TURN 成本需监控,大文件应尽快切应用层密文中转 3. 浏览器内存占用,大文件必须按块处理 4. 断点续传复杂度不适合首版 5. 端到端加密需覆盖文件名和文件列表