init: FileDrop phase1 architecture and scaffold

- 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
This commit is contained in:
2026-04-09 10:32:06 +08:00
commit 4b34a85599
34 changed files with 1561 additions and 0 deletions

View File

@@ -0,0 +1,392 @@
# 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 presence30-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. 端到端加密需覆盖文件名和文件列表