ChatView 是一个基于 Ed25519 公钥体系的即时通讯应用,使用 Go 实现,gRPC (HTTP/2 over TCP) 通信,支持多端同时在线。
| 组件 | 技术栈 | 存储 |
|---|---|---|
| Server | Go + gRPC | PostgreSQL |
| Client | Go + Fyne (跨平台桌面 GUI) | SQLite |
| Proto | protobuf | — |
- Ed25519 公钥身份 — 用户以公钥为唯一标识,私钥用 PIN 加密存储在本地,永不离开客户端
- Challenge-Response 认证 — 服务端生成随机挑战,客户端本地签名验证,零知识证明
- 幂等消息发送 — 每条消息携带 UUIDv7 作为幂等键,配合 PostgreSQL serialization failure 自动重试,保证 exactly-once 语义
- 离线 Outbox — 网络中断时消息进入本地 SQLite 队列,后台 worker 自动重试发送
- 实时事件推送 — 服务端 Hub 维护发布/订阅通道,客户端通过 gRPC server-streaming 订阅新消息、好友状态变更、强制下线等事件
- 结构化并发 — 全链路 context 树管理 goroutine 生命周期,登出时级联取消所有后台任务,无泄漏
- 管理员面板 — 用户管理(封禁/解封)、系统广播、统计数据面板
- 多端在线 — 同一公钥可注册多个客户端连接,消息推送到所有在线终端
chatview/
├── proto/ # protobuf 协议定义
│ └── chatview/
│ ├── common/types.proto # 通用类型(枚举、消息体)
│ ├── auth.proto # 认证协议
│ ├── chat.proto # 聊天协议(好友、消息、历史、已读)
│ ├── events.proto # 事件流(新消息、好友状态、强制下线)
│ └── admin.proto # 管理员协议
├── api/ # Go module(生成 + 手写代码)
│ ├── go.mod
│ └── gen/chatview/ # protoc 生成代码(.pb.go, _grpc.pb.go)
├── server/ # 服务端
│ ├── cmd/server/main.go # 入口
│ ├── config.example.yaml # 示例配置
│ ├── configuration.md # 配置文档
│ ├── migrations/ # PostgreSQL DDL 迁移
│ └── internal/
│ ├── account/ # 账户类型定义
│ ├── auth/ # Ed25519 加密工具
│ ├── config/ # 配置加载
│ ├── contextx/ # Context Principal 注入
│ ├── db/ # PostgreSQL 数据层
│ ├── eventhub/ # 事件总线(发布/订阅)
│ ├── interceptor/ # gRPC 拦截器(认证/权限/日志)
│ └── service/ # gRPC 服务实现
├── client/ # 桌面客户端
│ ├── cmd/client/main.go # 入口
│ ├── client.example.yaml # 示例配置
│ ├── configuration.md # 配置文档
│ └── internal/
│ ├── config/ # 配置加载
│ ├── core/ # 业务编排(接口 + 实现)
│ ├── domain/ # 纯数据类型
│ ├── identity/ # Ed25519 密钥管理(PIN 加密)
│ ├── rpcclient/ # gRPC 传输适配
│ ├── storage/ # SQLite 本地缓存 + outbox
│ └── ui/ # Fyne 桌面 UI
├── scripts/gen-proto # Proto 代码生成脚本
└── tools/protoc-*/ # protoc 工具链(本地目录,由 gitignore 排除)
- Go 1.21+
- PostgreSQL(服务端需要)
- protoc(可选,仅修改 proto 时需要)
cd server
# 创建数据库
createdb chatview
# 使用示例配置启动
go run ./cmd/server -config config.example.yaml服务端默认监听 :50051,需要先修改 config.example.yaml 中的 db_dsn 为你的 PostgreSQL 连接串。
cd client
# 连接本地服务端
go run ./cmd/client
# Wayland 下建议添加原生支持
go run -tags wayland ./cmd/client首次启动会显示 PIN 输入界面,可选择创建新身份或导入已有 Ed25519 私钥。
cd server && go test ./...
cd client && go test ./..../scripts/gen-proto详见 PROTO_GUIDE.md。
客户端 服务端
────── ──────
1. RequestChallenge(public_key) → 生成随机 nonce,关联 public_key
2. ← 返回 challenge (bytes)
3. 用 Ed25519 私钥本地签名 challenge
(私钥不发送给服务端)
4. Login(public_key, sig) → 验证 Ed25519 签名
5. ← 返回 session_token + role
之后所有 RPC 在 gRPC metadata 中携带 authorization: Bearer <token>。
UI (sync.go)
└→ Service.SendMessage(ctx, receiver, text)
├─ 生成 clientMessageID(UUIDv7,幂等键)
├─ 创建 pending 状态消息
├─ 在线 → gRPC SendMessage (带幂等键)
└─ 离线 → 写入 SQLite outbox → 后台 worker 重试
服务端 (chat_send.go)
1. 串行化重试(最多 3 次,处理 serialization failure)
2. clientMessageID 去重检查
3. 事务内:next_seq++ → INSERT message
4. 事件推送 → Hub.Push(receiver, NewMessageEvent)
5. 返回 messageId + serverSeq
服务端 Hub(map[pubKey]→[client channel])
├─ Register(pubKey, clientID, chan)
├─ Unregister(pubKey, clientID)
├─ Push(pubKey, event) → 推送到指定用户的所有连接
├─ Broadcast(event) → 推送到所有在线用户
└─ PushAdmins(event) → 仅推送给管理员
客户端 (events.go)
└→ 通过 gRPC server-streaming 订阅事件流
├─ new_message → 刷新消息列表 + 好友列表
├─ friend_status → 刷新好友列表
├─ system_broadcast → 桌面通知
├─ force_offline → 登出
└─ admin_update → 刷新管理员面板
全链路使用 Go context 管理 goroutine 生命周期:
startSessionContext() ← 根 context
├─ startOutboxWorker(ctx) ← 离线消息发送
├─ watchEvents(ctx) ← 实时事件订阅
├─ startOutboxPoller(ctx) ← 定期重试轮询
└─ runSessionTask(snapshot) ← UI 异步任务
└─ snapshot.ctx + sessionVersion ← 双重保护防竞态
服务端同理,Ctrl+C → signal.NotifyContext 取消 → 所有后台 goroutine 优雅退出。
| 键 | 默认值 | 说明 |
|---|---|---|
listen_addr |
:50051 |
监听地址 |
db_dsn |
(必填) | PostgreSQL DSN |
session_ttl |
24h |
会话有效期 |
challenge_ttl |
5m |
登录挑战有效期 |
cleanup_interval |
10m |
过期会话清理间隔 |
admin_pub_key |
空 | 初始管理员公钥 |
| 键 | 默认值 | 说明 |
|---|---|---|
data_dir |
平台相关 | 本地数据目录 |
grpc_target |
127.0.0.1:50051 |
服务端地址 |
grpc_tls |
自动判断 | 是否启用 TLS |
grpc_timeout |
10s |
单次 RPC 超时 |
详见 server/configuration.md 和 client/configuration.md。
推荐阅读顺序(从底层到上层,约 2 小时):
- 协议定义 →
proto/chatview/下 5 个.proto文件 - 服务端 →
server/internal/service/核心业务逻辑 - 客户端核心 →
client/internal/core/业务编排 - 客户端传输与存储 →
client/internal/rpcclient/+client/internal/storage/ - 客户端 UI →
client/internal/ui/
详细的代码阅读地图和技术要点解析见 CODE_GUIDE.md。Proto 代码生成见 PROTO_GUIDE.md。