Dify 一周动态-2026-W14

0 阅读12分钟

Dify W14 周报封面

日期范围: 2026-03-25 - 2026-03-31 摘要: v1.13.3 如期发布,聚焦流式可靠性与工作流修复;50 位贡献者参与;StreamsBroadcastChannel 并发问题由 QuantumGhost 重写解决;access token 退出失效安全 PR 终于取得实质性架构讨论进展;全局 httpx 客户端性能优化提上日程。


📦 版本发布

1. v1.13.3(Latest Stable)— 稳定性与流式修复

来源: github.com/langgenius/… 发布时间: 2026-03-27 类型: Stable 贡献者: 50 人(含 fatelei、QuantumGhost、lyzno1、tmimmanuel、Desel72 等);reaction 数 7(👍2 🎉5)

新功能:

  • ✅ LLM / 问题分类器 / 变量提取器节点支持变量引用配置模型参数(#33082,@scdeng)

Bug 修复(重点):

  • 🔥 StreamsBroadcastChannel 流式可靠性:修复 replay 和并发问题,确保前后端事件投递稳定(#34030 + #34061,@QuantumGhost)
  • 🔥 工作流编辑器:修复粘贴节点保留 Loop/Iteration 元数据的问题;阻止 HumanInput 节点被粘贴到无效容器(#29983 + #34077)
  • 🔥 运行时执行:恢复 prompt message 转换逻辑(v1.13.2 cherry-pick),修正 max_retries=0 处理(#33666 + #33619 + #33688)
  • 知识库检索:保留 Web 响应中的引用元数据、修复 dataset icon 缺失崩溃、hit-count 查询过滤纠正、索引文档 chunk 预览恢复(#33778 + #33907 + #33757 + #33942)

底层改进:

  • 大规模 testcontainers 迁移(20+ PR by @Desel72)
  • EnumText 持续迁移(@tmimmanuel 系列 PR)
  • TypedDict 类型化持续推进(@bittoby)
  • 前端 toast 组件全面迁移至 Base UI Toast API(@lyzno1)

⚠️ 升级注意:Sandbox 默认 Python/Node.js 路径已在上个版本更新,如有现存 Sandbox 配置文件需手动更新路径。

v1.13.3 发布亮点


🗓 本周发版节律

版本类型日期亮点
v1.13.3Stable2026-03-27流式 BroadcastChannel 修复、变量引用模型参数、知识库修复

📊 本周共 1 次正式发布(1 stable),节奏回归正常:上周 1.13.3 里程碑延期后本周顺利发布。


🔥 热门话题

2. 🔄 StreamsBroadcastChannel 重写取代 xreadgroup PR

来源: github.com/langgenius/… 时间: 2026-03-25(PR #34030 merged) 热度: 替代 PR #33884

摘要: W13 追踪的 fatelei 的 xreadgroup PR #33884 被 QuantumGhost 关闭(superseded),QuantumGhost 另起 PR #34030 直接修复 StreamsBroadcastChannel 的 replay 和并发问题。#34030 采用了更简洁的方案——从流末尾开始读取消息,避免了 consumer group 设计的额外复杂度。后续 #34061 进一步修复并发问题。两个 PR 均已合入 v1.13.3。

流式架构演进


3. 🔄🔐【跟进】access token 退出后仍有效 PR #31794 — 架构讨论取得突破

来源: github.com/langgenius/… 时间: 2026-03-17(QuantumGhost 评论)— 2026-03-31(仍 open) 热度: 安全高优先,5 参与者

摘要: 停滞 8 周的安全修复 PR #31794 本周取得实质性进展。laipz8200 此前以"stateless JWT 设计"为由 request changes,但 QuantumGhost 于 2 周前提出折中方案:引入 SessionRevocationStorage 接口(含 revoke()is_revoked()expunge() 方法),默认 NullSessionRevocationStorage 不引入任何状态,部署者可选择 Redis 后端。fatelei 据此重写代码(force-push 多次),Copilot AI Review 也提出了 SHA-256 fallback 等改进建议。目前 laipz8200 的 requested changes 仍未解除,但架构方向已明确。这是近 2 个月来该 PR 最活跃的一周。

access token 三方博弈


4. 🚀 性能优化:全局 httpx 客户端(#34309 / #34311)

来源: github.com/langgenius/… 时间: 2026-03-31(今日 4 小时前 open) 热度: 13 条评论

摘要: fatelei 提出 #34309 性能优化提案:将每次请求新建 httpx 客户端改为全局复用,减少连接开销。对应 PR #34311(size:L)同日提交,已有 13 条评论讨论中。此优化对高并发场景下的 API 调用延迟有直接正面影响。


5. 🔐 Docker Compose 硬编码默认密钥安全风险(#34321)

来源: github.com/langgenius/… 时间: 2026-03-31(1 小时前 open) 热度: 新 issue

摘要: mahdirajaee 报告 docker-compose.yaml 中硬编码默认 secrets(如数据库密码、API key 等)对生产部署构成安全风险。自托管用户若不手动修改默认值,可能暴露敏感数据。相关联 issue #34320 同时报告 api/worker/worker_beat 缺少 healthcheck 定义。


6. 🐛 MCP 工具从 1.7.1 升级到 1.14.0-rc1 后失效(#34275)

来源: github.com/langgenius/… 时间: 2026-03-30(20 小时前 open) 热度: 5 条评论

摘要: 用户从 Dify 1.7.1 升级到 1.14.0-rc1 后 MCP 工具完全不可用。此问题与 #24224(MCP session per-call reinitialization)可能相关,属于跨大版本升级的兼容性问题。


🚀 新功能 / 合并 PR

7. feat: 变量引用支持模型参数配置(#33082,已入 v1.13.3)

来源: github.com/langgenius/… 时间: 2026-03-27(v1.13.3 发布)

摘要: @scdeng 实现 LLM、问题分类器、变量提取器节点中模型参数的变量引用支持。此功能允许用户在工作流中动态配置模型参数(如 temperature、max_tokens 等),大幅增强工作流灵活性。


8. feat: inner API 端点支持管理员 DSL 导入/导出(#34059)

来源: github.com/langgenius/… 时间: 2026-03-27(已 merged)

摘要: @zhangx1n 新增内部管理 API 端点,支持管理员级别的 DSL 导入/导出操作。适用于企业级批量应用迁移和备份场景。


9. chore: 供应链安全加固(#34317)

来源: github.com/langgenius/… 时间: 2026-03-31(已 merged)

摘要: @hyoban 提交供应链安全改进,减少依赖链中潜在的安全风险。属于 CI/CD 安全最佳实践系列改进。


10. test: 大规模 testcontainers 迁移(@Desel72 系列)

来源: 多个 PR(#34304, #34305, #34306, #34286 等) 时间: 2026-03-28 — 2026-03-31

摘要: @Desel72 和 @YB0y 本周持续推进 testcontainers 迁移工作,将 mock-based 测试替换为真实数据库集成测试,覆盖 datasets controller、workflow controller、import controller、apikey controller 等多个模块。@Desel72 同时开始 sessionmaker().begin() 重构系列(#34283, #34284)。测试基础设施质量持续提升。


🐛 活跃 Bug 与问题

11. 💪 Workflow 用量应分别追踪 input/output token(#34315)

来源: github.com/langgenius/… 时间: 2026-03-31(3 小时前 open)

摘要: @JokerQyou 提出功能请求:工作流用量统计应分别追踪 input/output tokens 而非只显示总量,便于成本分析和优化。


12. 🔄【跟进】MCP session per-call reinitialization #24224 — 里程碑移至 next

来源: github.com/langgenius/… 时间: 本周无新评论 热度: 仍 open,assigned to @55Kamiryo

摘要: QuantumGhost 在 2 周前将此 issue 的里程碑从 v1.13.1 移至 "next"。55Kamiryo 的 PR #28621(MCP session reuse per workflow run)仍在维护,但短期内不太可能合入。该问题影响所有需要多步 MCP 工具链的用户(如 playwright navigate → screenshot)。


13. 🔄【跟进】1.14.0-rc1 CORS/Skill 创建 bug #32816 — 仍 open

来源: github.com/langgenius/… 时间: 2026-03-31(SmallStom 4 小时前评论) 热度: 9 条评论

摘要: 1.14.0-rc1 的 Skill 创建 CORS + ghost record + 500 error 问题仍 open。社区用户分享了 FILES_API_URL 配置为外网域名的变通方案,但根本问题(Ghost record 缺乏事务回滚)未修复。无人 assigned。


14. 🔄【跟进】userinput.files LEGACY 字段导致 Agent 节点故障 #33976 — 仍 open

来源: github.com/langgenius/… 时间: 上周开启,本周无新进展

摘要: Cloud 用户报告 Workflow 中不可删除的 userinput.files LEGACY 字段导致所有 Agent 节点报错"file type parameter file not supported in agent"。dosubot 确认为已知问题(hardcoded readonly system variable),Cloud 用户无可用的解决方案。与 #30835 关联。


📊 数据概览

维度数据
GitHub Open Issues372(与 W13 持平)
GitHub Open PRs407(较 W13 的 424 下降,大量 PR 合入 v1.13.3)
版本发布1 次(v1.13.3 stable)
v1.13.3 贡献者50 人
HN 热帖本周无 Dify 专题
Reddit 热帖本周无新帖
本周核心主题v1.13.3 发布、流式 BroadcastChannel 修复、access token 安全 PR 进展、全局 httpx 优化
当前最新稳定版v1.13.3
在途重点access token 安全修复 #31794、MCP session 复用 #28621、全局 httpx 客户端 #34311

🔍 深入分析:Access Token 安全修复架构演进(PR #31794)

PR: fix: fix access token is still valid after logout #31794 Issue: #31793 — 登出后 access token 仍有效 状态: Open(+479/-11,8 文件变更,29 条讨论,14 checks 运行中) 关联: EE-1525(企业版内部 issue,由 douxc 确认)

1. 问题本质

Dify 的认证流程基于 JWT access token + Redis-backed refresh token 的双 token 模型。登出时,AccountService.logout() 仅删除 Redis 中存储的 refresh_token,而 JWT access token 一旦签发即不可撤销——这是经典的无状态 JWT 设计取舍。

实际影响:用户点击「退出登录」后,在 access token 过期(默认 TTL)之前,该 token 仍然可以正常访问所有 API。对于 SaaS/多租户场景(Dify Cloud),这是一个真实的安全风险:离职员工、被踢出的协作者、或被盗的 session 在退出后仍有操作窗口。

2. 时间线与关键人物

时间事件角色
01-31fatelei 提交初版 PR:JWT 加入 jti 声明,登出时将 jti 写入 Redis 黑名单@fatelei(Contributor)
02-06douxc 关联 EE-1525(企业版需求确认)@douxc(Member)
02-10Copilot AI 第一轮 review:建议改善代码风格、性能、测试覆盖Copilot
~03 初laipz8200 请求修改并阻塞合并:「The stateless JWT design was chosen with performance and ease of use in mind; there is currently no reason to introduce additional state to change it.」@laipz8200(Member,CODEOWNER)
~03 初fatelei 回应:「no additional state, it just add a access token blacklist in redis」@fatelei
03-17QuantumGhost 提出 SessionRevocationStorage 接口方案——折中妥协@QuantumGhost(Contributor,CODEOWNER)
03-17fatelei 据此重写实现,force-push 新版本(feat: add session revoke storage@fatelei
03-17Copilot AI 第三轮 review:2 个 Medium 级安全建议Copilot
W14laipz8200 的 requested changes 仍未解除,PR 仍处于 blocked 状态

3. 架构之争:无状态 vs 有状态

这是一场经典的 JWT 安全性辩论,三方立场如下:

laipz8200 的立场(守)——纯无状态 JWT

"The stateless JWT design was chosen with performance and ease of use in mind; there is currently no reason to introduce additional state to change it."

  • JWT 的核心价值是无状态验证:只需 SECRET_KEY 即可在任意节点验证,无需查询存储
  • 引入黑名单 = 每次请求都要查 Redis → 本质上退化为有状态 session
  • 如果需要即时撤销,不如直接用 session-based auth

fatelei 的立场(攻)——实用安全优先

"no additional state, it just add a access token blacklist in redis to disable user visit api after logout"

  • 安全缺陷是现实存在的(企业版 EE-1525 也确认了需求)
  • Redis 查询只在黑名单命中时有额外开销(miss 是 O(1) EXISTS 操作)
  • 不改变 JWT 本身的签发和验证逻辑,只是增加一层可选检查

QuantumGhost 的折中(和)——Strategy Pattern

"What about extract an interface named SessionRevocationStorage, then let deployers of Dify specify SessionRevocationStorage to use while deploying Dify?"

这个方案当前已被 fatelei 实现,是 PR 的最新架构:

4. 最新架构详解

SessionRevocationStorage 架构

┌──────────────────────────────────────────────────────────┐
│                  SessionRevocationStorage (Protocol)      │
│                                                           │
│  revoke(token_id, expiration_time) → None                │
│  is_revoked(token_id) → bool                             │
│  expunge() → None                                        │
└─────────────────┬───────────────────┬────────────────────┘
                  │                   │
    ┌─────────────┴──────┐  ┌────────┴──────────────────┐
    │ NullSessionRevoke  │  │ RedisSessionRevoke        │
    │ Storage (默认)      │  │ Storage                   │
    │                    │  │                           │
    │ revoke → no-op     │  │ revoke → SETEX key TTL 1 │
    │ is_revoked → False │  │ is_revoked → EXISTS key   │
    │ expunge → no-op    │  │ expunge → no-op (TTL 自清)│
    └────────────────────┘  └───────────────────────────┘

关键设计决策

决策点选择理由
默认行为NullSessionRevocationStorage(禁用)兼容现有部署,不破坏无状态语义
配置方式SESSION_REVOCATION_STORAGE=null|redis运维层面可选启用,无代码侵入
存储键passport:blacklist:jti:{jti}使用 JWT 标准 jti 声明做唯一标识
TTL 策略SETEX(key, token剩余生存时间, "1")token 过期后黑名单记录自动清除,不会无限增长
单例模式_singleton + factory get_session_revocation_storage()避免每次请求创建新实例
旧 token 兼容无 jti 的 token fallback 使用原始 token 字符串过渡窗口内的向后兼容

请求流程(启用 Redis 模式后)

 用户请求 → JWT 签名验证 → [pass] → SessionRevocationStorage.is_revoked(jti)
                                        │
                                   ┌────┴────┐
                                   │  Redis   │
                                   │ EXISTS   │
                                   └────┬────┘
                                   存在? → 401 Token has been revoked
                                   不存在? → 放行,返回 payload

登出流程

POST /logout → extract_access_token(request)
             → AccountService.logout(account, access_token)
                → PassportService.revoke(token)
                   → decode(token, verify_signature=False)
                   → extract jti + exp
                   → storage.revoke(jti, exp_datetime)
                      → SETEX "passport:blacklist:jti:{jti}" {remaining_ttl} "1"
                → delete refresh_token from Redis(原有逻辑)

5. 遗留问题与风险

#问题严重度状态
1laipz8200 的 requested changes 未解除🔴 阻塞尽管架构已采纳 QuantumGhost 的折中方案,laipz8200 作为 CODEOWNER 的 review 仍为 blocking
2旧 token 使用原始字符串做 Redis key🟡 安全Copilot 指出:token_id = payload.get("jti") or token 会将完整 JWT 明文存入 Redis key,泄露 token material 给有 Redis 访问权限的人。建议改用 SHA-256 摘要
3黑名单 key 前缀重复定义🟡 维护passport:blacklist:jti: 分别硬编码在 passport.pyRedisSessionRevocationStorage 中,未来可能不一致
4Redis 高可用依赖🟡 可靠性QuantumGhost 指出:启用 Redis 模式后,如果 Redis 不可用,is_revoked() 会抛异常 → 所有认证请求被阻断。需要考虑 fallback 策略(如 Redis 不可达时 fail-open)
5过渡窗口🟢 低PR 合并后,已签发的旧 token(无 jti)在过期前不能被优雅地撤销。窗口长度 = access token 的当前 TTL

6. 行业对比

方案即时撤销每请求开销复杂度谁在用
纯无状态 JWT(Dify 现状)零额外 I/O大量小型项目
JWT + jti 黑名单(PR #31794 Redis 模式)1× Redis EXISTSAuth0, Firebase(类似)
短 TTL JWT + Refresh Token Rotation⚠️ 延迟撤销零额外 I/OOAuth 2.0 标准推荐
Opaque Token + Session Store1× DB/Redis 查询GitHub, GitLab

QuantumGhost 的方案本质上是 给部署者选择权:默认零开销(Null),需要时开启 Redis 黑名单。这在开源项目中是比较明智的策略——既不强制所有用户承担额外延迟,又为安全敏感场景提供了标准化出口。

7. 展望

  • 短期:需要 laipz8200 重新 review 并解除 blocking。考虑到 QuantumGhost 的方案已经将默认行为设为 Null(完全保持现有无状态语义),laipz8200 原始的异议理论上已被解决
  • 中期:Copilot 提出的 SHA-256 fallback、key 前缀去重建议需要在合并前处理
  • 长期:如果 Dify 推进 OAuth 2.0 / OIDC 集成(已有相关 issue),可考虑将 SessionRevocationStorage 扩展为更通用的 token 生命周期管理层