数据存储
Foya 默认将运行数据保存在当前操作系统用户配置目录下的 foya 子目录。数据不会
因为使用桌面界面而交给单独的云端服务;模型请求只发送到用户配置的 Provider。
本文说明持久化布局和一致性边界。事件语义参见事件存储。
数据目录
标题链接默认数据目录由操作系统的用户配置目录决定:
<user-config-dir>/foya/桌面内核的 Unix Domain Socket 也位于该目录。目录创建权限为仅当前用户可访问, SQLite 和敏感配置文件使用用户私有权限。
另有一组用户可编辑配置位于:
~/.foya/Project 自身还可以包含:
<project>/.foya/三者职责不同:
| 位置 | 主要内容 |
|---|---|
| Foya 数据目录 | 数据库、Connection、Artifact 和运行状态 |
~/.foya |
全局 Rules、Memory、Skills、Hooks、Commands、MCP |
<project>/.foya |
项目级 Rules、Skills、Hooks 和 Commands |
SQLite
标题链接共享数据库文件名为:
foya.db数据库打开时使用:
- Foreign Keys;
- WAL Journal;
synchronous=FULL;- 5 秒 Busy Timeout;
- Incremental Auto Vacuum;
- 单个活动数据库连接。
单连接配置牺牲部分并行 SQL 吞吐,换取桌面场景中更明确的事务顺序。
表结构
标题链接Session 与 Event
标题链接| 表 | 内容 |
|---|---|
sessions |
Session 元数据、模型绑定、Project、状态和 Child 信息 |
events |
持久化会话事件 |
message_projection |
当前有效消息索引 |
stream_snapshots |
可替换的流状态快照 |
deleted_sessions |
已删除 Session 防迟到写入标记 |
cleanup_jobs |
延迟清理任务 |
events.seq 是数据库级自增序号。它在所有 Session 间单调增加,不是每个 Session
独立从 1 开始。
上下文
标题链接| 表 | 内容 |
|---|---|
compaction_checkpoints |
每个 Session 当前有效 Checkpoint 投影 |
context_accepted_boundaries |
每个 Session 和 Route 最近成功请求边界 |
Checkpoint 的完成事件仍保存在 events。专用表是读取投影,可以在损坏时从事件
恢复。
文件变化
标题链接| 表 | 内容 |
|---|---|
file_blobs |
按 SHA-256 去重并使用 Gzip 保存的文件内容 |
file_changes |
Tool 修改前后的 Blob 引用和审查状态 |
file_rewind_journals |
文件回退事务状态 |
file_rewind_journal_files |
每次回退涉及的文件快照 |
事件 Payload 只保留 Blob Hash,不重复保存完整文件内容。
Usage
标题链接| 表 | 内容 |
|---|---|
usage_records |
Session 级模型请求明细 |
usage_daily_ledger |
按日期和模型聚合的 Token |
usage_message_daily_ledger |
按日期聚合的消息数 |
usage_session_days |
活跃 Session 日期集合 |
删除 Session 时,明细可以删除,已经形成的日 Ledger 仍用于总体统计。
事件与投影事务
标题链接Conversation Store 会在一个事务中追加 Event 并更新对应 Projection。例如:
message_end与消息有效索引;usage_updated与 Usage 明细、Ledger;compaction_completed与 Checkpoint 投影;context_request_accepted与 Accepted Boundary;- 文件 Tool Message 与 File Change、Blob。
事务提交失败时,两部分都不会生效。
跨 SQLite 与外部文件系统的操作无法共享同一个数据库事务,因此文件回退使用 持久 Journal 和状态令牌额外保护。
文件 Blob
标题链接文件工具修改文本文件时,会计算修改前后内容的 SHA-256。内容以 Hash 作为键保存, 相同内容只需存储一次。
Blob 使用 Gzip 压缩。File Change 记录:
- 绝对路径;
- 修改前是否存在;
- 修改前后文件模式;
- 修改前后 Blob Hash;
- 审查状态。
保存 Event 前,Conversation Store 会验证必要 Blob 确实存在。原始内容随后从 Event Payload 移除,避免同一数据同时出现在事件 JSON 和 Blob 表中。
File Change 保留
标题链接文件恢复能力不是永久版本控制。
未置顶 Session 的 File Change 会受到两项保留限制:
- 最近 100 条 User 消息对应的范围;
- 最长 30 天。
过期记录清理后,不再被引用的 Blob 会被垃圾回收。置顶 Session 不执行这项自动 裁剪。
因此,Foya 的文件恢复不能替代 Git 或正式备份。
Artifact
标题链接图片等二进制 Artifact 保存在:
<data-dir>/artifacts/<session-id>/每个 Artifact 包含:
- 规范化后的二进制文件;
- 独立 JSON Metadata;
- SHA-256;
- Media Type、尺寸和字节数;
- 是否已提交到消息的状态。
Artifact Store 会验证 ID、文件类型、尺寸、像素数和 Hash。事件日志只保存 Attachment 引用。
Session 分支会把引用到的 Artifact 复制到新 Session,避免新会话依赖源目录。 删除 Session 时,其 Artifact 目录一并清理。
其他持久文件
标题链接主要文件包括:
| 文件或目录 | 内容 |
|---|---|
connections.json |
Provider Connection 与 API Key |
default-models.json |
各用途默认模型 |
agent-limits.json |
SubAgent 调度限制 |
projects.json |
Project 目录索引 |
skills.json |
Skill 启用和固定状态 |
automations.json |
自动化任务 |
workflows.json |
Plan、Spec 和 Goal 工作流 |
agent-runs.json |
SubAgent Run 快照 |
canvases.json |
Canvas 文档 |
canvas-assets/ |
Canvas 资源 |
web-search.json |
Web Search 配置 |
memory/maintenance.json |
Memory 后台维护水位 |
MCP 配置与凭证位于 ~/.foya/mcp.json 和
~/.foya/mcp-credentials.json。
多数 JSON 和 Markdown 写入使用临时文件后 Rename,降低进程中断留下半个文件的 风险。但这些文件之间、以及它们与 SQLite 之间没有统一事务。
Rules 与 Memory
标题链接Rules 和 Memory 使用人类可读 Markdown:
~/.foya/rules/user_rules.md~/.foya/memory/user_profile.md~/.foya/memory/projects/.../project_memory.md<project>/.foya/rules/**/*.md<data-dir>/context/events.jsonl 保存它们的变更通知,用于客户端回放和同步。Markdown
文件仍是实际内容来源。
重启恢复
标题链接内核启动时执行以下恢复:
- 打开 SQLite 并确保当前 Schema 存在;
- 将 Session Phase 重置为
idle; - 修复中断的文件回退 Journal;
- 清理过期 File Change;
- 将未完成的 SubAgent Run 标记为
interrupted; - 将运行中的 Canvas 生成节点标记为错误;
- 重新加载 Automation、Channel、MCP 和配置文件;
- 启动 Memory Maintenance。
以下状态不会恢复:
- 运行中的 Provider Stream;
- 内存消息队列;
- 等待中的审批 Channel;
- Session 临时 Grant;
- 已启动但未完成的工具调用。
Foya 不自动重放这些操作,因为它们可能已经在外部系统产生副作用。
删除
标题链接永久删除 Session 时:
- Kernel Service 先停止运行态和所有 Child Session;
- Session ID 写入删除标记;
- SQLite 中的事件、投影和 Session 明细被删除;
- 不再引用的 File Blob 被回收;
- Session Artifact 被删除;
- 在线客户端收到删除事件。
删除 Project 只移除 Foya 的 Project 记录和关联 Session 数据,不删除实际项目 目录。
备份
标题链接一致备份的建议流程:
- 先关闭 Foya 内核;
- 备份完整 Foya 数据目录;
- 同时备份
~/.foya; - 根据需要备份各 Project 的
.foya目录; - 将备份视为敏感数据。
只复制 foya.db 会遗漏 Artifact、Connection、MCP、Rules、Memory 和其他配置。
运行中直接复制 WAL 数据库可能得到不一致快照。需要在线备份时应使用 SQLite Backup API,而不是只复制主数据库文件。
凭证
标题链接以下文件可能包含敏感信息:
connections.json中的 API Key;~/.foya/mcp-credentials.json中的 Bearer Token;- 用户自行配置到 Hook、Command 或外部服务中的环境变量。
当前实现依赖用户目录和文件权限保护这些内容,尚未统一接入操作系统 Keychain。 不要把完整数据目录提交到 Git 或直接公开分享。
当前边界
标题链接- Schema 通过幂等
CREATE TABLE IF NOT EXISTS建立,没有独立版本化迁移框架。 - SQLite 和外部 JSON/Markdown 文件之间不具备跨存储事务。
- 文件恢复数据有保留期限,不能替代版本控制。
- Memory Maintenance 的唤醒信号、审批等待和 Provider Stream 都是进程内状态。
- 数据目录格式面向单用户实例;本机、SSH 和 HTTPS 模式都不提供多租户数据库隔离。