本 Changelog 自 2026-05-22 起以 HTML 格式维护。此前之变更可从 main 分支 git log 取得;下表为近期较显著之 commit。
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 + 区域子标签细分 |