Skip to content

feat: add owner_id to CreateChatRequest to create chats for another user - #29581

Merged
ibetitsmike merged 9 commits into
mainfrom
mike/chat-create-for-user
Sep 21, 2026
Merged

ibetitsmike merged 9 commits into
mainfrom
mike/chat-create-for-user

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Owners and owner-level service accounts can now start a Coder Agents chat on behalf of another user by setting owner_id on POST /api/v2/chats. The field is optional and defaults to the caller, so existing clients are unaffected. A delegated owner must be an active, non-system member of the requested organization; anything else returns 400 Chat owner must be an active member of the organization. once the caller has passed the authorization gates below.

A chat runs entirely with its owner's credentials (synthetic API key, workspace access, OIDC and provider tokens), so creating one for someone else is acting as that user. On top of org-scoped chat:create for the owner, the caller must hold the authority to create API keys for that user, which is what the token endpoint demands to mint their session. Org admins therefore cannot use it; site owners and owner-role service accounts can. For the same reason the workspace binding, model config, and MCP server IDs are validated as the owner rather than the caller, and the owner must themselves hold chat:create (organization service accounts do not). The initial user message is attributed to the caller through chatd.CreateOptions.CreatedBy.

An earlier revision of this PR exposed the same behavior as POST /api/v2/users/{user}/chats; that route and codersdk.Client.CreateUserChat were removed in 62a8be3dd32 in favor of the request field. An explicit zero owner_id is rejected with 400 rather than silently defaulting to the caller. Coverage lives in TestPostChats_OwnerID.

Review record (path-based revision, gates unchanged)
  • Two-axis code review (Standards, Spec) plus a simplicity audit ran as Xum sub-agents on 1f3189898f0. The Spec axis found the workspace binding was authorized as the caller, fixed in 363d45281d8 with red-green coverage in WorkspaceNotAccessibleToOwner. The additional API-key gate and the CreatedBy attribution go beyond the workspaces pattern and were kept deliberately for the reasons above.
  • Codex review on 363d45281d8: one P2, suspended, dormant, or system users could be targeted and would get a chat that can never run because AI Bridge refuses to authorize them. Fixed in c065437e359 with OwnerSuspended (red on the old head); thread replied to and resolved.
  • Codex review on c065437e359: one P2, an explicit or personal-override model config the caller can read but the owner cannot was accepted and chatd would silently fall back to the default model at run time. Fixed in aa88b608182 by resolving the model config and MCP server IDs under the owner's context, with ModelConfigNotAccessibleToOwner (red on the old head); thread replied to and resolved.
  • Codex review on aa88b608182: one P2, an organization service account (which deliberately holds no chat permissions) could be targeted because the chat permission check ran as the caller. Fixed in a6fe8075c30 by also requiring the owner subject to hold chat:create, with OwnerWithoutChatPermission (red on the old head); thread replied to and resolved.
  • Codex review on a6fe8075c30: one P2, the active/non-system check was skipped when the caller could not read the owner's user object. Fixed in 0dd8bb4dd44 by moving the check behind the authorization gates with a system-restricted lookup; thread replied to and resolved. In 62a8be3dd32 that lookup became a single OrganizationMembers query, which also covers membership and excludes system and deleted users.
  • Codex review on 62a8be3dd32 (the owner_id revision): two P2s. An explicit zero owner_id was treated as omitted and created the chat for the caller; fixed in f54b4f33d36 with a 400 and OwnerIDNil (red on the old head). Creation and the read-back still ran as the caller while the preflight ran as the owner; fixed in the same commit by passing ownerCtx to CreateChat, GetChatByID, title generation, and the file-metadata fetch. That second scenario is unreachable today (only the built-in owner role holds api_key:create for others, and site-wide custom roles are refused by both the API and dbauthz), so it has no dedicated red test. Both threads replied to and resolved.
Remote UAT record (path-based revision)

Remote UAT on dogfood (chat d7442df3-693f-4403-805f-2de52f7bdbed, Xum sub-agent as critical runner) tested head 21ed6b389cc, the last path-based revision. Licensed dev server, real Haiku 4.5 model, 77 logged API requests, 23 inspected screenshots. Endorsed verdict: PASS. Site owner creates a chat for member X: X sees it, gets real answers to the admin prompt and to follow-ups, and state survives reload and mid-response refresh. The creating admin gets 403 Only the chat owner may send messages. and a read-only view; an unrelated member gets 404. Org admin, member, user-admin, and template-admin callers get 403; owner-role service account 201. Suspended, dormant, and service-account targets 400; owner-inaccessible workspace, model config, or MCP server 400; 300 KiB body 413. The owner_id revision changes only how the owner is supplied and the non-member response (400 instead of 404); the authorization and eligibility gates it exercised are unchanged and covered by TestPostChats_OwnerID.

