# ClipLink Daemon
**`cliplinkd`** — 桌面端守护进程,接收来自手机 ClipLink APP 的文本、图片和文件,写入系统剪贴板并自动粘贴。
## 特性
- **剪贴板拉取**:手机一键获取电脑剪贴板内容(文本/图片/文件),支持大文件确认和进度条
- **手机附件发送**:图片直接写入剪贴板;普通文件安全缓存后作为文件引用写入剪贴板
- **多播发现**:224.0.0.167 多播 + Burst 首发,手机自动发现,无需手动输入 IP
- **PIN 认证**:可选的 PIN 码保护,3 次失败后自动封禁 IP 30 秒
- **后台运行**:`cliplinkd start` / `stop` 一键启停,不占用终端窗口
## 安装
### crates.io
```bash
cargo install krust-cliplinkd
```
安装后二进制文件名为 `cliplinkd`。
### GitHub Releases
从 [Releases](https://github.com/krustd/cliplinkd/releases) 下载预编译二进制:
- `cliplinkd-aarch64-apple-darwin` — macOS Apple Silicon
- `cliplinkd-x86_64-apple-darwin` — macOS Intel
- `cliplinkd-x86_64-unknown-linux-gnu` — Linux
- `cliplinkd-x86_64-pc-windows-msvc.exe` — Windows
### 从源码编译
```bash
git clone https://github.com/krustd/cliplinkd.git
cd cliplinkd
cargo build --release
# 二进制位于 ./target/release/cliplinkd
```
## 配置
### 快速设置(推荐)
```bash
cliplinkd init
```
交互式向导,依次设置:
- **Service name** — 手机端显示的设备名(默认取 hostname)
- **PIN code** — 认证密码,留空则不验证
- **TCP port** — 监听端口(默认 9527,UDP 发现端口自动为 port + 1)
配置保存在 `~/.config/cliplinkd/cliplinkd.toml`。
### 手动编辑
配置文件加载顺序(优先级从高到低):
1. `~/.config/cliplinkd/cliplinkd.toml`
2. `./cliplinkd.toml`(本地覆盖)
```toml
[server]
bind = "0.0.0.0" # 监听地址
port = 9527 # TCP 端口(UDP 发现端口 = port + 1)
[auth]
pin = "123456" # PIN 码;留空则跳过认证
[service]
name = "My Computer" # 手机端显示的设备名,默认取 hostname
[clipboard]
max_fetch_size = 524288 # 文本自动获取阈值(字节),超过此大小需确认;默认 512KB
max_receive_size = 1073741824 # 手机单个附件的接收上限(字节);默认 1GB
```
## 运行
```bash
# 后台运行(推荐)
cliplinkd start # 启动后台守护进程
cliplinkd stop # 停止后台守护进程
cliplinkd status # 查看运行状态
# 前台运行(调试用)
cliplinkd
# 设置日志级别
RUST_LOG=debug cliplinkd
```
### 开机自启
**macOS** — LaunchAgent:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.cliplink.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/cliplinkd</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
```
**Linux** — systemd user service:
```ini
# ~/.config/systemd/user/cliplinkd.service
[Unit]
Description=ClipLink Daemon
[Service]
ExecStart=%h/.local/bin/cliplinkd
Restart=on-failure
[Install]
WantedBy=default.target
```
```bash
systemctl --user enable --now cliplinkd.service
```
**Windows** — 将 `cliplinkd.exe` 放入 `shell:startup` 目录,或注册为 Windows Service。
## 协议
通信格式为 JSON over TCP,`\n` 分隔。
| 方向 | 消息 | 说明 |
|------|------|------|
| 手机→电脑 | `{"type":"auth","pin":"...","device_name":"..."}` | 认证 |
| 电脑→手机 | `{"type":"auth_ok","name":"...","capabilities":[...]}` | 认证成功;新版服务含 `upload-v1` 能力 |
| 电脑→手机 | `{"type":"auth_fail","name":"...","message":"..."}` | 认证失败(含设备名) |
| 手机→电脑 | `{"type":"send","payload":"...","id":"uuid"}` | 发送文本 |
| 电脑→手机 | `{"type":"ack","id":"uuid","status":"sent"}` | 已发送 |
| 电脑→手机 | `{"type":"nack","id":"uuid","status":"error","message":"..."}` | 发送失败 |
| 手机→电脑 | `{"type":"key","key":"enter","id":"uuid"}` | 发送按键(目前仅支持 enter) |
| 手机→电脑 | `{"type":"upload_begin","id":"uuid","kind":"image\|file","filename":"...","size_bytes":123}` | 开始单个附件上传 |
| 电脑→手机 | `{"type":"upload_ready","id":"uuid","chunk_size":49152}` | 服务端允许上传 |
| 手机→电脑 | `{"type":"upload_chunk","id":"uuid","seq":0,"payload_base64":"..."}` | 单块数据;发送下一块前必须等待确认 |
| 电脑→手机 | `{"type":"upload_chunk_ack","id":"uuid","seq":0,"received_bytes":123}` | 数据块确认与进度 |
| 手机→电脑 | `{"type":"upload_finish","id":"uuid","size_bytes":123,"sha256":"..."}` | 完成上传并校验完整性 |
| 手机→电脑 | `{"type":"upload_cancel","id":"uuid"}` | 取消上传 |
| 手机→电脑 | `{"type":"ping"}` | 心跳 |
| 电脑→手机 | `{"type":"pong"}` | 心跳响应 |
| 手机→电脑 | `{"type":"clipboard_query"}` | 查询电脑剪贴板 |
| 电脑→手机 | `{"type":"clipboard_info","content_type":"text\|image\|file\|none",...}` | 剪贴板内容信息 |
| 手机→电脑 | `{"type":"clipboard_fetch","content_type":"text\|image\|file",...}` | 请求获取内容 |
| 电脑→手机 | `{"type":"clipboard_data","content_type":"text\|image\|file","payload_base64":"...",...}` | 返回内容数据 |
## 手机附件接收
- **图片**:目前支持 PNG / JPEG;解码后直接写入系统图片剪贴板,不保存图片副本。
- **普通文件**:写入系统缓存目录 `cliplinkd/uploads`,再将本地文件引用放入剪贴板。过期 7 天的缓存会在下一次附件上传前清理,以保证稍后粘贴仍有效。
- 传输按 48KB 原始数据块串行确认,服务端校验顺序、大小与 SHA-256;取消、断线或校验失败会删除未完成文件。
## 许可
MIT License