# 凭据与密封

存放点配置只在加密的本机行中保存 `credential_ref`（来源与精确查找名），不保存秘密。
来源定义放在本机配置中，文件来源必须位于所有受管仓库之外。显式引用缺失、过期、
被撤回或无效时明确失败，不回退 ENV、profile 或云元数据服务。

## 来源

| kind | 配置 | 查找 |
| --- | --- | --- |
| environment | 无额外字段 | name 是精确环境变量名，每次重新读取。 |
| files | 绝对 directory_path | name 是单一文件名；Unix 为当前用户所有、单链接、0400/0600 的普通文件，父目录受信任且不可由组或其他人写入；拒绝符号链接。 |
| systemd | 无额外字段 | 每次读取当前 CREDENTIALS_DIRECTORY 下的 name，沿用文件校验。 |
| keyring | 公开 service 名 | macOS Security、Windows Credential Manager、Linux Secret Service；后台不弹窗、不解锁集合，条目由用户另行配置。 |
| https | endpoint_url、bearer_token_ref，可选公开 CA certificate_path | 将 name 作为单一路径段发 HTTPS GET，Bearer 重新读取；禁止跳转，连接 3 秒、总计 10 秒截止。 |

最多 64 个来源，依赖深度最多 8，拒绝未知依赖与环。响应非空且最多 256 KiB。
目录被替换后重新校验，拒绝指向仓库内部的规范化别名。S3 来源返回 JSON：
access_key_secret、secret_key_secret，可选 session_token_secret、expires_at_epoch_s；
拒绝未知字段，每个凭据分量最多 65536 字节。密码为精确 UTF-8 字节，不自动去换行；
服务解封密钥为恰好 32 个原始字节，不进行文本或 base64 转换。

已注册 service 封装后可使用：

```json
{
  "secret_sources": {"files":{"kind":"files","directory_path":"/run/cipherfs-secrets"}},
  "vaults": [{"name":"personal","vault_path":"/srv/cipherfs/personal",
    "auto_unseal":{"kind":"key","source_id":"service","credential_ref":{"source":"files","name":"service-key"}}}],
  "listeners":[{"listen_address":"127.0.0.1:3000"}]
}
```

首次注册在已认证 Core 中调用 add_unlock_key_source；外部文件用
owner::read_credential_file 读取并保持 Zeroizing guard，密钥不要放进仓库。
密码来源将 kind 改为 password 并省略 source_id；显式 env:NAME 密码简写也会重试。
credential_bindings 可在保存之前把位置及可选 store_id 绑定到引用。CLI 持久注册：

```sh
cipherfs store add s3 --vault /srv/cipherfs/personal --bucket example-bucket \
  --region us-east-1 --prefix team --credential-source files --credential-name s3-writer
```

桌面 S3 表单与公开同步 API 只接受引用，拒绝直接填写 access/secret 字段。
私有原生凭据注入协议仍像进程内 C/Python/Go override 一样，限受认证本机传输和当前会话。

## 密封与恢复

serve 先密封取得租约、发布私有发现，再尝试来源。失败保持进程存活，health 为 sealed、
ready 返回 503；重试从 1 秒增长，最多 30 秒。成功建立新会话，手动密封不会立即自动解封。
cipherfs seal --vault PATH 撤销会话、停止同步与归属挂载、排空已接纳操作并清除密钥，保留
原租约和端点；cipherfs unseal 使用通常的密码输入，--configured 仅经私有端点取配置来源。
旧句柄、快照、写入、JWT、凭据上下文不会因解封复活。logout/lock 仍关闭归属并释放发现。

POST /api/v1/unseal 接受 kind=password/password_secret 或 kind=recovery/recovery_key_secret
证明；kind=configured 限私有端点，远端不能靠触发宿主来源获取会话。
POST /api/v1/seal 要求当前会话。错误证明不撤销已有有效会话。
Core 返回 Sealed，原生错误码 32，HTTP 分类 SEALED/503。

SyncedVault::set_idle_timeout(Some(Duration)) 按用户操作闲置密封，后台轮询不算活动；None
关闭策略。解封要显式传入新 provider，并重新启用同步。阻塞门面与绑定使用可选毫秒；
unseal/unseal_with_unlock_key 成功后需重新配置来源/凭据，不悄悄回退 ENV。绑定也提供密封打开、
独立服务密钥封装增删和来源配置。密码、恢复与服务封装可并存，都在 metadata schema 1 的
`unlock_wraps` 表里；封装格式见 `FORMAT.md` 第 5.5 节。

长期原生仓库密钥与 S3/WebDAV 凭据使用独立锁页，失败返回错误；先清零再解锁页面。
Linux 禁止 dumpable 且 core 的软硬限制均为 0；macOS 禁 core 并锁页。这些限制不可逆且影响
整个嵌入进程。临时密码学、HTTP、第三方及调用方缓冲无法全部保证清零/锁页；应用 provider
仍负责自身秘密存储。运行中 root 和虚拟机宿主读内存均不在防护范围，来源差异表见 README。

## 写入设备无删除权

为写入设备使用独立 S3 角色，只允许前缀下 GetObject/PutObject 及带 prefix 条件的 ListBucket，
显式拒绝 DeleteObject/DeleteObjectVersion，并拒绝该角色改生命周期、桶策略、版本状态或删除桶。
完整可替换 bucket/role/team 前缀的 JSON 模板见[英文页](credentials-sealing.md#writer-permissions)，
依据 [AWS 桶策略例子](https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html)
与[权限映射](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-with-s3-policy-actions.html)。
生命周期、管理员、GC 身份另行控制；禁止删除不阻止有 Put 权限者覆盖对象。

cipherfs store check-permissions --vault PATH --id ID 先有界 LIST 验证凭据，再 HEAD 确认随机
探针键不存在，只尝试 DELETE 该不存在的键；不 PUT，不删已知业务对象。
只把明确 AccessDenied 且随后 LIST 仍成功算 delete_denied=true。过期、撤销及其他错误不算成功；
删除允许则报告 false，CLI 非零退出。探测只反映一个命名空间，不能证明整个账户策略或版本删除权。
版本桶允许 DELETE 时仍可能生成[删除标记](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html)。