Non-blocking findings for the author's judgment, none of them regressions of this PR:

  1. X's UI shows no author on the admin-written first message and offers X "Edit" on it; created_by carries the admin but the frontend does not surface it.
  2. A site owner who is not a member of the organization can still create a chat for its members (201), while a self-owned POST /chats returns 403 for the same organization; same asymmetry as postUserWorkspaces.
  3. The creating admin can archive, rename, and interrupt X's chat although it cannot message it (pre-existing chat authz).
  4. Owner-inaccessible model config or MCP server is reported as "not found or disabled" with no hint that the owner, not the caller, lacks access.
  5. Dormant (never logged in) targets are refused because AI Bridge rejects non-active users; a new hire cannot be given a chat until first login.
  6. The endpoint docs list only 201 and 413.

Xum (an AI agent) acted on behalf of @ibetitsmike for this PR.

Owners and owner-level service accounts can now start a Coder Agents chat
on behalf of another user, following the /users/{user}/workspaces pattern.
The owner is resolved from the path through ExtractOrganizationMembersParam
and must be a member of the requested organization.

Because chat processing runs with the owner's credentials, creating a chat
for someone else requires the same authority as minting that user's API
keys in addition to org-scoped chat:create, so org admins cannot use it.
The initial user message is attributed to the caller.
A chat connects to its workspace as the owner, so the SSH check must run
with the owner's roles rather than the caller's when an administrator
creates a chat for someone else. The owner resolver now returns an error
through httperror instead of a status and response pair.
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Docs preview

Check 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.

@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-21T17:12:53.829698Z f54b4f3 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 363d45281d

ℹ️ 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".

Comment thread coderd/exp_chats.go Outdated
AI Bridge refuses to authorize model calls for suspended, dormant, and
system users, so a chat created on their behalf could never run. Reject
the request up front instead of leaving a broken chat behind.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

normalizedMCPServerIDs, invalidMCPServerIDs, err := validateChatMCPServerIDs(ctx, api.Database, req.OrganizationID, req.MCPServerIDs)

P1 Badge Validate selected MCP servers as the chat owner

When a delegated caller can read an MCP configuration that the target user cannot, this validation runs with the caller's authorization context and accepts the configuration. The ID is then persisted on the member-owned chat, and enabledMCPServerConfigsForChatOrg in coderd/x/chatd/generation_preparer.go deliberately does not recheck ACLs for persisted selections, so the target can use an MCP server they could not select themselves. Validate the requested IDs with ownerCtx so initial selection respects the chat owner's MCP ACL.

ℹ️ 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".

Comment thread coderd/exp_chats.go Outdated
Model configs and MCP servers carry user and group ACLs that chatd
re-evaluates as the chat owner, silently falling back to the default
model or dropping servers the owner cannot read. Validate both under
the owner's context so an inaccessible choice is rejected up front.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa88b60818

ℹ️ 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".

Comment thread coderd/exp_chats.go Outdated
Organization service accounts deliberately hold no chat permissions, yet
a site owner could create a chat owned by one because the creation
checks ran with the caller's authority. Require the owner to be able to
create the chat themselves.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6fe8075c3

ℹ️ 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".

Comment thread coderd/exp_chats.go Outdated
…gates

The active and non-system check depended on the caller being able to
read the owner's user object and ran before authorization. Look the
owner up directly after the caller has proven the authority to act as
them, so the check is unconditional and leaks nothing earlier.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 0dd8bb4dd4

ℹ️ 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".

@ibetitsmike
ibetitsmike marked this pull request as ready for review September 20, 2026 16:27

@kylecarbs kylecarbs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why separate route? It looks like we could just add an OwnerID option to CreateChatRequest and it'd be sufficient?

Drop POST /users/{user}/chats and the CreateUserChat SDK method. The
owner is now an optional owner_id in the existing create request,
defaulting to the caller. The delegated path keeps the same gates: the
caller needs api_key:create for the owner, and the owner must be an
active member of the organization with chat permissions.
@ibetitsmike ibetitsmike changed the title feat: add POST /users/{user}/chats to create chats for another user feat: add owner_id to CreateChatRequest to create chats for another user Sep 21, 2026
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62a8be3dd3

ℹ️ 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".

Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/exp_chats.go
…owner

An explicit zero owner_id was treated as omitted and created the chat
for the caller; it is now a 400. Chat creation and the read-back after
it run under the owner's context like the preflight checks already do,
so a delegated request behaves exactly as if the owner had created the
chat themselves.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: f54b4f33d3

ℹ️ 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".

@ibetitsmike
ibetitsmike merged commit af0ef00 into main Sep 21, 2026
34 of 35 checks passed
@ibetitsmike
ibetitsmike deleted the mike/chat-create-for-user branch September 21, 2026 18:49
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants