本 Changelog 自 2026-05-22 起以 HTML 格式维护。此前之变更可从 main 分支 git log 取得;下表为近期较显著之 commit。
2026-06-26 — 鉴权并入 Auth Broker:委派设备会话统一模型 · 四端迁移 · 显示名与头像修复
cdrop 的登录 / 会话 / 设备 / 令牌生命周期并入既有 Auth Broker(边缘身份中介),删去 cdrop 内的自建鉴权副本;浏览器、桌面、手机一律收敛为同一套“委派设备会话”模型,统一管理;并修复部分客户端显示名为账户 UUID、头像缺失的回归。
- 后端委托 Auth Broker(路径 A):删自建 OIDC 交换 / 自签会话 / step-up / shortcut /
web_sessions/accounts及验证链,cdrop 不再存任何凭证;鉴权中间件改读边缘broker { app cdrop }注入的X-Auth-Subject/Name/Avatar/Scope/Meta头。新增internal/brokerclient(委托铸造 / 吊销 / 续期 / 列举) - 统一会话模型“委派设备会话”:每个客户端 = 一条带
meta(device_id) +label(设备名) 的 broker 机器会话,Broker 作设备会话唯一注册表。浏览器全局 SSO、桌面设备授权(RFC 8252 loopback PKCE)、扫码批准,登录后一律“代铸”出设备会话(同一 device_id 幂等轮换、不再堆重复设备);cdrop 退化为薄覆盖层,会话真相与吊销授权一律走 Broker - 设备管理:浏览器 / 桌面 / 手机统一进一个设备列表,显示在线、当前设备与类型,可吊销、可改名(同 device_id 重铸换名、不产生重复行)。修复迁移引入的一组回归:设备列表为空 / 重复无名设备 / 看不到在线的另一台 / 吊销报“设备不存在”
- 显示名与头像修复:经 Broker 端到端补
X-Auth-Name/X-Auth-Avatar,浏览器与桌面恢复真实显示名与头像(此前部分客户端显示账户 UUID、无头像)。桌面新增启动期HealIdentity——注入前从/api/me自愈旧版烤进会话的陈旧身份并持久化,根治偶发 UUID - 四端迁移:后端、Web 前端、iOS、桌面(Wails)客户端同步迁移;存量会话于翻闸时失效、需重新登录
- commit
10cf36e(main)
2026-06-23 — P2P 传输修复:接收混合落盘 sink · 发送进度与完成语义 · 文字累积 · 速度显示
修复 iOS PWA → Mac 桌面客户端大文件 P2P 传输约 15MB 处死锁,及随之暴露的两处发送端语义问题;并修一类前端 CJK 文字累积 bug、为传输卡片增设实时速度。
- 接收端混合有界内存 sink(桌面):
wails://自定义 scheme 无持久 WebView 存储 → OPFS 降级、createWritable回压不畅,把接收 promise 链堵死、饿死dc.onmessage→ 接收方 SCTPrwnd=0→ 发送方bufferedAmount卡死 16MiB(即“PWA 16.0MB / Mac 0B、约 15MB 卡住”)。改为:小文件(暂存 < 256MB)纯内存、close整文件过 Go 写盘;超上限大文件流式 spill 到 Go 临时文件(每 64MB 一批),内存恒定有界。浏览器 / iOS 接收仍走 OPFS(其内存预算紧、又无 Go 落盘,OPFS 才有意义) - 桌面流式落盘:
download.go新增Begin/Append/Finalize/AbortStreamingDownload(复用SaveDownload的目录解析 / 文件名 sanitize / 不可写回退 / reveal,临时文件与目标同目录便于 finalize 原子 rename),app.go加 4 个绑定 +net/desktop.tsbase64 封装;IncomingSink.close()返回SinkResult(blob| 已落盘path),deliverIncoming统一落地;relay 接收一并改用,桌面大文件经中继也有界落盘。含单测download_stream_test.go - 发送端进度按“已交付字节”计:原先用
bytesSent(已塞进本地 16MiB buffer 的量),buffer 被秒满后定格、长 backpressure 等待期误报“疑似卡住”。改用bytesSent − bufferedAmount(已离开本地 buffer ≈ 已交付)+ 250ms 轮询在等待期持续刷新,进度随接收端实际收程平滑爬升、不误判 stalled - 发送端等抽干再宣告完成:原先送完入 buffer 即
setState("completed"),此时仍可能有多达 16MiB 在途,与接收端“仍在下载”状态不一致。改为送完 + done 帧后等bufferedAmount落到 0(字节已全部交付网络)再宣告完成 - 前端动态 CJK 文字累积修复:聚珍 shim 把会就地更新的 CJK 文本节点 charify 拆段后,旧碎片成了 React 不再追踪的兄弟节点残留、每更新一次累积一个多余字(典型“发送到 X”切设备后多出的“到”)。新增
DynText(key=文本内容,内容一变即整体卸载旧 span、连同 shim 碎片,再挂新 span 交 shim 处理一次——既消累积又保留中西排版,区别于data-jz-skip那样关掉排版),应用于发送按钮 / 设备名 / 在线数 / 相对时间 / 会话与令牌活跃时间 / 消息计数 / 剪贴板来源 / 问候语等就地更新处 - 传输卡片实时速度:store
upsertTransfer据相邻样本的字节 / 时间差分 + EMA(α=0.3)平滑算bytesPerSec;TransferRow仅在字节流动且未卡死、且速率为正时以“X/秒”展示(点分隔 meta 行追加),卡死时既有“似乎卡住了”状态已表达停滞、不显误导性 0 速 - commit
0451549(main)
2026-06-22 — 账户与登录子系统:扫码快登 · 自签会话 · OIDC 统一 · 即时吊销 · 设备=会话
“OAuth 为账户来源与凭证提供者、cdrop 自维护薄账户层 + 自签会话”的折中架构落地(蓝本见仓库 AUTH.md)。一台设备 = 一条会话,吊销即时生效、被吊销设备自动回到未登录态。
- 扫码快速登录:陌生设备显码、已登录手机扫码批准(微信 / WhatsApp 网页版模型)。三密钥分离(
request_id/approval_code/ 仅新设备私有的poll_secret),偷拍 QR 无法领取会话;会话只经新设备自身轮询连接下发 cookie。两档:“信任此设备”(scope=full、7 天滑动) / “仅此次”(scope=guest、1 小时受限),默认偏安全的“仅此次”。访客会话服务端强制受限(requireFullSession守门) - 薄账户层 + 自签会话令牌:新增
accounts表(cdrop 侧账户数据、不含凭证,存稳定match_key供管理员开启迁移时跨源关联);web_sessions泛化出kind/scope/granted_by/stepped_up_at。access token 改 cdrop 自签 HS256(密钥自CDROP_SESSION_SECRET域分离派生),脱离 IdP refresh、短 TTL(默认 900s)、载sid - OIDC 统一到自签 token:浏览器登录(含 OIDC)一律持自签 token ——
/auth/exchange先 JWKS 验id_token签名(唯一信任锚)再签自签 token,/auth/refresh仍打 IdP 轮换 refresh_token 但回自签 token。RS256 退出浏览器每请求热路径,仅保留分支供桌面端 loopback 直带 IdP token - 吊销即时 + 设备自知:
verifySelfToken每请求按sid查会话行,行删即拒 → 吊销在下次请求即生效(不等 TTL)。前端仅在“服务端以 401 确证失效”时清登录态回登录页(短期连接失败 / 5xx 不误清);离线设备下次使用即知,在线设备经Hub.Kick关流、约 1s 重连撞 401 即知 - 设备列表 = 会话列表:“登录会话”面板统一
web_sessions与原生客户端设备(桌面 / iOS,自devices登记呈现,native=true)。吊销网页会话连带删其设备登记、即时从所有列表消失;孤儿browser设备由清扫器(DeleteOrphanBrowserDevices)自动清除。原生设备“登出”走DELETE /api/devices/{name}(同要求 step-up) - 2FA step-up 钩子(默认关,
CDROP_STEP_UP_ENABLED):批准扫码设备 / 吊销会话 / 注销设备等敏感动作要求一次现场prompt=login再认证(provider 的 2FA 即在此过),按会话绑定、StepUpMaxAgeSeconds(默认 300s)窗口内不复要求。auth_time可选(Casdoor 不下发) - 应用内扫码器 + 聚珍崩溃修复:登录页内置
getUserMedia+ jsqr 扫码(不再外跳系统应用、避免开错浏览器);扫码 / 批准等三状态机页整页跳过聚珍(data-jz-skip),修 MutationObserver 与 React 重渲染同子树冲突的insertBefore崩溃 - 蓝本
AUTH.md;commit 待提交
2026-06-18 — 推送通知:网页 / PWA Web Push + 桌面原生通知
页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为“页面打开走应用内提示,关闭则系统通知”,三端各自落地。
- 网页 / PWA(Web Push, VAPID):服务端在事件未经 SSE 投递到设备时(即该设备页面已关)发 Web Push —— 收到文件、消息、传输完成 / 失败均可。新增
push_subscriptions表(含locale,主键为 endpoint 的 SHA-256 以幂等 upsert)+/api/push/{vapid-key, subscribe}端点 +internal/push发送器(404 / 410 自动清理死订阅)。文案按订阅设备语言在服务端渲染(中简 / 中繁 / 英)。覆盖浏览器 / Android / iOS 16.4+ 已安装 PWA - 桌面客户端原生通知:仅窗口非前台时弹原生系统通知(前台仍走应用内提示),复用 WebView 事件分发 + 客户端本地化。macOS 走
UNUserNotificationCenter(未签名时安全空操作,待签名 / 公证后显示)、Windows 走原生 toast - 判定复用既有信号:网页端“页面是否打开”直接取
Hub.SendTo的投递结果,零额外状态;离线消息改返 202,文本随推送送达 - 开关与配置:设置页新增“推送通知”面板(仅浏览器 / PWA),登录后自动把既有订阅重新登记到当前用户;prod 可选设
CDROP_VAPID_PUBLIC_KEY+CDROP_VAPID_PRIVATE_KEY(just vapid-keygen生成一对,二者须同时设置,都留空则推送惰性关闭) - commit
92a03ae
2026-06-16 — 浏览器免重登 + 设备名持久化
浏览器端不再每次重启都要重新登录;长寿命 refresh_token 从此完全不进 JS,改由服务端 HttpOnly cookie 会话持有。对长寿命凭据而言比原先(refresh_token 在 JS 内存、开页期 XSS 可读)严格更安全。
- 服务端会话表 + cookie:新增
web_sessions表(sqlc)。/auth/exchange换完 token 后建会话并下发 cookie(HttpOnly; Secure; SameSite=Lax; Path=/api/auth),响应体不再回传 refresh_token;refresh_token 以 AES-256-GCM 落盘(密钥取自CDROP_SESSION_SECRET,在 env、不在库内),cookie 只放不透明随机串、表 id 存其 SHA-256 —— DB 单独泄露既换不出可用 cookie、也解不出 token - 开机静默免重登:
/auth/refresh改为 cookie 驱动(7 天滑动失活)—— 这条同时就是开机静默免重登路径,前端首屏渲染前bootstrapAuth用 cookie 续期,命中直接进已登录态、避免登录页闪现;新增/auth/logout(删会话 + 清 cookie)、/auth/device(写设备名入会话) - 设备名持久化:设备名随会话存,开机 refresh 带回前端,抗 iOS PWA 存储清除 —— localStorage 被驱逐也能恢复,不再误跳设置页
- 多 tab 并发 refresh 防护:同一用户多 tab 共享一个 cookie → 一个 refresh_token,加 striped per-session 锁串行化并发 refresh,防一次性 refresh_token 被花两次而把用户从所有 tab 一起登出
- 凭据面收紧:前端 store 彻底移除
refreshToken字段,浏览器侧长寿命凭据不再触及 JS。CSRF 由 SameSite=Lax + Origin 校验双保险。桌面端不受影响(自有 loopback flow + 系统密钥库,不碰这两端点) - 部署要求:prod 须设
CDROP_SESSION_SECRET(任意长度高熵串,内部 SHA-256 派生 AES-256 密钥),缺则拒启动 - commit
e51666c
2026-06-15 — PWA 安装支持 + Safari OAuth 回跳卡顿修复
- PWA 安装支持:web 可“添加到主屏幕”独立启动(manifest + iOS meta + 同源静态壳 service worker)。SW 绝不拦截
/api(SSE / 剪贴板 / 中继需活连接)、亦不拦导航;PKCE verifier 改localStorage,使独立 PWA 在 OAuth 回跳重启上下文后仍能完成登录。面向用户短名统一小写cdrop - Safari OAuth 回跳卡顿修复:Safari / iOS PWA 从 IdP 回跳登录后,
setup.js与头像约 2 分钟拿不到连接(Chrome 正常)。根因是oauth.callback登录成功后用路由软导航,复用了 OAuth 流程遗留的 HTTP 连接,这些连接在 Safari 下卡死后续资源请求;改为整页跳转(window.location.replace)拿全新连接绕开。曾误判 service worker / h3 为主因,均已排除 - commit
5c28ddf(PWA 安装)/7082ccc(短名)/af754dc·8f550ad(Safari 修复)
2026-06-14 — 安全加固:中继会话防耗尽 + 令牌入系统密钥库 + 鉴权 / 限流多项收紧
多 agent 安全审计后的一轮加固,按信任模型(信任账号所有者 + 服务端;不信任跨用户 / 网络 hop peer / 本机其他软件)逐项收紧。
- 中继会话防耗尽重构:中继改为“预登记”模型 —— 传输进入
RELAY_ACTIVE时由服务端登记其两个合法参与方,之后仅已登记会话可接入、且调用方须为 sender / receiver。叠加 per-user 配额(3)+ 全局上限(8)+ 空闲回收(2min)+ ring 单 reader 断言。一举堵住“任意 id 凭空铸造会话耗尽全实例并钉 512 MiB”“幽灵会话”“id 泄露后无第二道防线”“双 reader 撕裂数据”四类问题 - 桌面 refresh_token 入系统密钥库:长效 refresh_token 不再明文落盘,改存系统密钥库(macOS Keychain 经
security/ Windows Credential Manager·DPAPI / Linux Secret Service),同机软件无法直读冒充;会话文件只留短效 access_token。遗留明文文件首次启动自动迁移并抹除 - HS256 token 强制 jti:HS256 路径只签 clipboard 限域的快捷指令 token(必带 jti),无 jti 的有效签名一律拒签,把密钥泄露的爆炸半径压回 clipboard-only
- prod 强制 OIDC audience:prod 下
CDROP_OIDC_AUDIENCE为空即拒绝启动 —— 空 audience 会跳过校验、令同一 JWKS 下兄弟应用的 token 也能验过(跨 app 冒充)。部署要求:prod 须设该变量(web + 桌面 client_id 逗号分隔) - 请求面收紧:信令 / 文本消息加请求体上限(防 OOM);内置 TURN 凭据 TTL 由 24h 降至 1h(缩泄露窗口 24×);
/auth/exchange与/auth/refresh加 per-IP 限流(防把服务当 OIDC 暴力跳板) - commit
bcdc2b4(中继 / HS256 / 密钥库 / body 上限)·c536a35(audience / TURN TTL / 限流)
2026-06-14 — 桌面文件落盘可配置 + 传输列表行内操作 + macOS 下载兜底修正
- 桌面端接收文件落盘到可配置目录(默认系统
~/Downloads):设置页加“下载目录”+ 原生目录选择框(ChooseDownloadDir→ WailsOpenDirectoryDialog)。机制为 Go 绑定写盘 —— 桌面模式下downloadBlob改走SaveDownload(blob → base64 过桥 → Go 写盘),对端文件名经sanitizeFileName剥目录成分防路径穿越、重名加(n)、落盘后open -R/explorer /select揭示 - macOS 下载兜底修正:WKWebView 不处理
<a download>的blob:,原“失败回退浏览器下载”在 macOS 是空操作、会静默丢文件;改为写配置目录失败时自动改写系统~/Downloads,连默认目录也失败才显式报错(toast.error),不再丢文件 - 传输列表行内操作:每行加 —— 非终态“取消”(拆本端 P2P / relay +
POST /transfer/{id}/cancel)、终态“删除”(从 active + history 移除)、发送方仍在试 P2P 时“立即中继”(清 30s ICE watchdog +POST /transfer/{id}/fallback,跳过等待) - 双端桌面客户端(macOS universal
.app+ Windows.exe)重构并同步;web 端 prod 部署(drop.commilitia.net) - commit
43ddf36(下载路径)/10a4c99(传输 UX + 兜底修正)
2026-06-12 — 接入聚珍(Juzhen)2.0 综合 CJK 排版:全角式 + 功能分级 + 本地优先字体
- submodule
cjk-autospace升级a5faec5 → a3c94dd:v1 自动空格 shim 从零重写为“聚珍(Juzhen)”2.0 综合排版引擎 —— 中西间隙 / 标点挤压(全角式)/ 禁则避头尾 / 垂悬字避免 / 长英文断词 / 功能分级;JS 只注入jz-*结构、视觉尺寸全在juzhen.css(必配) - 功能分级:功能分文本级(中西间隙 / 标点挤压,任意文本片段适用)与段落级(禁则 / 垂悬字 / 长词断字,需行段布局,对单行 UI 文本无意义甚至有害)。薄壳
level:{text:".juzhen"}令全树默认文本级(cdrop 绝大多数是 UI chrome);消息正文 / 剪贴板内容 / Callout / Toast 正文 / Field hint / 登录说明等 8 处流动正文加data-jz-level="paragraph"opt-in 段落级,失败模式良性 - 字体逐 region 明确化(
fonts.css,:lang()切 SC/TC/JP/KR):Sans = Orbit Gothic CJK;Serif = Noto Serif CJK(本地合一)+ Noto Serif <R>(Web 子集、跨区并集覆盖);Mono = Maple Mono + Orbit Gothic CJK - 本地优先字体层(新增
fontface.css):CDN 的@font-face无local()且 family 名与本机安装同名 → 被捕获、永远走网络。本层在 CDN<link>之后用src: local(), url()重声明项目用到的字重,本机命中即零网络、否则回退 CDN(swap) - Badge 加固(children 包单个 inline span,防聚珍注入元素在
inline-flexgap 中各成 flex item 致 gap 叠加);术语“Pangu”统一改“聚珍”;commit907d132/b3c74a6
2026-05-22 — README 补 clone 注意事项
- 新 clone 之 repo 预设不自动 fetch submodule,
web/src/lib/cjk-autospace/为空时vite build会因找不到 import 而失败;错误信息虽含路径但不直观 README.md顶部加“克隆”小节,指引git clone --recurse-submodules <URL>或补拉git submodule update --init --recursive- commit
1443543
2026-05-22 — cjk-autospace 抽出为共用 submodule
- 把原内联在
web/src/utils/cjkAutospace.ts的 Pangu shim 抽出为上游 git 仓库 cjk-autospace(双 pass 架构:跨样式标签透明 + 行内块边界,commitc3defc4) - 以 git submodule 引入
web/src/lib/cjk-autospace/;cjkAutospace.ts缩为 ~50 行薄壳,只负责 cdrop 的pillSelector="code, kbd, .cjk-pill"配置 + React 下的 MutationObserver 增量观察(自反馈防护、queueMicrotask防同 batch 重排) - 单一真实源:lib 修改一次三端(HTML 模板 / Peek.nvim shim / cdrop)同步生效;本仓库 deploy 时 Vite 把 submodule 检出之文件当一般 TS / JS 模块打包,运行时不读 Dropbox 也不依赖 git 元数据
- 架构文档(
PROJECT_BRIEF.md+FRONTEND_DESIGN.md)+ Changelog 移到 docs 分支(本档)
历史变更(移转前未以本档记录)
| commit | 说明 |
|---|---|
aa80793 | CJK 排版子系统:Pangu.js v7.2.1 端口 + cjk.css + Button label 包裹 |
4e8c541 | 前端视觉重塑后续迭代 + i18n 本地化清理 |
81dfbe1 | 前端重设计:tokens 变量化 + features-first + 原子组件 + 首页重画 + Toast + Dracula Pro 主题 |
6db6444 | 新增繁体中文(臺灣)+ locale 检测按 Hans / Hant + 区域子标签细分 |