- Rust axum signaling server with WebSocket support - Lit + TypeScript frontend with Vite - Redis session storage with TTL - WebRTC transport and crypto client stubs - Phase1 architecture plan in plans/ - Deploy directory structure prepared
7.7 KiB
7.7 KiB
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. 创建会话
- 发送端选择文件
- 浏览器本地生成
session_secret - 请求
POST /api/sessions - 服务端返回
room_id、join_token、ws_url、ice_servers - 前端生成分享信息:
- 短码:
room_id - 链接:
/join/{room_id}#k={session_secret}&t={join_token}
- 短码:
#k=在 URL fragment 中,不会发给服务端
2. 加入会话
- 接收端扫码进入
- 前端从 fragment 取出
session_secret和join_token - 请求
POST /api/sessions/{room_id}/join - 服务端校验会话状态和 token
- 成功后建立 WebSocket
3. 信令协商
- 双方连上
WS /ws - 发送端创建 offer
- 接收端返回 answer
- 双方交换 ICE candidate
- 建立
RTCDataChannel
4. 文件传输
- 发送端发送
file_manifest - 文件切块(64KB ~ 256KB)
- 每块独立 AES-GCM 加密
- 通过 DataChannel 发送
- 接收端逐块解密并缓存
- 完成后生成下载文件
- 双方发送完成确认并关闭会话
5. 会话销毁
触发条件:
- 传输完成
- 用户主动取消
- 超时(15 分钟)
- WebSocket/RTC 长时间断开
协议设计
HTTP API
POST /api/sessions
创建会话
请求
{
"file_count": 2,
"total_size": 1834201
}
响应
{
"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
加入会话
请求
{
"join_token": "opaque-token"
}
响应
{
"ws_url": "wss://app.example.com/ws",
"expires_at": "2026-04-09T12:00:00Z",
"ice_servers": [...]
}
GET /health
健康检查
WebSocket 消息
统一格式:
{
"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(加密分块)
安全设计
- 会话密钥只在浏览器生成和持有
- 文件名和文件内容都加密
- 服务端只看到 room_id、连接状态、粗粒度元信息
- 不保存文件内容
- 不保存传输历史
- Redis 全部使用 TTL
- 所有页面和 WS 强制 HTTPS/WSS
- TURN 使用临时凭证
- 前端在完成或取消后清空密钥和缓存
加密方案
- 会话密钥: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 + WSturn.example.com:TURN 服务
风险与注意事项
- Safari 对 WebRTC/DataChannel、后台标签页较敏感
- TURN 成本需监控,大文件应尽快切应用层密文中转
- 浏览器内存占用,大文件必须按块处理
- 断点续传复杂度不适合首版
- 端到端加密需覆盖文件名和文件列表