# 架构

## 系统概览

```
iOS 应用 / 代理客户端            -> api.<domain>  -> API Gateway -> Lambda 后端 -> Postgres
网页应用                        -> app.<domain>  -> CloudFront -> SPA
浏览器与代理认证                -> auth.<domain> -> API Gateway -> 认证 Lambda -> Cognito
顶级域名回退                    -> <domain>      -> CloudFront 重定向 -> app.<domain>
```

## 原则

1. `app`、`api` 和 `auth` 分别使用独立的公开域名。
2. Postgres 是唯一的真实数据源。
3. iOS 客户端采用离线优先模式，本地使用 SQLite，并通过同步与服务端保持一致。
4. 网页应用、iOS 应用和外部代理接口共享同一套工作区数据模型。
5. 外部代理从 `GET https://api.flashcards-open-source-app.com/v1/` 开始接入。

## 支持的客户端

- 运行在 `app.flashcards-open-source-app.com` 的网页应用
- 主仓库中的 iOS 应用，本地使用 SQLite 存储
- 发布在 Google Play 上的 Android 应用
- 通过发现流程、OTP 引导和 `Authorization: ApiKey` 接入的外部代理客户端

## 数据模型

- `workspaces`
- `workspace_members`
- `user_settings`
- `devices`
- `cards`
- `decks`
- `review_events`
- `applied_operations`
- `sync_state`

## 数据流

### 网页应用

1. 浏览器通过 `auth.<domain>` 完成登录。
2. 网页应用从 `api.<domain>` 加载工作区数据。
3. AI 聊天请求通过 `/chat/local-turn`。
4. 复习提交在写入时会更新调度器状态。

### iOS

1. iOS 应用先把数据写入本地 SQLite。
2. 本地变更会进入待发送队列。
3. 同步通过 `/v1/workspaces/{workspaceId}/sync/push` 上传变更。
4. 同步通过 `/v1/workspaces/{workspaceId}/sync/pull` 拉取远端更新。
5. 本地数据库应用这些变更，并推进同步游标。

### 外部代理

1. 代理客户端从 `GET /v1/` 开始。
2. OTP 引导流程在 `auth.<domain>` 上进行。
3. 代理客户端会获得一个长期有效的 API 密钥。
4. 代理客户端会先加载 `/v1/agent/me`，列出可用工作区，必要时选择其中一个，然后再调用 `/v1/agent/sql/query` 和 `/v1/agent/sql/execute`。

## 调度

Nibomo 使用 FSRS 作为复习调度器。

实现说明：

- 后端和 iOS 分别维护一套相互镜像的 FSRS 实现。
- 网页应用遵循同一份调度数据契约，但不会额外内置第三套调度器实现。
- 工作区级别的调度设置包括期望保留率（`desired retention`）、学习步骤（`learning steps`）、重新学习步骤（`relearning steps`）、最大间隔（`maximum interval`）和 `fuzz`。
- 实际的复习时间戳取自 `reviewedAtClient`。

更详细的契约说明，请参阅[主仓库中的 FSRS 调度逻辑](https://github.com/kirill-markin/flashcards-open-source-app/blob/main/docs/fsrs-scheduling-logic.md)。

## 认证

- 通过 Cognito 提供邮箱 OTP 登录。
- 托管的网页应用使用跨子域共享的浏览器会话 Cookie。
- `auth.<domain>` 上的代理客户端 OTP 引导流程会返回长期有效的 `ApiKey`。
- `AUTH_MODE=none` 用于本地开发。
- `AUTH_MODE=cognito` 用于接近生产环境的认证方式。

## 部署形态

- `app.<domain>` -> CloudFront + S3
- `api.<domain>` -> API Gateway + Lambda 后端
- `auth.<domain>` -> API Gateway + Lambda 认证服务
- Postgres 部署在 AWS RDS 上

顶级域名可以继续保留给独立的营销网站使用。如果在初始部署阶段尚未被占用，基础设施也可以临时将其重定向到 `app.<domain>`。

---
*[查看此页面的带样式 HTML 版本](<https://nibomo.com/zh/docs/architecture/>)*

*AI agent 可以从 `GET https://api.flashcards-open-source-app.com/v1/` 开始，为用户在该服务中创建账号。完成设置后，也可以通过 MCP 连接 `https://mcp.nibomo.com/mcp`。*

*提示：在 https://nibomo.com 上任意 URL 后追加 `.md`，即可获得该页面的纯 Markdown 版本。*