架构
从浏览器界面到 LINE RPC,说明每一层分别负责什么。
整体结构
Browser
│ HTTP(常规 UI 同步)
│ WebSocket(通话 PCM bridge)
▼
Desktop Web UI (Vyline/apps/desktop)
│
▼
Backend (Vyline/backend)
├─ account/session orchestration
├─ API / storage / restore / media
├─ plugin runtime
└─ profileBridge / lineService
│
▼
@vyline/protocol ← submodule: vyline-api
├─ login (QR / Email / Token)
├─ transport / headers / RPC
├─ E2EE / identity
├─ Talk / sync / domain facade
└─ Desktop profile updater
│
▼
LINE services
side modules:
@vyline/plugin-sdk ← submodule
@vyline/themes ← submodule
Vyline-Search ← submodule / reverse-engineering toolchain
职责边界
| 层 | 职责 | 主要位置 |
|---|---|---|
| UI | 聊天、设置、账号、主题等界面显示 | Vyline/apps/desktop |
| Backend | HTTP API、账号状态、DB、媒体、restore、plugin runtime | Vyline/backend |
| Protocol | LINE RPC、login、transport、E2EE、Talk domain | Vyline/packages/protocol |
| Plugin | 扩展的类型、权限与 lifecycle | Vyline/packages/plugin |
| Themes | VyTheme preset/token | Vyline/packages/themes |
| Tools | Desktop LINE 的 version/unpack/xref/decompile 辅助工具 | tools |
从 Web 到 LINE
- UI 调用 Backend 的 BFF API。
- Backend 根据 account ID 取得已登录 session。
lineService或 domain facade 将处理交给 Protocol。- Protocol 根据 device mode、transport 与 E2EE 状态向 LINE 发送 RPC。
- 取得的 chat/message 写入 Backend 的账号专用 SQLite。
- Frontend 通过 bootstrap 与增量 polling 合并到 store;通话使用专用 WebSocket。
启动与 session 恢复
- Bun 启动
Vyline/backend/src/index.ts,初始化 Hono router、static UI 与 WebSocket upgrade handler。 clientManager读取已保存的 account/session,恢复可继续使用的 session。- activate session 后,准备包含 E2EE/transport 状态的 Protocol client,并启动后台 Talk sync。
- Browser 选择账号后,先从
/line/:accountId/bootstrap读取本地 SQLite 中的 chat/message preview。 - 首次显示后,
useVylineSync同步新 event 与 active chat delta,只把必要部分 merge 到 store。
首次页面不应每次都等待 LINE RPC。bootstrap 先从本地 DB 立即 hydrate,remote fetch 再根据 freshness 或用户操作在之后执行。
接收与同步流程
LINE Talk sync
↓
clientManager fetch-ops loop
↓
lineService.processFetchedOperations
├─ decrypt / normalize
├─ 写入 SQLite
└─ 加入 event buffer
↓
GET /line/:accountId/events/poll?cursor=...
↓
Frontend pollIncoming()
↓
Zustand store / active chat
active chat 在需要时还会通过
GET /messages/:chatMid/delta?after=...
继续追踪增量
Backend 把常规 Talk RPC、background poll、urgent fetch 与 send 分成不同执行路径,由 clientManager 协调,避免同步任务不必要地阻塞发送。
发送流程
Composer
↓ HTTP
POST /line/:accountId/send
↓
api/line.ts 输入验证 / HTTP response
↓
lineService message 生成 / E2EE / retry / 保存
↓
@vyline/protocol TalkService / E2EE RPC
↓
LINE
↓
结果写入 SQLite / store
api/line.ts 是 BFF 边界。LINE 特有的 retry 与 E2EE 处理交给 service/lineService.ts,不堆在 route 中。Protocol 的原始 Thrift 类型也不会直接返回给 UI,而是先规范化成 Backend/Frontend 使用的类型。
媒体
图片、视频、音频、文件把 message metadata 与 binary 内容分开处理。Protocol/Backend 负责 LINE object storage 路径与 E2EE,Browser 通过 Backend media endpoint 取得内容。Docker 中保存的 media/cache 放在 /app/storage,account 状态与 chat DB 等放在 /app/data。
Browser 与 Backend 的信任边界
loopback bind 时,owner access 被视为本地访问。非 loopback bind 即使没有修改 VYLINE_LAN_ACCESS,也会按 remote deployment 处理,BFF 要求 installation-bound subdevice session。Browser 将 installation ID 与 session 绑定,因此只把 token 复制到另一个 browser 不能复现同一个 session。
VYLINE_TRUST_REMOTE_OWNER=true 只用于到达路径已由其他层强约束的环境,例如 Tailscale ACL 或 Cloudflare Access。它不是普通 LAN/Internet 公网暴露开关。
主仓库与 submodule
protocol、plugin、themes、tools 是 Git submodule。Backend、Desktop UI、共享类型、CLI 等则属于主仓库 workspace。单独对 Protocol 做类型检查时,仍需要 @vyline/line-types 等同级 workspace package。
源码地图
| 要追踪的处理 | 入口 | 接着查看 |
|---|---|---|
| 界面数据读取 | apps/desktop/src/api/client.ts | hooks/useLineData.ts / useVylineSync.ts |
| HTTP endpoint | backend/src/api/line.ts | service/lineService.ts |
| account/session | backend/src/line/clientManager.ts | @vyline/protocol login/client |
| 持久化 | backend/src/storage/chatStore.ts | chatStoreSqlite.ts / media storage |
| LINE RPC | packages/protocol/src/dictionary/rpcMap.ts | stack → domain → Backend |
| Desktop 更新跟踪 | packages/protocol/src/modules.map.ts | tools / analysis docs |
仓库边界的详细说明见 子模块与源码地图。LINE 通信属于 Protocol,持久化和应用业务属于 Backend,preset 属于 Themes,Desktop 规格调查属于 Tools。