# 账号与权限

## 账号创建

Cents 不开放注册。管理员生成一条应用邀请：

```bash
PUBLIC_ORIGIN=https://cents.example.com npm run bootstrap --workspace @cents/api
```

应用邀请使用 256 位随机 `key`，数据库只保存 SHA-256 摘要。链接固定 7 天过期并且只能创建一个账号。

开发环境可以直接添加用户：

```bash
npm run user:add -- '用户名' '至少8位的密码'
```

生产环境通过 Docker Compose 在 API 容器内运行 npm 脚本，宿主机不需要安装 Node.js 或 npm：

```bash
docker compose -f compose.pro.yaml exec api \
  npm run user:add -- '用户名' '至少8位的密码'
```

用户与个人账本在同一数据库事务内创建；用户名已存在或任一步失败时都不会留下半成品数据。

新用户打开 `/invite?key=...` 后填写姓名和密码：

- 用户 ID 由服务端生成 UUID。
- 姓名同时作为登录名，忽略首尾空格和大小写后必须唯一。
- 密码长度为 8 至 128 个字符。
- 密码使用 Argon2id 保存，参数为 19 MiB 内存、2 次迭代、并行度 1。
- 新账号自动获得唯一的“个人账本”，并成为 owner。
- 个人账本不能分享或加入其他成员，每笔账目都会由服务端强制关联到其所有者的个人账本。

已登录用户打开应用邀请时直接返回应用，不创建账号，也不消费邀请。

## 登录会话

登录成功后，服务端生成 256 位随机会话凭证，并通过 Cookie 返回：

- `HttpOnly`：前端脚本不能读取。
- `Secure`：生产环境只允许 HTTPS 发送。
- `SameSite=Lax`：限制跨站携带。
- `Path=/`：不设置 `Domain`。

数据库只保存会话凭证的 SHA-256 摘要。浏览器不保存原始密码。会话最长设置为浏览器普遍支持的 400 天，并在每次有效访问时滚动续期，因此正常使用时会一直保持登录。

登录接口使用统一的“姓名或密码错误”提示，并设置更严格的请求限速。

## 密码重置

不提供公网“忘记密码”接口。管理员登录宿主机后执行：

```bash
NEW_PASSWORD='新的密码' npm run password:reset --workspace @cents/api -- '用户姓名'
```

重置成功后，该用户的所有旧会话会立即撤销，需要使用新密码重新登录。

## 邀请类型

应用邀请与账本邀请是两个不同的能力：

- `/invite?key=...`：创建应用账号，不加入别人的账本。
- `/join-ledger?key=...`：把已经登录的用户加入指定账本。

未登录用户打开账本邀请会先进入登录页，登录成功后返回原邀请继续操作。

## 权限

- 用户只能访问自己是成员的账本。
- `owner` 可以修改账本设置并创建账本邀请。
- 个人账本名称固定，且不能创建账本邀请。
- `member` 可以查看和记账，但不能创建邀请或修改账本设置。
- 服务端在每次请求时查询成员关系，不依赖前端隐藏按钮。
- 所有写请求检查 `Origin`，API 同时启用安全响应头和请求限速。
