自建中继服务器
中继服务器在你的设备之间搬运已经加密的剪贴内容。它看到的只有房间哈希和 密文,而且什么都不存 —— 但这件事你不必只凭信任,因为你可以自己跑一个。 它就是一个 Python 文件,没有数据库。
跑起来
docker run -p 8000:8000 ghcr.io/akshaynikhare/realtimeclipboard-relay
或者从一份克隆直接跑,连容器都不用:
pip install -r backend/requirements.txt
python -m uvicorn main:app --app-dir backend --port 8000
整个中继服务器就这些。没有数据库,没有卷,没有迁移步骤,也没有什么要备份 的 —— 每个会话都活在内存里,进程一退就没了,这是设计如此,不是能力不足。
确认它跑起来了
curl http://localhost:8000/health
它会回一个实例标识。那个字段不是装饰:客户端正是靠它才能发现自己在会话中途 被负载均衡换到了另一个副本上,而这恰恰是这套设计必须小心的失败方式 —— 见下面关于副本的那一节。
把应用指过去
- 打开应用,进入 设置 → 中继服务器。
- 填你自己的中继地址 —— 本地测试用
ws://localhost:8000, 正式的用wss://clip.example.com。 - 它一答复,状态栏就显示已连接。在第二台设备上照做一遍,在两者之间 同步一条剪贴内容;整个过程没有碰过公共中继服务器。
命令行客户端用 --relay 或
REALTIMECLIPBOARD_RELAY 做同一件事。
TLS 和一个真实域名
cd deploy
REALTIMECLIPBOARD_DOMAIN=clip.example.com docker compose up -d
deploy 目录 里有一个 compose 文件,它把 Caddy 放在中继服务器前面并自动申请证书, 旁边还有一份给 Kubernetes 用的 Helm chart。
TLS 在实践中不是可选项。浏览器只在安全上下文里提供
crypto.subtle,所以一个通过普通 HTTP 从局域网地址打开的页面
连密钥都推导不出来 —— 无论中继服务器做什么,应用在那里都不可能工作。
扩容之前
把副本数钉死在 1,或者给它一个 Redis。一个房间只存在于服务它的那个
进程的内存里,所以轮询负载均衡后面的两个副本,会把一个会话的两半分到两台
机器上,谁也看不见谁。表现出来就是剪贴内容悄无声息地不到,看着像客户端的
bug,其实不是。Helm chart 会拒绝在没有 Redis 的情况下接受
replicaCount > 1,正是为了这个。
自建说明
完整讲了环境变量和高可用的路子。
它不工作的时候
显示已连接,但剪贴内容一直不来
几乎总是副本不止一个、又没有共享状态 —— 见上面。把每台设备拿到的
/health 标识对一下:如果不一样,答案就在这里。
应用根本连不上它
两种可能。页面自己的 Content-Security-Policy 列出了它允许连接的中继来源,
所以你自建的那份静态站点需要在 index.html、
app.html 以及 src/pages/ 下的每一个页面里更新
connect-src。而如果你用的是官方站点配自己的中继服务器,
官方那份策略不会放行 —— 两半都要自建。
连上了又反复掉线
反向代理没有转发 WebSocket 升级。客户端随后退回到 server-sent events 加
POST,继续能用,只是慢一些,所以这个问题看起来是变迟钝而不是失败。把
Upgrade 和 Connection 转发过去。
空闲的会话消失了
房间一空就会被回收。这是有意的 —— 什么都不留的中继服务器,也就没有什么 可泄露的。
关掉它
docker compose down # or: docker rm -f <container>
没有数据要删。中继服务器持有过的一切都在内存里,而且都是密文。
完整的运维说明,包括环境变量表和高可用方案: docs/SELF-HOSTING.md。 所有平台:安装指南。 这一页有错或者过时了? 提一个 issue。