dsh-startup-auth-cn
给 DeepSeek Harness 手搓的开机门:六幕冷启动动画 + PIN / Windows Hello / 验证器动态码,纯本地、零依赖
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 23, 2026
- Updated
- Oct 26, 2026
Introduction
dsh-boot-handmade
仓库名:
dsh-startup-auth-cn· 包名:dsh-boot-handmade(两者故意不同,包名是装进 profile 的标识)
给 DeepSeek Harness 手搓的开机门:六幕冷启动动画,加上一把真正拦得住人的锁。 纯前端 + 本地宿主半,零依赖、零联网、零第三方服务。
视觉语言参考了 B 站视频《deepseek 那些豪气冲天的启动动画》(@熟橘枯摘 · BV1T7nM6xEqo)——完整的出处链和三位更早的作者见图末「参考动画与致谢」。 全部用 DOM + CSS + SVG 画出来,没有视频、没有位图、没有第三方素材,任意分辨率都清晰;音效是 WebAudio 现场合成的。
更新记录
0.4.1
- 启动更快:点图标到门可操作 780ms → 400ms;解锁到进工作区约 1.95s,另有 4 秒 强制上限——装饰动画永远不能把人挡在门外。
- 门内改 PIN:
更改登录密码就在门上。刚验证过(PIN / 指纹 / 动态码,5 分钟内) 只填新 PIN;否则先给当前 PIN。写盘是原子的,不存在改到一半的中间态。 - 进门后的入口:右下角常驻
⚿,随时改 PIN。层级顶到 2147483645,并且挂载后 用elementFromPoint自检是否被别的插件盖住,被盖就重排/让位。 - 面板关不掉:叉号、Esc、点外面,三条独立的路。
- 指纹自愈:采样中途关窗会让
CredentialUIBroker卡住,下一次采不到样。 现在失败后自动杀掉 broker 重试一次。 - 账号:门上新增「DeepSeek 账号」,验证后自动打开应用自带的账号菜单。
- 去掉没做出来的东西:邮件验证码、微信 / QQ 入口从门上移除(邮件救援整套已在 0.4.12 删除,见下)。
0.4.0
- 按钮点击音效(WebAudio 现场合成)。
它是什么
DeepSeek Harness 启动时先播一段六幕的开机动画(黑屏描边 → 过曝白闪 → 技术档案 → 权限门 → 核验 → 进入), 然后停在一道验证门前:输 PIN、按指纹、或者填验证器动态码,三者任意一个通过才进得去工作区。
它不是"开机自启个小窗"那种装饰,是档在应用前面的最后一步。
三种开门方式
| 方式 | 说明 |
|---|---|
| 访问 PIN | 4–8 位数字,scrypt 加盐哈希存本地,连续错 5 次锁 30 秒 |
| Windows Hello | 调用系统凭据验证(指纹 / 人脸 / Hello PIN),并把系统窗口强制置顶,不会被应用挡住 |
| 验证器动态码 | 标准 TOTP(RFC 6238),手机扫码或手输密钥即可,完全离线;配 8 个一次性备用码防手机丢失 |
安装
# 1. 克隆到任意目录
git clone https://github.com/zoushangkun-dev/dsh-startup-auth-cn.git
# 2. 装进 desktop profile(link 方式,改代码即时生效)
& "D:\dsh\resources\runtime\cli\bin\dsh.cmd" plugin --profile desktop add "link:<克隆到的路径>"
# 3. 重开客户端
Windows 用户也可以直接双击仓库里的 安装.cmd(会先检查客户端是否已退出)。
装在哪个路径都可以,但不要放在会被清理的临时目录——profile 里存的是软链。 客户端运行中直接装通常会成功(实测 pnpm 3.7 秒);如果报
EPERM ... package.json.lock,退出客户端再装。
首次使用
- 重新打开客户端,动画播完会出现验证门;
- 先设一个 4–8 位 PIN(这一步会同时把你放进去);
- 之后再启动,可以直接输 PIN,也可以点「用验证器 6 位码进入」先配好验证器。
强烈建议配置验证器:点「设置验证器(6 位动态码)」→ 屏幕给出 32 位密钥 + 8 个备用码 → 手机装 Microsoft Authenticator / Google Authenticator,选「其他账户 → 输入代码」,把密钥粘进去。
那 8 个备用码只显示一次,请抄在纸上或存进密码管理器。手机丢了或刷机了,它是唯一退路。
配置与数据
全部在本机 ~/.dsh/ 下,插件不联网、不上传任何东西:
| 文件 | 内容 |
|---|---|
boot-auth.json | PIN 的 scrypt 哈希 + salt(不存明文) |
boot-totp.json | TOTP 密钥 + 备用码的 scrypt 哈希 |
逃生口与回滚
| 场景 | 怎么做 |
|---|---|
| 忘了 PIN / 被锁 30 秒 | 点「用验证器 6 位码进入」,或填备用码 |
| 手机和电脑都不在手上 | 双击 急救绕过.cmd,创建一个标记文件,宿主看到就直接放行 |
| 想彻底撤掉 | 双击 卸载.cmd,或 dsh plugin --profile desktop remove dsh-boot-handmade |
| 想退回旧版本 | 双击 回滚.cmd 选择目标提交 |
急救标记是一个本地文件:
%USERPROFILE%\.dsh\boot-auth-bypass。删掉它(或双击恢复锁定.cmd)即恢复验证。 门不会因为"等久了"自己打开——没有倒计时放行,也没有超时自动进入。
安全边界(请先读这段)
它是仪式感 + 家用门闩,不是保险箱。
- 这个锁保护的是动画和验证这一层,不是整个 Web 服务:懂技术的人可以打开开发者工具删掉遮罩节点,或者直接调宿主接口。
- 真正的强制门禁必须做在应用外壳前面(Electron 主进程 / 系统级),profile 插件够不着。
- PIN 是 scrypt 加盐哈希,会话令牌只在内存,错 5 次锁 30 秒;TOTP 密钥和备用码哈希都在本机。
- 但如果有人已经能读写你的用户目录,他可以直接删掉
boot-auth.json绕过一切——这是本地锁的固有边界。
换句话说:别把唯一一份重要数据的访问权押在它身上。
常见问题
Q:点「Windows Hello」后系统窗口没出现,或者出现在应用后面?
A:插件会把 Credential Dialog Xaml Host 那个窗口强制置顶并抢前台。仍然没出现的话,先到锁屏试试指纹是否正常;不正常说明是本机指纹驱动在用户会话里起不来,需要修驱动,和插件无关。
Q:验证器填了码一直说不对? A:确认填的是 6 位、且是当前显示的码(30 秒一换,插件容忍前后 60 秒)。手机和电脑时间差太多也会对不上。
Q:动画太长 / 太闪? A:门在约 0.8 秒后就可操作,装饰文字在门后面并行播放,不阻塞输入。频闪和撕裂效果已移除。
Q:能改成英文界面吗? A:目前界面文案是中文,代码里没有硬编码依赖,欢迎 PR。
技术细节
- 动画:纯 DOM + CSS + SVG,没有图片、没有字体文件、没有构建步骤
- 宿主半:一个 cordis 插件,注册
/boot-auth/*路由 - PIN / 备用码:
crypto.scryptSync+ 随机盐 - TOTP:HMAC-SHA1,与 RFC 6238 官方向量对齐(仓库内
tools/test_totp.mjs可复跑) - 测试:
tools/test_boot_auth.mjs、test_totp.mjs、test_pin_change.mjs、test_settings_ui.mjs、test_settings_section.mjs
已知限制
- 应用内指纹依赖本机 Windows Hello 驱动;部分老驱动只能在锁屏用,应用内验证窗会拿不到采样。
- 启动画面从点击到出现仍受 Electron 本体启动时间影响,插件只负责"出现之后"的那段。
- 「直接进入(跳过验证)」是双入口模式的临时按钮,稳定几版后会移除。
参考动画与致谢
本项目的视觉语言与分幕节奏参考了下面这条视频,并沿着它的出处一路往上追到了三位更早的作者。 我们只做形式参考与工程重写,没有搬用任何原作者的素材或代码。
- 《deepseek 那些豪气冲天的启动动画》— @熟橘枯摘 · BV1T7nM6xEqo
它自己标注的三条源头:
| 灵感来源 | 作者 | 原仓库 |
|---|---|---|
| 最早的启动动画(按空格/回车推进的第一幕) | 沈宏宏 | 6shenhonghong9/dsh-startup-screen |
| 龙娘启动动画(线描手法) | @mytjxs | mytjxs/codex-startup-animation |
| 550C 启动动画 | @yannicksong0106 | yannicksong0106/dsh-550c-boot |
如果这几样对你有用,请去原仓库点个 star——功劳是他们的。
License
MIT
桌面启动器(V4.1 起内置)
DSH 主进程要几秒才建出窗口,这段时间里连点图标只会拉起几个立刻退出的进程, 看起来像「点了没反应」。插件自带一个启动器脚本解决这个问题:
安装启动器.cmd 在桌面创建「DeepSeek Harness (Launcher)」快捷方式
卸载启动器.cmd 移除该快捷方式
launcher/ 启动器本体(dsh-launcher.vbs + dsh-launcher.ps1)
点一下,约 100 ms 内弹出小窗显示「正在启动…」,主窗口出现后自动关闭; 如果 DSH 已经在运行,启动器直接把已有窗口切到前面,不会重复启动。
排错:launcher/launcher.log 会记录每次调用的每一步。
在应用内修改访问 PIN(V4.1 起)
门上有「修改访问 PIN」入口,不用退出应用、不用把 PIN 重置成未知状态:
- 刚验证过(PIN / 指纹 / 动态码任意一种,5 分钟内):直接填新 PIN 两遍即可
- 没验证过:先填当前 PIN,再填新 PIN 两遍
- 改完不会退出登录,下次开门用新 PIN
为什么要有它:以前改 PIN 只能到门外重设,一旦中途失败,账号就进了死胡同。 现在改动是原子的——要么旧 PIN 还能用,要么新 PIN 已生效,没有中间态。
接口:POST /boot-auth/pin/change,参数 { next, confirm, current? }。
宿主只在「5 分钟内刚验证过」或「当前 PIN 正确」时接受。
DeepSeek 账号:由应用负责,门只负责带路(V4.1 起)
账号的登录与切换属于 DSH 应用本身,插件拿不到(核心的 service-accounts
没有对插件开放)。所以门上只做一件事:
门 →「DeepSeek 账号」→ 验证后进入 → 自动打开应用自带的账号菜单
为什么值得做:不是所有人都找得到那个入口。门在验证完成后会在 3 秒内查找 账号/头像元素并点开;找不到就什么都不做——此时人已经在应用里,界面和平时一样。
进门之后也能改 PIN(V4.1 起)
门的入口只在门出现时存在,进了应用就找不到了。所以进入后右下角会常驻一个
⚿ 按钮,点开就是「修改访问 PIN」面板,参数与门内的那个完全一致。
- 需要当前 PIN(刚验证过 5 分钟内可留空)
- 改完不退出登录,下次开门用新 PIN
- 面板自带样式,不依赖门那套样式表(门关掉时会一起移除)
入口位置与撞车处理(V4.1 起)
入口挂在左下角「已登录 DeepSeek」那一行,而不是飘在角落——那里才是人会看的地方。
找不到时按顺序退:头像/账号元素 → 侧栏底部 → 标题栏 → 右下角浮标。
防撞车:插入后量一次矩形,和自己兄弟节点重叠就撤掉、退到下一个位置; 之后每 2 秒复查(最多 10 次),因为别的插件可能是稍后才注入的。 被挤掉时不会盖住别人的控件。
启动器卡在进度条怎么办
症状:小窗弹出来了,进度条跑满,但窗口一直不出现。
原因是任务管理器里还留着一个没有窗口的 DSH 进程,它占着单实例锁; 新进程启动后被它挡回来、立刻退出,所以什么都看不到。
启动器现在会识别这种情形:约 5 秒后把小窗文案改成「已有实例在后台,等待窗口」, 20 秒后提示「请在任务管理器结束 DSH 后重试」。真遇到了,按提示做一次即可。
桌面启动器(0.4.11 起恢复发布)
双击 安装启动器.cmd 会在桌面加一个 DeepSeek Harness (Launcher),
点它会出现一个小窗:深色卡片、DEEPSEEK HARNESS、正在启动…、橙色进度条,
右侧是百分比。窗口一出现小窗就自己关掉。
进度条最多只到 92%,永远不会假装跑完——满格和"还在等"必须能一眼分清。
进度条上面还有一行字,说明现在在做什么,而不只是走了多少:
| 时间 | 步骤 |
|---|---|
| 0 – 1.2s | 正在启动进程 |
| 1.2 – 6s | 等待窗口出现 |
| 6s 以后 | 界面加载中 |
「进度条跑满但窗口不来」的根因(已修)
上一次异常退出会在后台留下没有窗口的 DSH 进程。它占着单实例锁, 新进程启动后立刻被挡回来并退出,于是屏幕上什么都不会出现。
现在启动器在拉起程序前先判断:
- 有窗口 → 聚焦已有窗口,不重复启动
- 没有窗口但有进程 → 认定为残留,全部结束,再干净启动(日志写
stale instances cleared)
卸载:双击 卸载启动器.cmd。原有的 DeepSeek Harness 快捷方式从头到尾没被改过。
安装启动器.cmd 只是给本地用;装了插件的人不需要跑任何脚本:
右下角 / 左下角的入口 → 「启动方式(可选)」→ 启用桌面启动器
- 默认不装,旧的快捷方式一直可用
- 启用后桌面多一个
DeepSeek Harness (Launcher),点下去立刻有反馈 - 不满意点同一处「卸载启动器」,旧入口从未被动过
之所以做成选装:新入口解决的是「连点好几次」的体感问题,但它依赖 DOM 与窗口行为, 不是所有机器都需要,也不该在没验证过的情况下替用户做主。
入口改叫「更改登录细节」(V4.1 起)
这块面板现在管两件事,所以入口名不能只写改 PIN:
鼠标移到「更改登录细节」上
├─ 更改登录 → PIN 表单
└─ 更改启动方式 → 桌面启动器开关
名字取“细节”是因为找的人不一定是在找密码——也可能是想换启动方式。 单独写「更改登录密码」会让另一半功能显得不存在。
指纹采样失败后的自愈(V4.1 起)
Windows 把 Hello 提示框放在 CredentialUIBroker.exe 里,而且会复用这个进程。 如果在采样中途把窗口关掉(点取消、按 Esc、直接关),broker 会保留一次失效的采样会话: 下一次窗口照样弹出来、也显示「请扫描指纹」,但采不到任何东西,只有重启才行。
现在两种处理:
- 自动:这次 Hello 返回 Canceled / RetriesExhausted / no-result 时,宿主先
taskkill /F /IM CredentialUIBroker.exe再自动重试一次 - 手动:
POST /boot-auth/hello/reset随时清掉卡住的 broker
代价是重试会多弹一次窗;但比“指纹彻底用不了、只能重启”好。
进门后的入口
右下角常驻 ⚿ 浮标,点开是「更改登录 PIN」。它完全在我们自己的 DOM 里,
不碰应用界面的框架节点(之前挂进标题栏把界面卡死过,那条路废了)。
进应用后的那个 ⚿ 浮标曾经有两种实现:挂进应用标题栏(后来确认会把界面卡死,
因为那部分 DOM 归 React 管),以及右下角浮标。两者在当前版本上都不稳定,故整段移除。
现在能改 PIN 的唯一位置是门本身(「更改登录密码」)。它不碰应用界面,是安全的。
桌面启动器改用 安装启动器.cmd 安装,不再依赖应用内入口。
层级与点得到(V4.1 起)
别的插件也在往右下角放东西,层级低于它们就会变成"看得见、点不到"。三层保障:
- 门:
z-index: 2147483640+isolation: isolate——锁不该被任何插件盖住 - 常驻入口:
2147483645,面板2147483646 - 运行时自检:挂上 400ms 后用
elementFromPoint量一次自己是不是最上层; 被挡就重新 append 到documentElement末尾(同层级里后者赢),还被挡就上移一格
第 3 条会往控制台写一行 entry was covered by <对方>,方便以后定位是谁在抢位置。
面板关不掉(V4.1 起)
现象:点叉号没反应,反而听到余额挂件「叮」一声——因为那块角落同时被别的挂件占着, 它的点击层压在我们的叉号上。
修法不是抢赢了事,而是让关闭有三条独立的路:
- 叉号(34×34 热区、面板内
z-index: 5) - Esc(键盘,完全不经过那块区域)
- 点面板外面任何地方
面板整体也从 right:20px 让到 right:84px,不再和挂件重叠。
开机耗时记录
每次启动,门会把几个关键节点上报给宿主,追加到:
~/.dsh/boot-timing.log
格式是 时间戳 相对毫秒 阶段,阶段包括 run-start、gate-ready、unlocked、finish。
启动器自己的时间线在 launcher/launcher.log。
两处加起来就能回答「哪一段慢」,不需要开控制台。
新用户怎么知道有启动器(0.4.11 起)
装了插件的人不会自己找得到新入口,所以补了四件事:
- 入口自愈:
ensure会检查桌面快捷方式。说好装了却不见了(误删、清理工具), 就静默补一个回来。 - 一次性公告:进入应用后弹一次「现在有了新的启动方式」,里面有一段和启动器 一致的步骤 + 百分比示意,让人知道等待时长什么样。
- 避让其他弹窗:弹之前扫一遍其他插件的 modal / 更新日志 / toast。有就每 1.5 秒 再看一次,最多等 60 秒;它们说完了才轮到自己。这条通知不急,不该插队。
- 按版本记忆:勾选「下一次更新前不再展示」会写进
~/.dsh/boot-announce.json, 记录的是版本号而不是布尔值——发新版本会自动重新提示一次。
接口:POST /boot-auth/announce,action 为 status / dismiss / ensure。
没设过 PIN 的人怎么办(0.4.11 起)
插件第一次跑的时候 boot-auth.json 还不存在,门会进入设置模式:
为了让这把锁生效,请先为它设置一个 PIN(4-32 位数字)
○ ○ ○ ○ ← 先给 4 格,够 4 位就是一个完整 PIN
[1][2][3] … ← 可视化键盘
输完第一遍 → 提示「请再输一次这个 PIN,确认没有按错」
两遍一致 → 写入并放行
两遍不一致 → 清空重来,不会把错误的那一次写进去
为什么必须输两遍:以前只输一次就生效,一个手滑就把自己永久锁在外面——
而那时唯一的出路是 急救绕过.cmd,等于锁白做了。
另外,点是自适应的:4 位时显示 4 格,第 5 位开始才多一格,到 8 位封顶。 不会让人觉得"还有 4 格没输完"。
所有密码输入都能看明文(0.4.11 起)
门上的点旁边多了个「显示」按钮;设置里改 PIN 的三个框上方有「显示输入内容」勾选。
- 打开后点变成真正的数字,
1 2 3 4一目了然(数字槽 22px 宽,不会溢出压到按钮) - 能核对「我输入的是不是我想的那个」
- 记不住的人可以拍照留存——全是一样的点,拍照没有任何意义
默认关闭。这是本地 UI,不上传、不记录,除了你自己没人看得到。
「直接进入」保留到 0.5.0(决定,不是遗漏)
门上有一个 直接进入(跳过验证)。它不是忘了删,是故意留的退路:
现在还不知道会不会出现结构性的 bug 让人根本进不来。真有那一天, 「看起来像锁」没有意义,「进得去」才重要。
计划在 0.5.0 移除,前提是这之前没有出现过"门本身进不去"的问题。
在那之前,能救急的三条路,按代价从低到高:
| 办法 | 做什么 | 恢复 |
|---|---|---|
| 直接进入 | 门上点一下,跳过验证 | 无副作用,随时可用 |
急救绕过.cmd | 在 ~/.dsh/ 放一个 boot-auth-bypass 标记文件 | 删掉标记即恢复 |
回滚.cmd | 停用插件本体 | 重新启用即可 |
前两条都不碰你已经设置的 PIN,只是临时放行;第三条是把插件关掉。
PIN 长度:4–32 位(0.4.11 起)
最早限定 4–8 位,那是我拍的数字,不是任何标准。现在放宽到 4–32 位纯数字:
- 键盘只有 0–9,所以不会出现"按不出来的字符"
- 超过 8 位时点会自动换行,不会挤出屏幕
- 门的输入框、设置里的改 PIN、宿主校验三处一致
同时删掉了门下方那行内部备注「双入口模式 · 稳定后会把「直接进入」移除」—— 那是给开发者看的,不该出现在用户的界面上。
门只管进门(0.4.11 起)
门上原本还塞了一个「更改登录密码」表单,和设置里的那份重复。现在门上只剩通过验证需要的:
数字键盘 + 显示
WINDOWS HELLO · 指纹 / 人脸 / PIN
用验证器 6 位码进入(配好了才显示)
DeepSeek 账号
直接进入(跳过验证)
改 PIN、改外观、开关启动器小窗,全在左侧栏的「⚙ 设置」里。
门的输入:一个普通密码框 + 眼睛按钮(0.4.11 起)
自绘的点、屏幕键盘、可变槽位——全部撤掉。回到最稳的形态:
输入登录密码 · 已输入 9 位
[ ● ● ● ● ● ● ● ● ● ] [👁] [解锁]
- 输入框就是浏览器的密码框,输入、退格、粘贴、回车全部原生行为
- 右边一个眼睛按钮:闭眼时是点,点一下变睁眼,点立刻变成数字; 再点一下回到闭眼,数字变回点
- 图标和颜色跟随深浅主题(深色下是浅灰/米白)
- 位数写在提示行(
已输入 9 位),因为 9 位以上靠肉眼数点容易数错
切换只改 input.type,不碰输入值,所以不存在"点一下就清空"。
长度逻辑集中在一个函数里
没有任何写死的位数。门的提示、校验、上限全部走同一个函数:
var PIN_MIN = 4, PIN_MAX = 32
function pinInfo(value) {
var len = String(value || '').length
return {
length: len,
tooShort: len > 0 && len < PIN_MIN,
okRange: len >= PIN_MIN && len <= PIN_MAX,
remaining: Math.max(0, PIN_MIN - len),
room: Math.max(0, PIN_MAX - len),
}
}
- 点不用我们算:输入框是原生密码框,打几个字就几个点
paintPin()用它更新提示:还差 2 位/已输入 9 位- 提交前用它校验,报错文案里的数字也来自这两个常量
验证覆盖 0 / 2 / 4 / 9 / 32 / 33 位六种情况,全部符合预期。
要改范围(比如放宽到 64),只改 PIN_MAX 一处。
设置条目进应用自己的设置页(0.4.11 起)
用官方 API 注册,不往 React DOM 里塞东西:
ctx.inject(['settings'], (sctx) => {
const scope = sctx.settings.register('boot-auth', Schema, {})
scope.watch(apply) // 用户改了 → 写进 ~/.dsh/boot-settings.json
})
条目会出现在 设置 → 插件 → 插件配置 → boot-auth,和 Vision Router、Cappuccin 并列。
当前可配置项:
| 项 | 类型 | 作用 |
|---|---|---|
| 外观 | auto / light / dark | 门的深浅色 |
| 启动小窗 | 布尔 | 点启动器时是否显示进度小窗 |
| 当前 PIN / 新 PIN / 再输一次 | 文本(write-only 语义) | 三个都填好且两次一致才执行 |
改 PIN 也在这张卡片里:三个字段,填完保存即执行(需要当前 PIN,
或 5 分钟内刚验证过身份)。表单本身不显示结果,执行结果记在
~/.dsh/boot-settings.json 的 lastPinResult 里(ok: current-pin at … 或失败原因)。
同样的输入不会重复执行。
为什么整段都有 try/catch
插件是 link 安装的,@deepseek-ai/schemastery 不在自己目录里,裸 import 会
ERR_MODULE_NOT_FOUND。所以:先常规 import,失败再从各 profile 的 node_modules 里找;
任何一步失败都只打一行日志,门本身完全不受影响。已用假宿主验证过三种情况:
正常注册、watch 生效、没有 settings 服务的宿主照常启动。
为什么设置没能进「内置插件」(查清了)
这个宿主的 settings 服务没有 register,只有 describe 和 getSnapshot:
skipped: register failed: sctx.settings.register is not a function
Vision Router 之所以能在设置页里出现,是因为它自带了一整套兼容层
(lib/dsh-settings-017-compat.js、dsh-contract-compat.js),把 configEditor、
web 路由、revision registry 全部实现了,而且命名空间写死 vision-router,不可复用。
所以结论:在当前版本上,做不到"和别的插件并列",除非逆向那几个未公开接口。
现在的做法
应用内不再放任何我们自己的 UI(那块属于 DSH,插进去是局外元素)。 设置回到它该在的地方——门本身:
需要访问许可
输入登录密码 · 已输入 9 位
● ● ● ● ● ● ● ● ● 👁 解锁 →
────────────────────────────────
更改登录 PIN ← 点开在纸内展开,不是浮层
直接进入(跳过验证)
点「更改登录 PIN」会在同一页纸上展开三个字段(当前 / 新 / 再输一次), 就地保存、就地显示结果。外观(深/浅)已并入这张卡片,启动小窗在设置文件里。
设置页也要先验证(0.4.11 定稿)
打开 设置.cmd 的页面后,先出现的是:
先验证一次身份
改 PIN 需要第二因素:验证器动态码、备份码,或 Windows Hello。
[ 验证器 6 位码 / 备份码 ]
[验证] [用 Windows Hello]
通过后才显示 PIN 表单和小窗开关。验证有效 5 分钟,改完即作废。
接口层同样强制:/boot-auth/pin/change 没有 step-up 令牌直接 401,连当前 PIN 都不看。
为什么没有邮件救援(0.4.12 删掉)
曾经做过一条"忘记 PIN 就发邮件验证码"的路,用的是本地 SMTP(126 邮箱授权码)。 后来整套删掉了,原因不是难做,是可靠性不够:
- 邮件能不能发出去,取决于邮箱服务商当天的心情、端口是否被封、授权码是否过期。 一条"救援通道",只有在最坏的时候才被使用;它却建立在最不可控的一环上。
- 配置过程本身要求用户去网页版生成授权码——对普通用户是一道门槛, 对想进门的人也未必拦得住。
- 安全模型上,它把门的安全性降到了邮箱的安全性:能进邮箱的人就能重置这把锁。
所以决定留到能自建发信服务(有自己的域名、SPF/DKIM、退信处理)时再做。 在那之前,可靠的三条路是:PIN、Windows Hello、验证器 6 位码 + 8 个一次性备份码。