From 23b2820101de6c3d79f7f95165968e1c37a16aea Mon Sep 17 00:00:00 2001 From: Wang Defa <1+wangdefa@noreply.gitea.bcde.io> Date: Thu, 30 Jul 2026 12:44:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=20v0.8.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .trellis/.version | 2 +- .trellis/spec/backend/auth-login-methods.md | 132 +++++++++++++++++++ .trellis/spec/backend/database-guidelines.md | 4 + .trellis/spec/backend/index.md | 1 + .trellis/spec/backend/oci-audit.md | 2 +- .trellis/workflow.md | 39 +++--- CHANGELOG.md | 16 +++ DASH_VERSION | 2 +- cmd/server/main.go | 2 +- docs/docs.go | 2 +- docs/swagger.json | 4 +- docs/swagger.yaml | 2 +- 12 files changed, 181 insertions(+), 27 deletions(-) create mode 100644 .trellis/spec/backend/auth-login-methods.md diff --git a/.trellis/.version b/.trellis/.version index 8b707c6..b976b13 100644 --- a/.trellis/.version +++ b/.trellis/.version @@ -1 +1 @@ -0.6.7 \ No newline at end of file +0.6.10 \ No newline at end of file diff --git a/.trellis/spec/backend/auth-login-methods.md b/.trellis/spec/backend/auth-login-methods.md new file mode 100644 index 0000000..7a1707a --- /dev/null +++ b/.trellis/spec/backend/auth-login-methods.md @@ -0,0 +1,132 @@ +# Auth Login Methods(登录方式接入契约) + +> 面板现有登录方式:密码(+可选 TOTP)、OIDC / GitHub(OAuth)、通行密钥(Passkey / WebAuthn)、Web3 钱包(EIP-4361)。 +> 新增或修改任何登录方式时,以下契约是安全红线,缺一不可。 + +## 1. 登录守卫(防爆破) + +公开登录端点的失败必须计入 `loginGuard`:`guardKey(clientIP, <账号标识>)`,账号未知用固定占位名(如 passkey 的 `__passkey__`),已知用其标识(如钱包地址小写)。锁定检查在校验之前;失败经 `auth.failLogin`(自带阈值转锁定与告警推送),成功 `guard.success`。阈值/时长取安全设置,不硬编码。 + +## 2. 系统日志留痕 + +公开登录端点不经系统日志中间件,handler 必须显式 `logs.Record` 成败(参考 `authHandler.recordLogin` / `passkeyHandler.recordLogin` / `walletHandler.recordWallet`);登录失败是安全关键事件,不允许无痕。JWT 组内的绑定/解绑由中间件自动留痕,无需额外埋点。 + +## 3. 令牌版本(TokenVersion) + +绑定、解绑、凭据及认证因子变更均属敏感变更:必须在事务内 +`bumpTokenVersionTx`(或 `RevokeSessions`)使旧 JWT 失效,并按第 6 节携带 +`TokenProof`、接续操作者会话。因子写入与版本递增不得拆成两个独立提交。 + +## 4. 「至少一种登录方式」不变量 + +密码登录禁用要求至少一种在当前 `EffectiveAppURL` 下**可实际登录**的免密方式 +(`usablePasswordlessTx`);地址为空时 Passkey、钱包和 OAuth 均不可用。禁用期间移除 +最后一种免密方式被拒:解绑身份走 `ensureNotLastLogin`,删通行密钥走对称检查。 +这些检查都在事务内先 `lockUserForAuthChange` 锁用户行,防并发绕过。新登录方式要 +明确决策如何参与该不变量: + +- **复用 `UserIdentity` 表**(如钱包,provider+subject 唯一)→ 走 `identityCountTx` 自动参与,零改动; +- **独立表**(如 Passkey 的 `UserPasskey`)→ 参照 `passkeyCountTx` 增加计数入口,并在开启门槛(`hasAnyPasswordless`)与两个 `ensureNot*` 检查里对称接入;api 层把 `ErrLastIdentity` 映射 409。 + +## 5. 挑战/状态的一次性消费 + +挑战-响应类流程(OAuth state、WebAuthn challenge、SIWE nonce)一律进程内 `map + mutex + TTL + gc`,取用即删(参考 `oauthPending` / `passkeyPending` / `walletPending`);不落库、不引 Redis。TTL 5-10 分钟。 + +## 6. 场景:已鉴权敏感事务的提交屏障 + +### 1. Scope / Trigger + +凡请求先通过 JWT 中间件、随后仍会读取 body、等待外部回调或写入认证/安全配置, +都必须防止请求处理期间的 Logout、定点撤销或撤销全部会话被旧请求绕过。 + +### 2. Signatures + +```go +type TokenProof struct { + Ver uint + Jti string +} + +func tokenProofOf(c *gin.Context) service.TokenProof +func (s *AuthService) ensureTokenCurrentTx( + tx *gorm.DB, user *model.User, proof TokenProof, +) error +func (s *AuthService) RenewToken( + ctx context.Context, username, oldToken string, meta SessionMeta, +) (string, time.Time, error) +``` + +持久化契约使用 `user_sessions.token_id` 表示逻辑会话;敏感换发不改变该 JTI。 + +### 3. Contracts + +- `RequireAuth` 必须把本次请求实际观察到的 `TokenVersion + JTI` 写入 Context, + Handler 不得事后只按用户名读取“最新版本”代替 proof。 +- 敏感事务、`RevokeSession` 与 Logout 的数据库路径统一先锁用户行,再检查/更新 + 会话行;持锁后调用 `ensureTokenCurrentTx`。 +- proof 通过后,认证因子写入、版本递增及事务内换发必须同成同败。需要在 Handler + 末尾换发的旧接口,`RenewToken` 也须重新锁用户并确认当前版本恰为 + `oldClaims.Ver + 1`。 +- 换发沿用旧 JWT 的合法 JTI;有旧会话行时只接续未撤销行,存在但已撤销或更新 + 失败必须关闭。兼容期无行 Token 可建行,但仍沿用旧 JTI,因此 Logout 的 + tombstone 能同时撤销换发前后的 JWT。 +- Logout 持久撤销须按 JTI 会话行解析稳定的 `user_id`,不得只按旧 JWT 的可变 + username/subject 找用户;JTI tombstone 至少保留“旧 JWT 剩余寿命 + + 一个完整 `tokenTTL`”,兜住 legacy 无行换发、改名提交和持久化撤销之间的 + 窄窗口。 +- 外部身份登录须在用户锁内重查身份仍绑定;OAuth 登录还须在最终签发事务内重查 + Provider 当前可用。 +- proof 不二次检查自然到期:请求在 JWT 未过期时通过中间件即取得本次请求的认证 + 边界;不得为此额外引入长事务、分布式锁或持久状态机。 + +### 4. Validation & Error Matrix + +| 条件 | 结果 | +| --- | --- | +| `user.TokenVersion != proof.Ver` | `ErrTokenStale` → HTTP 401 | +| JTI tombstone 命中或会话行已撤销 | `ErrTokenStale` → HTTP 401 | +| 旧会话行存在但接续命中 0 行 | 失败关闭,不得 fallback 创建 | +| 已验签当前版本的兼容 Token 确无会话行 | 沿用原 JTI 创建会话行 | +| 改名后用换发前 JWT Logout | 按 JTI/user_id 撤销新旧 JWT;旧 subject 不参与归属 | +| Handler 分段换发时版本不是 `oldVer + 1` | `ErrTokenStale`,前端重新登录 | +| 登录签发前身份已解绑 / Provider 已不可用 | 拒绝登录,不签发 JWT | + +### 5. Good / Base / Bad Cases + +- Good:TOTP 激活事务内依次锁用户、验 proof、写因子、递增版本、沿用 JTI 换发。 +- Base:无会话行的存量 JWT 可完成一次合法变更,但换发后的 JWT 与旧 JWT 共用 + JTI,后到 Logout 可同时撤销。 +- Bad:proof 普通查询通过后释放锁,定点撤销完成,再把旧行更新失败当“无行”创建 + 新会话;这会复活已撤销 Token。 + +### 6. Tests Required + +- TOTP 激活 × 撤销全部:旧 proof 返回 `ErrTokenStale`,因子与版本均不改变。 +- proof check → 定点撤销 → renew:撤销后的行不得被新建替代。 +- recorded 与 legacy 无行会话各测一次“改名后 old-token Logout”:换发 JWT + 必须随同失效;recorded 行写入 `revoked_at`,legacy tombstone 覆盖 fresh expiry。 +- OAuth/钱包登录在最终事务前删除身份:不得签发会话;OAuth 禁用同理。 +- Security/OAuth PATCH 使用已撤销 proof:返回 `ErrTokenStale` 且设置不落库。 + +### 7. Wrong vs Correct + +```go +// Wrong:只按最新用户状态改因子,随后无条件签最新版本 JWT。 +saveFactor() +bumpTokenVersion() +return IssueToken(ctx, username) + +// Correct:请求 proof、因子写入、版本递增和会话接续共享事务与用户锁。 +return db.Transaction(func(tx *gorm.DB) error { + user, err := lockUserForAuthChange(tx, username) + if err != nil { return err } + if err := auth.ensureTokenCurrentTx(tx, user, proof); err != nil { return err } + return changeFactorAndRenewTx(tx, user, oldToken) +}) +``` + +## 其他约定 + +- 面板地址依赖:RP ID / 回调 / SIWE domain 均派生自 `settings.EffectiveAppURL()`,为空时返回引导错误(文案对齐 `ErrOAuthNoAppURL`),不得回退猜测。 +- 登录失败对外文案统一、不透出内部细节(防探测);错误变量集中定义在对应 service 文件头部。 +- 新端点全量 swagger 中文注释并重新生成 docs/;providers 公开端点(`/auth/oauth/providers`)按需追加 `Login` 可用性布尔,供登录页渲染入口。 diff --git a/.trellis/spec/backend/database-guidelines.md b/.trellis/spec/backend/database-guidelines.md index 306afec..456856c 100644 --- a/.trellis/spec/backend/database-guidelines.md +++ b/.trellis/spec/backend/database-guidelines.md @@ -60,3 +60,7 @@ db.Model(&model.AiChannel{}). **Fix**:键值加入 `cred.CompartmentID`(空 = 租户根,天然区分)。 **Prevention**:缓存键必须覆盖影响回源结果的**全部**输入维度(租户、区间、区域、过滤参数);给 Credentials/查询结构体新增会改变结果的字段时,同步检查 `ckey` 调用点;隔离行为写进 `cached_test.go` 的 isolation 用例。 + +## GORM 列名与 map 更新 + +- GORM 默认命名把连续大写按公共缩写拆分:`UserOCID` → `user_oc_id`、`TenancyOCID` → `tenancy_oc_id`(不是 `user_ocid`)。用 `Updates(map[string]any{...})` 写裸列名前先确认真实列名(`schema.Parse` 或查建表 SQL),SQLite 下写错列名报 "no such column" 才暴露,MySQL/PG 同错。零值字段(如清空 `passphrase_enc`)必须走 map 更新,struct 更新会忽略零值。(2026-07-23 activate-api-key 切换签名用户) diff --git a/.trellis/spec/backend/index.md b/.trellis/spec/backend/index.md index a83f68d..efba5e6 100644 --- a/.trellis/spec/backend/index.md +++ b/.trellis/spec/backend/index.md @@ -22,6 +22,7 @@ Gin + GORM + SQLite(纯 Go 驱动 `glebarez/sqlite`,免 CGO;默认与推荐)+ AE | [IdP Icon Upload](./idp-icon-upload.md) | IdP 公共图标上传、校验与清理契约 | 已填 | | [Database Guidelines](./database-guidelines.md) | GORM 实战约定与常见坑 | 已填 | | [OCI Audit](./oci-audit.md) | 审计事件双通道数据源、检索语义与预算纪律 | 已填 | +| [Auth Login Methods](./auth-login-methods.md) | 登录方式接入契约(守卫/留痕/令牌版本/不变量/一次性挑战) | 已填 | --- diff --git a/.trellis/spec/backend/oci-audit.md b/.trellis/spec/backend/oci-audit.md index 30d0877..e0082c0 100644 --- a/.trellis/spec/backend/oci-audit.md +++ b/.trellis/spec/backend/oci-audit.md @@ -20,7 +20,7 @@ ## 日志回传链路(logrelay)资源命名与描述纪律 -- **命名派生**:Topic 前缀、IAM Policy 名、SCH Connector DisplayName 一律由 `relayResourceNames(tenancyOCID)`(见 `internal/oci/logrelay_names.go`)基于 `SHA-256(tenancyOCID)[:4]` 派生 `<8hex>-audit` / `<8hex>-audit-p`。**不得在 `logrelay.go` 里再引入品牌明文**(`oci-portal` / `ociportal` / `logs` 等),否则跨租户恒定字面量会成为 Oracle 内部风控识别「共用同一套 oci-portal」的强指纹;背景与证据分档见调研档案 `docs/oci-sdk-caller-fingerprint.md`。 +- **命名派生**:Topic 前缀、IAM Policy 名、SCH Connector DisplayName 一律由 `relayResourceNames(tenancyOCID)`(见 `internal/oci/logrelay_names.go`)基于 `SHA-256(tenancyOCID)[:4]` 派生 `<8hex>-audit` / `<8hex>-audit-p`。**不得在 `logrelay.go` 里再引入品牌明文**(`oci-portal` / `ociportal` / `logs` 等),否则跨租户恒定字面量会成为 Oracle 内部风控识别「共用同一套 oci-portal」的强指纹;背景与证据分档见工作区任务归档 `.trellis/tasks/archive/2026-07/07-22-research-oci-caller-info/research/oci-sdk-caller-fingerprint.md`。 - **描述文本**:Topic 与 Policy 的 `Description` 用集中常量 `relayTopicDescNew` / `relayPolicyDescNew`(中性英文),不允许写 `oci-portal 日志回传:...` 等品牌/中文明文;SCH Connector 干脆不设 `Description`,避免恒定文案。 - **向后兼容 legacy 命名**:`findRelayTopic` / `findRelayPolicy` / `findRelayConnector` 支持传入多前缀/多名称,顺序为「新命名 → legacy 命名」;新增查找需求要沿用变参签名,不要单独硬编码 legacy 常量到业务函数里。命中 legacy 资源时,`refreshRelayTopicDesc` / `refreshRelayPolicyDesc` 会尽力刷新描述为中性文案,失败不阻塞主流程(权限/服务限流等场景直接忽略)。 - **测试**:命名派生、legacy 常量值、描述中性性由 `logrelay_names_test.go` table-driven 锁定;若必须调整 legacy 常量,先评估是否会让存量租户找不到既有资源导致重复创建。 diff --git a/.trellis/workflow.md b/.trellis/workflow.md index 7775a93..bf3d1c1 100644 --- a/.trellis/workflow.md +++ b/.trellis/workflow.md @@ -183,7 +183,7 @@ Complex task: ask the user if you can create a Trellis task and enter the planni - 1.0 Create task `[required · once]` (only after task-creation consent) - 1.1 Requirement exploration `[required · repeatable]` (`prd.md`; complex tasks also need `design.md` + `implement.md`) - 1.2 Research `[optional · repeatable]` -- 1.3 Configure context `[required · once]` — Claude Code, Cursor, OpenCode, Codex, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix (sub-agent-dispatch platforms only; inline platforms skip) +- 1.3 Configure context `[required · once]` — Claude Code, Cursor, OpenCode, Codex, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Grok, Kimi Code (sub-agent-dispatch platforms only; inline platforms skip) - 1.4 Activate task `[required · once]` (review gate, then `task.py start`; status → in_progress) - 1.5 Completion criteria @@ -220,7 +220,7 @@ Inline mode: skip jsonl curation; Phase 2 reads artifacts/specs via `trellis-bef therefore must cover every required step from implementation through commit, including Phase 3.3 spec update and Phase 3.4 commit. --> -Sub-agent dispatch protocol applies to all platforms and all sub-agents, including class-2 Codex/Gemini/Qoder/Copilot/ZCode/Reasonix/Trae and `trellis-research`: every dispatch prompt starts with `Active task: ` before role-specific instructions. +Sub-agent dispatch protocol applies to all platforms and all sub-agents, including native Codex `SubagentStart` context injection with child-side pull fallback, class-2 Gemini/Qoder/Copilot/Reasonix/Trae/Grok/Kimi Code, hook-backed ZCode/Snow, and `trellis-research`: every dispatch prompt starts with `Active task: ` before role-specific instructions. On Grok Build, use `spawn_subagent` with `subagent_type` set to the Trellis agent name (e.g. `trellis-implement`). On Kimi Code, dispatch the built-in `coder` / `explore` sub-agent with the matching `.kimi-code/skills/trellis-/SKILL.md` instructions. [workflow-state:in_progress] Tools: `trellis-implement` / `trellis-research` are sub-agent types only (Task/Agent tool, NOT Skill; there is no skill by these names). `trellis-update-spec` is a skill. `trellis-check` exists as both; prefer the Agent form when verifying after code changes. @@ -272,13 +272,13 @@ Code committed. Run `/trellis:finish-work`; if dirty, return to Phase 3.4 first. When a user request matches one of these intents inside an active task, route first, then load the detailed phase step if needed. -[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] - Planning or unclear requirements -> `trellis-brainstorm`. - `in_progress` implementation/check -> dispatch `trellis-implement` / `trellis-check`. - Repeated debugging -> `trellis-break-loop`; spec updates -> `trellis-update-spec`. -[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] [codex-inline, Kilo, Antigravity, Devin] @@ -353,7 +353,7 @@ Return to this step whenever requirements change and revise the relevant artifac Research can happen at any time during requirement exploration. It isn't limited to local code — you can use any available tool (MCP servers, skills, web search, etc.) to look up external information, including third-party library docs, industry practices, API references, etc. -[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] Spawn the research sub-agent: @@ -361,11 +361,11 @@ Spawn the research sub-agent: - **Task description**: Research - **Key requirement**: Research output MUST be persisted to `{TASK_DIR}/research/` -[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] [codex-inline, Kilo, Antigravity, Devin] -Do the research in the main session directly and write findings into `{TASK_DIR}/research/`. (For `codex-inline` this avoids the `fork_turns="none"` isolation that prevents `trellis-research` sub-agents from resolving the active task path.) +Do the research in the main session directly and write findings into `{TASK_DIR}/research/`. `codex-inline` is the explicit mode that keeps work in the main session. [/codex-inline, Kilo, Antigravity, Devin] @@ -380,7 +380,7 @@ Brainstorm and research can interleave freely — pause to research a technical #### 1.3 Configure context `[required · once]` -[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] Curate `implement.jsonl` and `check.jsonl` so the Phase 2 sub-agents get the right spec/research context. These files were seeded on `task create` with a single self-describing `_example` line; your job here is to fill in real entries. @@ -425,7 +425,7 @@ Ready gate: both `implement.jsonl` and `check.jsonl` must contain at least one r Skip this step only when both files already have real curated entries. -[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] [codex-inline, Kilo, Antigravity, Devin] @@ -458,11 +458,11 @@ If `task.py start` errors with a session-identity message (no context key from h | `design.md` exists (complex tasks) | ✅ | | `implement.md` exists (complex tasks) | ✅ | -[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] | `implement.jsonl` and `check.jsonl` each contain at least one real curated entry (seed row does not count) | ✅ | -[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] --- @@ -472,21 +472,22 @@ Goal: turn reviewed planning artifacts into code that passes quality checks. #### 2.1 Implement `[required · repeatable]` -[Claude Code, Cursor, OpenCode, CodeBuddy, Droid, Pi, Oh My Pi] +[Claude Code, Cursor, OpenCode, codex-sub-agent, CodeBuddy, Droid, Pi, ZCode, Snow, Oh My Pi] Spawn the implement sub-agent: - **Agent type**: `trellis-implement` - **Task description**: Implement the reviewed task artifacts, consulting materials under `{TASK_DIR}/research/`; finish by running project lint and type-check -- **Dispatch prompt guard**: Tell the spawned agent it is already the `trellis-implement` sub-agent and must implement directly, not spawn another `trellis-implement` / `trellis-check`. +- **Dispatch prompt guard**: The prompt MUST start with `Active task: `, then tell the spawned agent it is already the `trellis-implement` sub-agent and must implement directly, not spawn another `trellis-implement` / `trellis-check`. The platform hook/plugin auto-handles: - Reads `implement.jsonl` and injects referenced spec/research files into the agent prompt - Injects `prd.md`, `design.md` if present, and `implement.md` if present +- For Codex, `SubagentStart` supplies native context injection; the agent profile keeps child-side loading as the fallback -[/Claude Code, Cursor, OpenCode, CodeBuddy, Droid, Pi, Oh My Pi] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, CodeBuddy, Droid, Pi, ZCode, Snow, Oh My Pi] -[codex-sub-agent, Gemini, Qoder, Copilot, ZCode, Reasonix, Trae] +[Gemini, Qoder, Copilot, Reasonix, Trae, Grok, Kimi Code] Spawn the implement sub-agent: @@ -498,7 +499,7 @@ The pull-based sub-agent definition auto-handles the context load requirement: - Resolves the active task with `task.py current --source`, then reads `prd.md`, `design.md` if present, and `implement.md` if present - Reads `implement.jsonl` and requires the agent to load each referenced spec/research file before coding -[/codex-sub-agent, Gemini, Qoder, Copilot, ZCode, Reasonix, Trae] +[/Gemini, Qoder, Copilot, Reasonix, Trae, Grok, Kimi Code] [Kiro] @@ -526,13 +527,13 @@ The platform prelude auto-handles the context load requirement: #### 2.2 Quality check `[required · repeatable]` -[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] Spawn the check sub-agent: - **Agent type**: `trellis-check` - **Task description**: Review all code changes against specs and task artifacts; fix any findings directly; ensure lint and type-check pass -- **Dispatch prompt guard**: Tell the spawned agent it is already the `trellis-check` sub-agent and must review/fix directly, not spawn another `trellis-check` / `trellis-implement`. +- **Dispatch prompt guard**: The prompt MUST start with `Active task: `, then tell the spawned agent it is already the `trellis-check` sub-agent and must review/fix directly, not spawn another `trellis-check` / `trellis-implement`. The check agent's job: - Review code changes against specs @@ -540,7 +541,7 @@ The check agent's job: - Auto-fix issues it finds - Run lint and typecheck to verify -[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Reasonix, Trae] +[/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] [codex-inline, Kilo, Antigravity, Devin] diff --git a/CHANGELOG.md b/CHANGELOG.md index 683c417..c6894ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)(版本段不记日期),版本号遵循语义化版本。 +## [0.8.3] + +### Added + +- 新增通行密钥(Passkey / WebAuthn)注册、管理与免用户名登录,要求验证器完成用户验证;新增 Web3 钱包绑定与 EIP-4361 签名登录,支持浏览器注入的 EOA 钱包 +- 新增活跃会话管理:按登录方式、客户端 IP、User-Agent 与最近活跃时间查看当前账号会话,可定点撤销其他会话;敏感变更换发令牌时接续原会话,升级前签发的存量令牌保持兼容 + +### Changed + +- 租户 API Key 激活接口支持切换到租户内其他 IAM 用户,验证新密钥可用后同步更新面板签名用户与凭据 +- 禁用密码登录时将通行密钥计入可用免密方式,并阻止删除最后一种可用登录方式,避免账号自锁 + +### Fixed + +- 同一实例的终止与电源操作增加飞行期互斥,重复请求稳定返回 409,避免并发执行相互冲突的生命周期操作 + ## [0.8.2] ### Added diff --git a/DASH_VERSION b/DASH_VERSION index c84521b..e18f845 100644 --- a/DASH_VERSION +++ b/DASH_VERSION @@ -1 +1 @@ -v0.8.2 +v0.8.3 diff --git a/cmd/server/main.go b/cmd/server/main.go index 4316869..bf16186 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -31,7 +31,7 @@ import ( ) // @title OCI Portal API -// @version 0.8.2 +// @version 0.8.3 // @description 自托管 OCI 多租户管理面板 API。业务接口用 JWT(Bearer);AI 网关端点(/ai/v1/*)用独立网关密钥(Authorization: Bearer 或 x-api-key)。 // @BasePath / // @securityDefinitions.apikey BearerAuth diff --git a/docs/docs.go b/docs/docs.go index 6e1252a..b07f999 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -12723,7 +12723,7 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "0.8.2", + Version: "0.8.3", Host: "", BasePath: "/", Schemes: []string{}, diff --git a/docs/swagger.json b/docs/swagger.json index 2527204..12ab00b 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -4,7 +4,7 @@ "description": "自托管 OCI 多租户管理面板 API。业务接口用 JWT(Bearer);AI 网关端点(/ai/v1/*)用独立网关密钥(Authorization: Bearer 或 x-api-key)。", "title": "OCI Portal API", "contact": {}, - "version": "0.8.2" + "version": "0.8.3" }, "basePath": "/", "paths": { @@ -12712,4 +12712,4 @@ "in": "header" } } -} \ No newline at end of file +} diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b9c72ac..703e935 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -3146,7 +3146,7 @@ info: description: '自托管 OCI 多租户管理面板 API。业务接口用 JWT(Bearer);AI 网关端点(/ai/v1/*)用独立网关密钥(Authorization: Bearer 或 x-api-key)。' title: OCI Portal API - version: 0.8.2 + version: 0.8.3 paths: /ai/v1/audio/speech: post: