makecindy / makecindy/cindy

架构:建立 Profile 级账号隔离,为多账号与共享任务预留长期演进边界

Open
#1,266 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 背景

当前 Desktop 的业务 SQLite 已经按账号分文件,`sessions`、`messages`、Scheduler、IM、Provider/MCP 等数据默认不会跨账号混用。

但数据库之外仍有一部分状态落在共享的 `userData` 根目录或共享运行时单例中,例如:

- auth session / refresh token 与 auth realm;
- safeStorage 中的 Provider / OAuth 凭证;
- Device Link 授权与运行时仲裁;
- 媒体字节仓、部分 transcript / hook / IM 辅助文件;
- `currentUser`、当前 DB client、Scheduler、Maker 等模块级单例。

这会导致同机多个实例、不同构建或账号切换时出现状态互相覆盖。之前遇到的“用户没有操作却退出登录”,根因就是共享 `userData` 中的 refresh token 被另一个实例轮换/覆盖,当前实例随后被动进入未登录态。

## 本 Issue 要解决的问题

在不把所有业务表改造成多租户表的前提下,建立稳定的本地账号隔离边界,并为未来多账号同时运行、跨设备登录和显式共享任务留下清晰路径。

## 架构方向

### 1. 一个账号一个 Profile

- 一个 Profile 对应一个稳定的 Data Owner / membership。
- 一个 Profile 使用一份 Profile DB、独立凭证命名空间和独立运行时。
- 同一 Profile 的重复启动只聚焦已有运行时。
- 多账号同时运行通过多个 Profile 运行时完成。
- CN / Global 是构建期固定区域,不是用户可选的账号隔离维度。

### 2. 业务数据库继续一库一 Profile

现有业务表继续属于当前 Profile DB,不批量新增 `owner_id`。数据库文件本身就是 owner 边界,现有 FK、事务和删除语义可以保留。

需要显式区分:

- Profile DB:任务、消息、Scheduler、IM、Provider/MCP、Embedding、媒体账本等;
- Machine Registry:Profile 列表、账号去重、默认 Profile、最近使用时间和运行时租约;
- Runtime Lease:同一 Profile 的进程 / Device Link 单持有者仲裁;
- Machine Cache:Agent、模型、插件包等可再生缓存,不承载账号归属。

### 3. 共享任务必须是显式能力

不同账号默认不能读取同一本地对话库。未来如果需要账号 A 与账号 B 共同访问一个任务,应建立独立的 Shared Workspace / Shared Task + ACL 机制,而不是把现有 `sessions` 直接改成多 owner。

### 4. 多端登录需要服务端配合

本地 Profile 可以解决同机实例互相覆盖凭证的问题;不同物理设备之间是否互相登出,还需要服务端提供独立的 refresh-token family / device session:

- 新设备登录不撤销其他设备;
- 普通 logout 只撤销当前设备;
- “退出所有设备”才撤销全部 session。

## 兼容性要求

- 旧用户升级后不要求重新登录。
- 现有 `-.db` 先登记为 legacy Profile,不立即搬迁或复制大数据库。
- Registry 缺失时可从现有 auth + legacy DB 自动补建。
- 旧路径和旧 safeStorage key 保留兼容读取窗口。
- Schema 变化继续遵守 append-only migration、`db:validate` 和 migration replay。
- 不在旧客户端仍可能运行时直接移动旧数据库、媒体仓或凭证。
- 失败可重试、幂等、可回滚,不能丢聊天、附件、配置或凭证。

## 建议实施顺序

1. 冻结表级 ownership matrix 与路径清单。
2. 引入 Machine Registry 和 ProfileContext,但保持 legacy DB / legacy path 可用。
3. 让 DB client、auth、secret store、Scheduler、Maker、IM、Device Link 共用同一 Profile scope。
4. 按模块逐项迁移旁路存储与运行时租约。
5. 最后再评估旧 Profile 的物理目录搬迁。
6. 另行设计 Shared Task / ACL 与服务端多设备 session 语义。

## 非目标

- 不在本 Issue 中实现多账号 UI。
- 不批量给 `sessions` / `messages` / `schedules` 增加 `owner_id`。
- 不把 refresh token、OAuth 凭证或 Provider key 放入 SQLite。
- 不把 CN / Global 做成运行期可切换的 Profile。
- 不直接移动或删除现有用户数据。

## 验收标准

- Profile A 的任务、消息、凭证、媒体和 Device Link 事件不会在 Profile B 的正常产品路径中可见。
- 同机多个 Profile 可同时运行而不覆盖 refresh token。
- 旧 Profile 可在不重新登录的情况下继续打开原有聊天和配置。
- 未来共享任务具有显式授权、撤销和审计,而不是隐式跨账号可见。

Contributor guide

Open the contributing guide

Research direction

No files, tests, or entry points are named. Start by mapping the listed Profile DB, Machine Registry, Runtime Lease, auth, secret-store, Scheduler, Maker, IM, and Device Link components into an ownership matrix. Done means the stated isolation, legacy compatibility, concurrent-profile, retry, rollback, and explicit-sharing acceptance criteria are met.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
authentication, databases, desktop
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.