# 访问审计

CipherFS 在 Core 记录逻辑访问与实际提交，在 HTTP、私有本地 API、S3 与 FUSE 另记交付观察。桌面 **Access** 面板按 FileId 或 DeviceId 查询，并显示保留覆盖、写入状态、积压与丢失数。当前入口接入已实现；运行状态提供每个入口的默认模式与观测边界。

## 模式与覆盖

| 入口 | 默认模式 | 身份与观测边界 |
| --- | --- | --- |
| Core、Rust、CLI、C/Python/Go、桌面 | off | 本机进程，Core 操作与返回/提交边界 |
| HTTP JSON | required | 已验证会话的公开身份，Core 与响应正文交接 |
| S3 | required | SigV4 验证后的注册身份，Core 与明文正文交接 |
| 私有本地 API | off | 受保护端点凭据验证后的公开身份，Core 与正文交接 |
| 独立 FUSE | off | 回调实际提供的 UID/GID/PID，Core 与 reply 交接 |
| 守护进程挂载 | best effort | 相同的 OS 回调观察 |
| 嵌入式后台任务 | off | 系统身份与维护事实 |
| 守护进程后台任务 | best effort | 系统身份与维护事实 |

`off` 不承诺审计。`best_effort` 尝试全部事件并报告丢失，跨崩溃的缺口数只是下界。`required` 在意图无法落盘时拒绝业务访问，提交事实与业务变更处于同一事务。完成屏障失败前可能已经提交业务，重试修改前应检查结构化的已知回执。HTTP 返回 503 与 `AUDIT_UNAVAILABLE`，绑定使用错误类别 32。必需审计故障影响就绪状态，运行诊断仍可查询。

拒绝观察与签名属主关闭发生竞态时，仍保留未授权响应，并记入既有的有界拒绝汇总。存活属主的 Required 审计失败仍返回不可用；这个例外不会放行业务访问。

主体不保存会话 token、密码、请求头、路径和正文。认证失败只记未知身份，不解析请求目标。UID/GID/PID 不认证可执行程序。不同层字节数不可相加，FUSE reply 没有接收回执，交付保持未知；任何交付记录都不证明人已阅读。

## 查询与配置

使用正常密码来源或已运行的本机归属进程。查询参数是公开 ID，不是路径。

```sh
cipherfs audit status --vault "$VAULT_PATH"
cipherfs audit config --vault "$VAULT_PATH" > audit-config.json
cipherfs audit file "$FILE_ID" --vault "$VAULT_PATH" --limit-count 100
cipherfs audit device "$DEVICE_ID" --vault "$VAULT_PATH" --limit-count 100
cipherfs audit set-config --vault "$VAULT_PATH" --input-file-path audit-config.json
```

先编辑完整配置，再运行 `set-config`。`mode_overrides` 用 `http`、`bindings`、`daemon_mount` 等入口名映射到 `off`、`best_effort` 或 `required`；缺省入口沿用上表。事件、队列、spool、段大小，flush/关闭超时与独立的 `audit_retention_days` 都有正数上限；无效预算和冲突中的操作会拒绝配置更新。

移动观察和事务提交事实分别有每次操作或事务 `min(4096, max(1, queue_max_bytes / 256))` 项的有限上限。较大的 required 批次应拆分；超过提交事实预算时该事务回滚。Best effort 继续业务并报告丢失，不完整汇总不指向单个目标。

认证后的 JSON 路由相对 `/api/v1`：

| 方法与路由 | 操作 |
| --- | --- |
| GET `/audit/status` | 运行状态与各入口覆盖 |
| GET 或 PUT `/audit/config` | 读取或替换完整本机配置 |
| POST `/audit/query` | 文件/设备查询或继续分页 |
| POST `/audit/flush` | 把待处理事件封入本机密文 spool |
| DELETE `/audit/cache` | 只清空本机密封查询缓存 |
| POST `/audit/cleanup` | 按存放点、设备和上限显式清理 |

查询请求为 `{"op":"query","query":{"file_id":"<FileId>","limit_count":100}}`；查设备用 `device_id`。可选时间过滤为 `from_epoch_ms`、`through_epoch_ms`，`result` 使用有限结果名。继续分页用 `{"op":"page","cursor":<next>,"limit_count":100}`，其中 `next` 原样取自上一页。CLI `audit page` 使用 `--snapshot-id`、`--offset-count`、`--limit-count`。

先验证全部有界输入，再筛选结果。序号冲突、内部缺口、签名失败和预算耗尽均返回错误。覆盖信息明确报告缺失的保留前缀，`remote_tail_confirmed` 始终为 false。本机密封快照最多八个、64 MiB、五分钟，过期或清空后必须重新查询。独立 Python 解密器用纪元/设备派生域验证公开 `CFSAUD01` 向量。

## 保存与保留

审计对象位于存放点 `a/`，不进入共享索引和普通正文 GC。私有 spool 只含最终加密签名段；所有已配置存放点明确确认后才删除本机副本。密封撤销钥副本，未完成尝试恢复为中断/未知；关闭超时显示待处理状态。

关闭先停止接纳，再排空活跃尝试、排队事件和延迟返回决定。三者均为空时，在原关闭时限内分批恢复遗留的持久开始记录，复用启动恢复的 `OperationRecovered` 事件。记录保留已证明的提交事实，结果与交付仍为未知；丢失的 best_effort 完成不会被记为成功。仍活跃的尝试继续保持待处理，也仍可使关闭失败。

```sh
cipherfs audit cleanup --vault "$VAULT_PATH" --store-id "$STORE_ID" --device-id "$DEVICE_ID" --limit-count 128
```

清理必须持有后端实际删除权限，保护尚未发表的本机段。最短保留从本机首次观察与已认证段/事件时间中的最晚值开始，未来时间会推迟删除。文件保留策略不删除审计历史。成功删除数仅表示后端明确确认，不证明服务商物理空间已回收。

[ADR](adr/006-access-audit.zh.md)记录设计，[FORMAT 第 11 节](FORMAT.md#111-independent-access-audit-stream)规定容器、严格载荷与本机表。绕过这些观测边界的访问不在日志内。
