Parcourir la source

docs: 永远全放开标准 + 状态残留排查

## 新增标准(用户裁决 2026-11-12)
「希望永远全放开」:
```
GATEWAY_ALLOW_ALL_USERS=true
MATRIX_ALLOWED_USERS=          ← 必须留空
```
原因:非空白名单优先于 ALLOW_ALL,白名单非空则全放开失效。

## 新增排查项
`running` 状态残留(实测于 agent-lj):
- 左侧一直转圈但右上角在线
- 根因:last_activity_at 只在 turn 开始时写 + 120 秒衰减窗口 + 上报间隔也是 120 秒
- 根治:判据改用消息表最新时间戳 / turn lease

## 踩坑表 5 → 8 条
新增:ALLOW_ALL 配错、.env 改了不生效、reaction sender 误判

Co-Authored-By: Claude Code <noreply@anthropic.com>
Huccct il y a 13 heures
Parent
commit
9522dd002c

+ 112 - 8
docs/03-interface/多用户共用容器-完整规范-20261112.md

@@ -131,19 +131,82 @@ If that is you or someone you trust, add the ID to MATRIX_ALLOWED_USERS …
 ⚠️ **危险点**:如果哪天有人把 `.env` 的白名单修好并加载进进程,**行为立刻反转** ——
 只有名单里的人能用,其他人全部被拒。
 
-### 3.5 推荐配置
+### 3.5 🔴 标准配置:**永远全放开**(用户裁决 2026-11-12)
 
-| 目标 | 配置 | 说明 |
-|---|---|---|
-| **所有人可用**(当前需求) | `GATEWAY_ALLOW_ALL_USERS=true`<br>**且** `MATRIX_ALLOWED_USERS` 留空 | 行为确定 |
-| 只允许特定人 | `MATRIX_ALLOWED_USERS=@a,@b,@c`<br>`GATEWAY_ALLOW_ALL_USERS=false` | 每加一人**必须重启** |
+> **用户原话**:「希望永远全放开。在文档里面体现。」
+
+**所有接入 Fmode App 的容器,统一使用这个配置:**
+
+```bash
+GATEWAY_ALLOW_ALL_USERS=true
+MATRIX_ALLOWED_USERS=          # ← 必须留空
+```
+
+#### 为什么必须留空
+
+```
+① MATRIX_ALLOWED_USERS 非空? → 只认名单,ALLOW_ALL 被完全忽略
+② 为空 → 看 GATEWAY_ALLOW_ALL_USERS
+   true  → 全部放行
+   false → 拒绝
+```
+
+**只要白名单非空,ALLOW_ALL 就等于没开。** 这是最容易配错的地方。
+
+#### 配置步骤
+
+```bash
+# 1. 备份
+cp /opt/data/.env /opt/data/.env.bak-$(date +%Y%m%d-%H%M%S)
+
+# 2. 清空白名单
+sed -i 's|^MATRIX_ALLOWED_USERS=.*|MATRIX_ALLOWED_USERS=|' /opt/data/.env
+
+# 3. 开启全放开
+grep -q "^GATEWAY_ALLOW_ALL_USERS=" /opt/data/.env \
+  && sed -i 's|^GATEWAY_ALLOW_ALL_USERS=.*|GATEWAY_ALLOW_ALL_USERS=true|' /opt/data/.env \
+  || echo "GATEWAY_ALLOW_ALL_USERS=true" >> /opt/data/.env
+
+# 4. 重启
+s6-svc -r /run/service/gateway-default
+```
+
+#### 验证(**必须做**)
 
-**验证命令**:
 ```bash
 PID=$(cat /opt/data/gateway.pid | tr -d '[:space:]')
 tr '\0' '\n' < /proc/$PID/environ | grep -E "ALLOW_ALL|MATRIX_ALLOWED"
 ```
 
+**期望输出**:
+```
+GATEWAY_ALLOW_ALL_USERS=true
+MATRIX_ALLOWED_USERS=          ← 空
+```
+
+**然后验证一个陌生用户**:用 `_principal_authorized('@任意编造的用户:fmode.cn')` 应该返回 `True`。
+
+#### 这个配置的效果
+
+| 用户 | 能否使用 |
+|---|---|
+| 容器主人 | ✅ |
+| 同公司任何成员 | ✅ |
+| **其他公司的任何用户** | ✅(全放开的代价) |
+| **未来新加入公司的成员** | ✅ **无需任何配置** |
+
+**唯一代价**:跨公司用户只要知道容器 mxid 也能用。这是用户明确接受的取舍。
+
+---
+
+### 3.5.1 备选:只允许特定人(不推荐)
+
+| 目标 | 配置 |
+|---|---|
+| 只允许名单里的人 | `MATRIX_ALLOWED_USERS=@a,@b,@c`<br>`GATEWAY_ALLOW_ALL_USERS=false` |
+
+**代价**:每加一人**必须重启 gateway**(打断进行中的对话)。
+
 ### 3.6 为什么「每次加人」很麻烦
 
 | 环节 | 问题 |
@@ -222,7 +285,47 @@ tr '\0' '\n' < /proc/$PID/environ | grep -E "ALLOW_ALL|MATRIX_ALLOWED"
 | `task` | `pre_llm_call` 的 `user_message` 首行 | 顶栏显示「本会话在做什么」 |
 | `updatedAt` | 写入时的 epoch ms | App 判断 running 是否过期(10 分钟) |
 
