feat: add chat model config RBAC resource - #27957
Conversation
Docs previewCheck off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here. |
|
@codex review |
|
/coder-agents-review |
|
Chat: Review posted | View chat Review history
deep-review v0.9.0 | Round 4 | Last posted: Round 4, 36 findings (1 P1, 6 P2, 20 P3, 6 Nit, 3 Note), COMMENT. Review Finding inventoryFinding inventory, PR #27957Findings
Contested and acknowledgedCRF-6 (P3, coderd/rbac/policy/policy.go:88) - write actions published but inert
CRF-27 (Note, coderd/database/migrations/000567_chat_model_config_scopes.up.sql:1) - five of six irreversible enum values speculative
CRF-6 panel closure (R2, 11/11 accept)
CRF-27 panel closure (R2, 11/11 accept)
Cross-check notes
Round logRound 1Netero pass: 2 P3 (CRF-1, CRF-2). Panel of 21 (19 trigger-matched + 2 wildcards: Takumi, Melody). New: 1 P1, 5 P2, 12 P3, 5 Nit, 2 Note. Takumi: no findings. Reviewed against 9f6d499..6c26421. Round 2Churn guard: PROCEED. 25 addressed (author claims, pending panel verification), 2 contested (CRF-6, CRF-27), 0 silent. Reviewed against 483b499..ee602bd. Round 2 panelPanel of 12 (11 trigger-matched + wildcard Kite; Netero advisory first). All 25 addressed findings verified fixed by at least one reviewer (tests run against Postgres). CRF-2 re-raised (owner-matcher compile case missing). CRF-6 and CRF-27 closed 11/11 on author defense. New: 1 P2, 6 P3, 1 Nit, 1 Note (CRF-28 through CRF-36). Komugi: no findings. Mafu-san note: CRF-16 fix leaves emitted SQL tagged "-- name: GetChatModelConfigs" (residue only, method removed at M3, not posted as finding). Round 3BLOCKED by churn guard. Silent: CRF-35 (body-folded Nit, no response) and CRF-2 re-raise (no reply, owner-matcher compile case still absent at head). Author replies for CRF-28 and CRF-30 claim fixes the head contradicts (RBACObject still present; GetDefaultChatModelConfig comment byte-identical); CRF-31 and CRF-32 fixed at some sites, unchanged at the anchor sites the findings targeted. PR restructured onto stack base #27956 (migrations renumbered to 000571/000572); CI failing (test-go-pg, test-go-pg-17, test-go-race-pg, required); Codex flagged migration renumbering as upgrade-breaking. No reviewers spawned. Reviewed against d0f5fa0..722e64a. Round 4BLOCKED by churn guard, solely on CRF-35 (Nit, silent second consecutive round; the finding has never had a reviewable thread because its line is outside every diff). All other open findings verified fixed in 91eeb0b: CRF-2 (ChatModelConfigNoOwner compile case), CRF-28 (RBACObject deleted), CRF-30, CRF-31, CRF-32. CI green at head 6c72e7d; migration renumbering resolved (scopes migration now 000575). No reviewers spawned. Reviewed against 038921b..6c72e7d. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
This PR gets the hard parts of the read cutover right, and the panel said so repeatedly: the GetDefaultChatModelConfigCandidates split prevents a silent no-default commit that an authorized-filter candidate list would have shipped (five reviewers traced it, and the write-window test pins the rollback semantics against real Postgres), the converter mirrors the established WorkspaceConverter shape, the migration follows the enum-add precedent, and every interim write gate carries a ticketed TODO. Takumi ran the concurrency trace and found nothing: "The timing is fine."
Severity count: 1 P1, 5 P2, 14 P3, 5 Nit, 2 Note.
The P1 is the headline: six reviewers converged independently, three with empirical probes against Postgres, on org admins and org auditors of non-default organizations regressing from the full enabled model list to an empty list for the whole fallback window, while plain members of the same org keep the full list. The PR description discloses the custom-site-role regression but not this one, and this one hits built-in roles on every multi-org deployment through the UI model picker.
Two P2s are description/comment honesty: the PR body attributes member read to the seeded org ACL when the code preserves it via the chatd subject grant, and the create-gate comment justifies itself with a dbauthz create-in-org check that does not exist in this PR. One P2 is a test fixture that silently inverts (Enabled: false is dbgen's zero value), turning the centerpiece disabled-visibility assertions into no-ops.
Process observations: CI shows "Pixel / Review" failing on this head. The only frontend changes are generated files, so the diff is an unlikely cause, but a failing check is not "pre-existing"; resolve it or state why it is out of scope before merge. Separately, Gon audited all 33 new comments and found a pattern: several were written for the PR's development narrative (plan milestones, revert history) rather than for the next reader of the file; the individual findings are inline.
Fun quote, from Hisoka: "I pulled the isAdmin thread and something on the other end moved. Shall I show you?"
coderd/exp_chats.go:6551
P2 [CRF-4] The PR description justifies the accepted regression with a mechanism the code does not implement: "members read through the org ACL seeded in the schema PR." (Mafu-san P2, Pariston Note)
Mafu-san traced every member-subject read path: "The list handler routes non-admins to GetEnabledChatModelConfigs(dbauthz.AsChatd(ctx)), so members read because the chatd service subject gained site-level chat_model_config:read (dbauthz.go:795), not through any ACL." ChatModelConfig.RBACObject() has zero non-test callers, and an ACL-only grantee can never reach the SQL filter because the AnyOrganization gate evaluates a bare object with empty ACL lists.
Consequence: the human accepting the tradeoff is told member read is preserved by the seeded ACL. If M3 removes or narrows the chatd grant on that assumption, member read breaks silently. Pariston adds the regression itself is overstated: custom deployment_config:read roles fall to the else branch and still see every enabled config; what they lose is the management view (disabled rows), not "the model list."
Fix: correct the PR description to name the chatd-subject path as the member read mechanism and to scope the stated loss to the management view.
🤖
coderd/exp_chats.go:6551
P3 [CRF-14] The non-admin branch is the only interim authorization site in this PR without a cutover TODO. (Kurapika P3; Hisoka, Mafuuu, Meruem Notes)
Kurapika: "Every authenticated user of any org therefore enumerates every org's enabled configs (model names, provider IDs, context limits, reasoning options), and the org ACLs the schema PR seeded decide nothing on this path. ... After the M3 cutover, when configs are per-org, this branch becomes a standing cross-org disclosure unless someone remembers to scope it. Every other interim decision in this PR carries TODO(mafredri): ... after CODAGT-709 M3; this branch carries only a nolint:gocritic comment."
Three other reviewers flagged the same asymmetry: org-scoped readers are SQL-filtered to their orgs while every plain member sees all orgs' enabled configs. Fine during the window; untagged interim behavior is the kind that survives its window. Add the M3 TODO stating the branch must switch to an org-scoped query at cutover.
🤖
🤖 This review was automatically generated with Coder Agents.
6c26421 to
2026b3f
Compare
2026b3f to
ee602bd
Compare
|
@codex review |
|
/coder-agents-review |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Round 2 is what a fix round should look like. All 25 addressed findings were verified against the code by the panel, most by running the tests against Postgres: the P1 access regression is gone (site-scoped gate, non-default-org roles pinned by new test cases), the fixture actually disables what it claims, the custom-role fixtures use a shape production can hold, and the comment surgery from round 1 landed. Mafu-san's process read: corrections were generalized, not spot-patched (the bare require.Error became a shared helper across all three rollback subtests; the fixture fix came with enabled-flag pins on all three fixtures).
Both contested findings are closed on the author's defense, 11/11. CRF-6 (write actions published but inert): fail-closed, ticketed window, and the panel found the defense stronger than stated: the scope constants are generated from the policy action set and the enum must match, so trimming to read-only would force a second irreversible enum migration at cutover. CRF-27 inherits that decision through the same generation chain; Pariston, its raiser, withdrew it. Related observation from Ryosuke, recorded for the M3 checklist rather than re-raised: the org auditor's new read grant also has no consuming path until cutover, same forward-declaration class.
New this round: 1 P2, 6 P3, 1 Nit, 1 Note, plus one re-raise. The P2 and one P3 are the same disease CRF-10 had, in new locations: authorization comments justifying checks with a "default-org fallback" serving mechanism that this PR's own base commit (483b499) deleted. The largest convergence (five reviewers) is the 500-for-authz-denial contract that the new test helper now pins. The re-raise: the converter compile tests cover four of five matchers; the owner matcher (AlwaysFalse) has no case.
Small residue, not findings: the CRF-16 fix makes the Prometheus label fire, but the emitted SQL still carries "-- name: GetChatModelConfigs", so slow-query-log correlation attributes candidates traffic to the wrong name; the whole method is TODO-tagged for M3 removal. Leorio also notes the fix commit body is empty for a 289/235-line change treating 25 findings; the PR description carries the durable record, so Note weight only.
Fun quote, from Hisoka: "I came back for a rematch and the author rebuilt the arena."
coderd/x/chatd/chatd.go:1538
Nit [CRF-35] The chatdModelConfigLookupContext comment still says the elevation exists for "daemon-scoped deployment-config reads," but this PR changed GetEnabledChatModelConfigByID to authorize chat_model_config:read. (Mafuuu)
"Outside the diff, but the diff falsified it: dbauthz.go:3740 now checks rbac.ResourceChatModelConfig, and the chatd subject's matching grant is the new chat_model_config:read at dbauthz.go:795, not its deployment-config read. Stale permission comments are exactly the drift class CRF-12/CRF-26 cleaned up elsewhere in this PR."
🤖
🤖 This review was automatically generated with Coder Agents.
ee602bd to
3adca40
Compare
f395ff2 to
a04ca61
Compare
a04ca61 to
7131db5
Compare
7131db5 to
c440fe0
Compare
c440fe0 to
45729fd
Compare
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Round 4 is blocked on a single finding, and it is the last open item on this PR.
Everything else from rounds 1 through 3 is now verified fixed at head 6c72e7d (commit 91eeb0b): the CRF-2 re-raise (the ChatModelConfigNoOwner compile case landed, exactly the one table row asked for), CRF-28 (ChatModelConfig.RBACObject() deleted), CRF-30 (the GetDefaultChatModelConfig comment now cites the real safety argument, the org-scoped chat authorization), CRF-31 (the stale fallback sentence is gone from the dbauthz anchor), and CRF-32 (all five comment sites trimmed). CI is green and the migration renumbering concern is resolved (the scopes migration now takes a fresh number, 000575).
The one open finding, silent for two consecutive rounds:
[CRF-35] (Nit) chatdModelConfigLookupContext at coderd/x/chatd/chatd.go:1596-1599: the nolint comment still says "Chat message admission needs daemon-scoped deployment-config reads for model config validation." The check it feeds (GetEnabledChatModelConfigByID, dbauthz.go:3750) has authorized chat_model_config:read since round 2, and the chatd subject's matching grant is chat_model_config:read, not its deployment-config read. Concrete fix, a one-line comment edit: "Chat message admission needs daemon-scoped chat_model_config reads for model config validation."
To be fair about why this keeps slipping: the line sits outside every diff in this PR, so the finding has been folded into review bodies since round 2 and has never appeared as a reviewable thread. It still needs one of: the one-line fix above, a filed ticket, or a stated reason it should not change. A bare pass-over cannot close it.
Per the review process, no panel runs on a blocked round. Respond to or fix CRF-35 and the next round proceeds to a normal panel review.
🤖 This review was automatically generated with Coder Agents.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c72e7d184
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
6c72e7d to
e022ab1
Compare
e022ab1 to
b6656a4
Compare
b6656a4 to
58e4333
Compare
58e4333 to
771557b
Compare
771557b to
fe8c02b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe8c02b8f8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return scopes.Has(database.ApiKeyScopeCoderAll) || | ||
| scopes.Has(database.ApiKeyScopeChatModelConfigRead) |
There was a problem hiding this comment.
Honor wildcard model-config scopes
When an internally minted or restored API key carries the valid chat_model_config:* scope, this exact-membership check returns false and the list endpoint responds with 403, even though RBAC expands that wildcard to include read. Include ApiKeyScopeChatModelConfig here, or use the scope-only RBAC authorization pattern used by the MCP config endpoint so wildcard and future covering scopes retain their documented semantics.
Useful? React with 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
fe8c02b to
d945749
Compare
d945749 to
618f3f9
Compare
Rename the admin-managed SDK resource from `ChatModelConfig` to `ChatModel`. Rename catalog entries from `ChatModel` to `ChatModelCatalogEntry` and replace `ChatModelsResponse` with `ChatModelAvailabilityResponse`. API routes and JSON fields do not change. Experimental SDK and OpenAPI schema names do change. Depends on coder#27957 _This pull request description was generated by Coder Agents._
Add the `chat_model_config` RBAC resource, actions, and API key scopes. This layer includes the scope migration and generated API types. Model list queries apply token scopes, SQL authorization filters, and row ACLs. Write and default-management handlers keep deployment configuration authorization until the next API layer replaces it. Depends on #27956 _This pull request description was generated by Coder Agents._
Rename the admin-managed SDK resource from `ChatModelConfig` to `ChatModel`. Rename catalog entries from `ChatModel` to `ChatModelCatalogEntry` and replace `ChatModelsResponse` with `ChatModelAvailabilityResponse`. API routes and JSON fields do not change. Experimental SDK and OpenAPI schema names do change. Depends on #27957 _This pull request description was generated by Coder Agents._
Add the
chat_model_configRBAC resource, actions, and API key scopes. This layer includes the scope migration and generated API types. Model list queries apply token scopes, SQL authorization filters, and row ACLs.Write and default-management handlers keep deployment configuration authorization until the next API layer replaces it.
Depends on #27956
This pull request description was generated by Coder Agents.