用途与实际影响
这项功能怎样使用
为什么需要它
Windows 当前剪贴板只保留最新内容,临时资料很容易被覆盖;只存去重文本又会丢掉“复制了几次、何时复制、是否来自历史恢复”的真实事件。
举个实际例子
我复制过同一个链接好几次,现在又被别的内容盖掉了。在本机剪贴板页筛选日期或链接,查看这一组的复制次数和最近时间,确认完整内容后点“再次复制”。新复制等原采集器记录后才会更新次数,不由界面先加一。
最后我会得到什么
本机筛选后按完全相同内容显示分组、准确总数和复制次数;预览正确内容后可再次复制,也能暂停或继续记录。允许的消费者仍按独立游标取得原始新事件。
正常时
查看器按筛选条件找到本机已保存内容,重复复制次数来自真实事件,再次复制后由采集器记录新时间。
发现问题时
来源不允许记录、内容过大或剪贴板暂时被占用时留下不含正文的缺口,不编造丢失内容。
入口不可用或证据不足时
记录停止、数据库损坏或搜索不可用时明确报告,保留已有历史,不用低效的全库翻找冒充恢复。
从哪里开始
在本机已安装的密码中心综合窗口进入剪贴板页,按记得的词、日期或类型查找;独立 TimeAudit 剪贴板历史仍是已有入口。确认正确内容后再选“再次复制”。
需要准备什么
- 记得的文字片段、日期或内容类型
- 要看相同内容被复制几次,还是要把它重新放入剪贴板
从开始到拿到结果
- 1
找回被覆盖的内容
按关键词、日期或类型筛选本机已记录的复制,再把完全相同的内容放在一组;同一次复制的重复观察不增加次数。
- 2
确认后再次复制
选择正确记录并点击“再次复制”,内容回到 Windows 剪贴板,找回动作也另记一条事件。
- 3
核对范围
只说明本机实际观察到的复制;空白、跳过或采集间隙单独显示,复制事实不代表执行或同意。
技术实现与依据
这里保留实现、关键条件、精确入口、历史记录和验证结果。
9 月 24 日正式源码已有分组/类型筛选,PCConfig 安装版综合窗口显示 50 条;8 月 31 日事件/索引计数只作历史,本轮未刷新当前总数或近线恢复关键规则与设计选择
每次复制都是独立事件;相同内容只复用 blobs 正文对象,绝不合并 events 事实。
展示时才按完全相同正文分组,空格不同仍是不同内容;复制次数排除同一来源实例、开机、会话、剪贴板序列里的重复观察。筛选先于分组与分页;疑似密钥只按特征提示,不能保证识别全部秘密。
启动、解锁、恢复和暂停结束只建立新的 clipboard sequence baseline(剪贴板序列基线),不导入此前当前内容。
只接收 Unicode 文本、HTTP(S) URL 和普通文件路径列表;图片、二进制、虚拟文件、私有格式与超限内容不保存正文。
查看器、增量出口和备份各有只读/恢复边界;下游 checkpoint(续作检查点) 只有在下游 writer(写入方)成功后才能推进。
公开页只展示 schema(数据结构)、组件、任务、路径类别、状态和计数;原始正文、内容 hash(内容指纹)、私密窗口与凭据始终不公开。
本模块用到的名词
- WM_CLIPBOARDUPDATE
- Windows 在剪贴板发生变化时发出的消息;事件驱动,不是轮询或键盘监听。
- WAL(预写日志)
- SQLite 让持续写入与只读查询并行的日志模式;恢复不能靠在线手工复制 WAL/SHM。
- FTS5(全文搜索索引)
- 为关键词检索建立的 SQLite 虚拟表;缺失就停止查看,不长期扫描正文表。
- lineage(恢复来源链)
- `restored_from_event_id` 与 `restore_request_id` 把再次复制连接回原事件。
- checkpoint(增量游标)
- 最后成功提交的观察时间与事件 id;重复读取同一 event_id 必须由下游幂等处理。
专业定义
在当前 Windows 会话记录新复制;本机按词、日期与类型找回,相同内容分组显示实际复制次数,确认后可再次复制。增量出口和近线恢复仍独立。
解决什么
解决剪贴板被覆盖、重复内容事件丢失、全文搜索退化、再次复制来源无法证明、增量消费重复/漏读和在线复制数据库/WAL 导致不一致恢复。
当前怎样实现
- collector.pyw 创建隐藏 Win32 消息窗口,以 AddClipboardFormatListener / WM_CLIPBOARDUPDATE 事件驱动采集,不轮询、不装键盘 hook(钩子)、DLL、驱动或 Session 0 服务。
- 读取时遵守 Windows 来源格式:ExcludeClipboardContentFromMonitorProcessing 或 CanIncludeInClipboardHistory=0 会阻止本地保存;CanUploadToCloudClipboard=0 只禁止云剪贴板,不阻止本机历史。
- SQLite schema(数据结构) v1 包含 meta、blobs、events、content_fts 与 adapter_events_v1;WAL + synchronous=FULL,events/blobs 由 trigger(触发器)保持 append-only(只追加)。
- 同一内容 SHA-256 映射到一个 blob;每次复制仍生成独立 event_id。再次复制写 versioned restore marker(版本化恢复标记),只有 marker 与原事件正文一致才记录 lineage。
- viewer.pyw 使用 mode=ro 与 query_only=ON,在 Tkinter 桌面窗口按关键词、UTC+8 日期、类型和分页查询;FTS5 缺失时明确失败。
- 622b66c 的 ReadOnlyClipboardStore.search_grouped 先在全历史按词、日期、payload 类型和内容类型筛选,再按 blob_id 分组分页;同 sequence 的重复观察只算一次,真正再次复制另计。model.py 的 link/secret_like 是格式线索,独立采集器和 append-only events 不改变。PCConfig 综合桌面已接入同结果。
- adapter_stdio.py 提供 timeaudit.clipboard-export.request.v1 / response.v1 的 JSON/stdio 只读接口;checkpoint(续作检查点) 是 `(observed_at_utc,event_id)`,没有 HTTP API、浏览器依赖或固定端口。
- backup.py 使用 SQLite Online Backup(在线一致备份),保存大小、计数、integrity 与 SHA-256 清单;恢复前验证副本,只写入空目标目录。
执行流程
- 1
启动 collector 后先记录 baseline,不读取启动前已在剪贴板中的内容。
- 2
新复制触发 WM_CLIPBOARDUPDATE;读取 source policy(来源策略)与受支持格式,失败则写无正文 skip/gap。
- 3
为每次观察写 events,并按内容 SHA-256 复用 blobs;content_fts 同步保存可搜索索引。
- 4
用户在只读桌面查看器搜索、筛选和预览;点击再次复制时写原事件 id 与本次 request id marker。
- 5
获准消费者用版本化 JSON/stdio 读取有序事件,成功提交后在自己的 owner 边界保存下一 checkpoint(续作检查点);sidecar(侧车文件) 不接受消费确认或删除。
- 6
每日 nearline backup(近线备份)用 SQLite Online Backup 写 G 盘;恢复先校验清单与数据库,再读回空目录,正式替换需单独停止 collector/watchdog 并保留 pre-image(替换前副本)。
边界
- sidecar(侧车文件) 与 PostgreSQL/Grafana 主链完全独立;主链健康不能证明剪贴板健康,反之亦然。
- 复制事实只证明本机观察到一次复制,不证明阅读、收件、同意、执行、归属或用户意图。
- 活动私密库在 E 盘持久数据层,G 盘只是近线恢复副本且不是查询依赖;网页、Git、日志和浏览器不接触原始正文、hash(内容指纹)、FTS 或凭据。
- 当前合同只覆盖 Windows 电脑;手机来源是另一 source instance(来源实例),不能在 collector 内猜测合并。
- clipboard_sequence 只在同一 Windows clipboard station(剪贴板站)相邻事实中解释,不能当作跨启动、会话或设备的全局顺序。
- 增量合同存在不等于下游消费者已上线;本轮只验证无 payload 的 adapter(执行适配器) 形状,没有验证实际下游提交。
- 版本化恢复标记和增量合同的内部文件名不进入公开页面;它们只连接再次复制与原事件,不表示另一个中央系统是当前产品或消费者。
失败与恢复
- 来源明确禁止、格式不支持、内容超限或剪贴板暂时锁定
- 保存无 payload 的 skip/gap 原因;不把缺失内容猜回数据库。
- heartbeat 超过 30 秒或 collector 退出
- TimeAudit_ClipboardWatchdog 通过隐藏启动器检查并恢复;不启动第二个长期 collector。
- schema(数据结构) 版本不支持或 FTS5 缺失
- viewer/adapter(执行适配器) 失败关闭,不回退为长期全表扫描。
- 备份 hash(内容指纹)、大小、计数或 integrity 不一致
- 拒绝恢复;非空目标也拒绝覆盖,活动库原样保留。
- marker 缺失、无效或正文不匹配
- 新观察按普通 copy 保存,不靠时间窗口或 hash(内容指纹) 猜 lineage。
真实入口
E:\Projects\Tools\TimeAudit\clipboard_history\collector.pywWin32 消息采集、baseline、边界与 heartbeat
E:\Projects\Tools\TimeAudit\clipboard_history\storage.pySQLite schema(数据结构)、WAL、FTS5、只读查询与增量投影
E:\Projects\Tools\TimeAudit\clipboard_history\model.py链接与疑似密钥内容类型提示,不保证识别全部凭据
E:\Projects\Tools\TimeAudit\clipboard_history\viewer.pyw本机搜索、预览、暂停与再次复制
E:\Projects\Tools\TimeAudit\clipboard_history\adapter_stdio.py版本化 JSON/stdio 只读增量出口
E:\Projects\Tools\TimeAudit\clipboard_history\backup.py在线一致备份、校验与空目录恢复
E:\PCConfig\tools\Test-TimeAuditClipboardHistory.ps1任务、ACL、心跳、schema(数据结构)、计数与无正文 adapter(执行适配器) 验收
E:\PCConfig\docs\recovery\timeaudit_clipboard_history.md机器路径类别、三任务、近线备份与恢复顺序
E:\Projects\Tools\TimeAudit\test_clipboard_history.py模型、存储、查看、lineage、adapter(执行适配器) 与备份回归
如何验证
- Source(源码):已发布 001cee0 包含完整 clipboard_history 实现与合同;44a842e..001cee0 没有修改 clipboard_history 或 test_clipboard_history.py,LHM 收口不改变这条独立产品轴。
- Tests(测试):8月31日使用Python3.11 `-B -m unittest -v test_clipboard_history.py`,11 项全部通过,用时 0.806 秒;单元测试包含 SQLite Online Backup 到空目录的合成读回。
- Runtime(运行):8月31日PCConfig无正文验证status=passed;collector Running、watchdog/nearline task Ready、heartbeat age 2824 ms、state=running、integrity=ok、schema(数据结构) v1,5234 events / 2312 blobs / 3406 FTS rows。
- Contract(合同):adapter(执行适配器) 返回 timeaudit.clipboard-export.response.v1、source profile `src.timeaudit.windows_clipboard`、1 个事件且 payload_absent=true;这验证接口形状,不验证实际下游消费。
- Gap(缺口):本次未运行会写入唯一合成内容的 smoke_test.py,未把最新 G 盘副本恢复到空目录,也未验收实际下游 writer;不能声称真机 lineage 或端到端灾难恢复已经完成。
与其他模块的关系
这是与 PostgreSQL/Grafana 主时间线并列的独立产品轴;PCConfig 只拥有机器路径、ACL、三任务、快捷方式、Watchdog 与近线恢复,TimeAudit 拥有采集语义、SQLite schema(数据结构)、查看器与增量合同。它不依赖其他模块,但可把获准的版本化事件交给独立消费者。
