Cloudflare D1(SQLite) · USER_DB(apps/server/wrangler.jsonc) · contexts/users

USER_DB

Berth 的「户口本 + 门禁登记处」:记录你是谁(钱包 → 用户)、你在哪些设备上登录(手机 / 网页 / 桌面),以及公开资料、关注和收藏。

不存钱:订单 / 仓位 / 资产 / 交易授权的权威都在 Hyperliquid 链上(ADR-0083);私钥归 wallet,交易授权归 account。

10 张表 73 个字段 5 个聚合 5 个迁移 语义层已对齐 · sha256:869d3aeb46871a84

板块颜色贯穿全文

全库约定

业务流程跟着流程看表怎么变

📱 第一次用手机钱包登录

  1. App 请求登录 → 服务端在 user_login_clock 取号,写一条 login_challenges
    原文由服务端保存,验签不接受客户端自己拼的版本
  2. 用户用钱包签名,App 把签名交回来
  3. 一个 batch 里一次做完:没有这个钱包就建 users(有就复用)→ 发 user_sessions → 删挑战 → 登记 user_login_devices
    要么全成功,要么全不做;同一安装上的旧会话被结束
  4. App 拿到 token(库里只有它的 SHA-256),30 天后到期需重新登录

💻 电脑扫码登录,agent 名额满了要顶掉一台

  1. 电脑出二维码 → 已登录的手机扫码确认
    配对过程在 PairingSession DO,不进 USER_DB
  2. 电脑在本机生成一把 HL agent,手机签 approveAgent
  3. HL 每账户 named agent 名额已知下限是 3;满了默认顶掉最久没活跃的那台电脑,沿用它的 agent 名,同名重批即注销旧 agent
  4. 插入新的 user_sessions,同名旧会话写上 ended_at + agent_revoked_at,那台电脑被当场踢下线
  5. 到期前 7 天起可续期:换新 agent、手机签一次,会话再延 30 天

🏷️ 把用户名从 alice 改成 bob

  1. 检查冷却:当前名用满 14 天了吗?没满 → USERNAME_COOLDOWN
  2. alice 的 held_until 设为「现在 + 30 天」,变成保留名
  3. 插入 bob,held_until = NULL 即当前名
    bob 已被占用 → 整笔回滚,alice 不变(USERNAME_UNAVAILABLE)
  4. 30 天后 cron 删掉 alice,别人才能注册这个名字

设计要点

🔐 token 只存哈希库泄露也拿不到能用的凭证。对外只暴露 session_id。
🕰️ 不删行,用时间戳标状态ended_at / revoked_at / held_until:能展示「待撤销设备」,旧设备上线也能知道记录已被改过。
⚛️ batch 代替事务D1 没有交互式事务;多步写放进一个 db.batch,配合 INSERT … SELECT … WHERE 条件写防并发。
🔢 版本号 + 回执 = 多端同步收藏用 version 做 CAS、每次操作留回执,离线回放不会覆盖新状态。
🎫 login_order 排先后同一安装并发登录或旧挑战晚到,都不能覆盖较新的设备登记。
⛓️ 链上状态不复制agent 是否有效实时查 HL extraAgents;库里只记「我们已知它被撤掉了」。