-### 5.3 心跳间隔(统一标准)
+### 5.3.1 🔴 `running` 状态残留(实测于 agent-lj,2026-11-12)
+
+**现象**:左侧会话列表一直转圈,但右上角显示「在线」。
+
+**根因**(容器自查确认,三重叠加):
+
+| 因素 | 问题 |
+|---|---|
+| `last_activity_at` 只在 turn **开始**时写一次 | 长 turn 期间口径越算越偏 |
+| 120 秒衰减窗口 | 窗口内一直显示 `running` |
+| 上报间隔最长也是 120 秒 | 和窗口一样长,形成空档 |
+
+**实测数据**:
+```
+running $approot_mwcdacku_4dedsov4:fmode.cn  age=140s   ← 卡了 140 秒
+```
+
+**为什么 lj-test 没出现**:同一套缺陷,只是没撞上那个窗口。
+
+**容器侧根治方案**(待实施):
+把 `status-report.py` 的判据从 `last_activity_at` 改为**消息表最新时间戳**,或引入 turn lease。
+
+**App 侧兜底**(已实现):
+`running` 超过 **10 分钟**未更新 → 视为过期,回落到「不知道」。
+
+> ⚠️ 这个兜底治标不治本:10 分钟内仍会显示转圈。
+> 真正的修法在容器侧。
+
+### 5.3.2 两个数据源不同步的固有风险
+
+| 界面 | 数据源 | 更新时机 |
+|---|---|---|
+| 右上角 | `heartbeatAt` + ✅ reaction | 实时 / 心跳 |
+| 左侧列表 | `currentSessions[].status` | turn 首尾(有延迟) |
+
+**所以「左侧转圈 + 右上角在线」是可能的**,不一定是 bug ——
+但如果持续超过 2 分钟,就该怀疑容器状态残留。
+
+---
+
+### 5.4 心跳间隔(统一标准)
 
 | 项 | 标准值 |
 |---|---|
@@ -236,10 +339,11 @@ tr '\0' '\n' < /proc/$PID/environ | grep -E "ALLOW_ALL|MATRIX_ALLOWED"
 
 **验收**:观察 10 分钟(无 turn 活动),上报请求应 **≤ 1 次**。
 
+**实测反例**:`agent-lj` 曾用「30 秒心跳 + 每 2 分钟 cron」,比标准快 120 倍,已改回。
+
 ---
 
 ## 六、App 侧(已实现,无需容器配合)
-
 | 能力 | 实现 |
 |---|---|
 | 会话级状态隔离 | 只认本会话的 `currentSessions` 记录 |

+ 53 - 1
docs/03-interface/提示词-容器自我优化-v2-20261111.md

@@ -287,7 +287,7 @@ test -w /opt/data/plugins && echo "plugins 可写" || echo "不可写"
 
 ---
 
-## ⚠️ 五个真实踩过的坑(务必避开)
+## ⚠️ 八个真实踩过的坑(务必避开)
 
 | # | 坑 | 实际情况 |
 |---|---|---|
@@ -296,6 +296,58 @@ test -w /opt/data/plugins && echo "plugins 可写" || echo "不可写"
 | 3 | 以为 `m.typing=false` 可以当结束信号 | 错。实测早于正文 0.6 秒。**用 ✅ reaction** |
 | 4 | 声称「已完成」但没实测 | 有过容器改的是**只读文件**,一个字没生效。**必须贴实测输出** |
 | 5 | 以为「历史会话太多导致慢」 | 错。实测 242 会话 / 40MB **不进新会话上下文**。慢的是 DNS |
+| 6 | **以为配了 `ALLOW_ALL` 就放开了** | 错。**非空白名单优先于 ALLOW_ALL**。必须把 `MATRIX_ALLOWED_USERS` **清空**,否则全放开等于没开 |
+| 7 | **以为改了 `.env` 就生效** | 错。`os.environ` **启动时读一次**,且某些行可能根本没被加载。必须重启 + **回读 `/proc/<pid>/environ`** |
+| 8 | **以为 reaction 的 sender 是容器** | 错。App 用容器身份登录,**用户点的 emoji 也显示成容器发的**。要看 target 和时机 |
+
+---
+
+## 🔴 标准配置:永远全放开(用户裁决 2026-11-12)
+
+```bash
+GATEWAY_ALLOW_ALL_USERS=true
+MATRIX_ALLOWED_USERS=          # ← 必须留空
+```
+
+**目的**:同公司任何成员(以及未来新加入的成员)都能用,**无需每次加白名单 + 重启**。
+
+**验证**(必须做):
+```bash
+PID=$(cat /opt/data/gateway.pid | tr -d '[:space:]')
+tr '\0' '\n' < /proc/$PID/environ | grep -E "ALLOW_ALL|MATRIX_ALLOWED"
+```
+
+期望:
+```
+GATEWAY_ALLOW_ALL_USERS=true
+MATRIX_ALLOWED_USERS=          ← 空
+```
+
+**然后验证陌生用户**:`_principal_authorized('@随便编一个:fmode.cn')` 应返回 `True`。
+
+---
+
+## 🔴 状态残留自查(`running` 卡住)
+
+**现象**:左侧列表一直转圈,但右上角显示「在线」。
+
+**检查**:
+```bash
+# 看有没有 running 超过 2 分钟没更新的
+python3 -c "
+import json,time
+# 从上报表里读 extra.currentSessions,检查 updatedAt
+"
+```
+
+**常见原因**(实测于 agent-lj):
+| 因素 | 问题 |
+|---|---|
+| `last_activity_at` 只在 turn 开始时写 | 长 turn 期间越算越偏 |
+| 衰减窗口 120 秒 | 窗口内一直 running |
+| 上报间隔也是 120 秒 | 形成空档 |
+
+**根治**:判据改用「消息表最新时间戳」,或引入 turn lease。
 
 ---