Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add /api/v2/aibridge/serve endpoint - #26506

Merged
pawbana merged 7 commits into
mainfrom
pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2
Jun 26, 2026
Merged

feat: add /api/v2/aibridge/serve endpoint#26506
pawbana merged 7 commits into
mainfrom
pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2

Conversation

@pawbana

@pawbana pawbana commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Adds a new enterprise-only GET /api/v2/ai-gateway/serve endpoint that standalone AI Gateway replicas use to connect to coderd over a DRPC-over-WebSocket transport, mirroring the existing in-memory path used by the embedded AI Bridge daemon.

  • The endpoint upgrades the HTTP connection to a WebSocket, multiplexes it with yamux, and finally serves the three DRPC services (Recorder, MCPConfigurator, Authorizer).
  • The X-AI-Governance-Gateway-Key header is used for authentication.
    • The key is looked up by its hashed secret
    • Missing or revoked keys return 401.
  • API version negotiation is enforced via a new aibridged/proto version (v1.0).
    • Incompatible versions return 400.
  • FeatureAIBridge entitlement is required.
  • Key liveness (last_used_at) is recorded immediately on connection and refreshed every 60 seconds while the session remains open.
    • When key liveness detects the key was deleted (no rows where updated) session is closed.

Small refactors

  • The three DRPC service registrations are extracted into aibridgedserver.Register, shared by both the in-memory and WebSocket paths.

  • The literal 256 * 1024 used as the yamux-aligned WebSocket read limit is replaced with the named constant drpcsdk.YamuxDefaultStreamWindowSize in all call sites.

@linear-code

linear-code Bot commented Jun 18, 2026

Copy link
Copy Markdown

AIGOV-308

pawbana commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from aaa5bce to 00065d0 Compare June 23, 2026 10:48
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 441ef79 to 2566351 Compare June 23, 2026 10:48
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 00065d0 to 3a82a8f Compare June 23, 2026 13:46
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 2566351 to 6ba893e Compare June 23, 2026 13:46
@github-actions

Copy link
Copy Markdown
Contributor

Docs preview

📖 View docs preview for docs/reference/api/enterprise.md

@pawbana pawbana changed the title feat(coderd/httpmw): add AI Gateway key auth middleware and proto version feat(enterprise/coderd): add /api/v2/aibridge/serve endpoint Jun 23, 2026
@pawbana

pawbana commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-06-24 17:57 UTC by @pawbana
Spend: $82.19 / $100.00

Review history
  • R1 (2026-06-23): 22 reviewers, 4 Nit, 1 P1, 1 P2, 6 P3, 1 P4, REQUEST_CHANGES. Review
  • R2 (2026-06-24): 10 reviewers, 4 Nit, 1 P1, 2 P2, 10 P3, 1 P4, REQUEST_CHANGES. Review

deep-review v0.9.0 | Round 2 | 65dff84..d1181b3

Last posted: Round 2, 18 findings (1 P1, 2 P2, 10 P3, 1 P4, 4 Nit), REQUEST_CHANGES. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:41 Swagger @Security CoderSessionToken is wrong; endpoint uses gateway key auth R1 Netero Yes
CRF-2 Nit Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:196 Doc comment starts with trackAIGatewayKeyUsage but function is aiGatewayTrackKeyUsage R1 Netero Yes
CRF-3 P1 Author contested; panel re-raised R2 enterprise/coderd/aibridgeserve.go:160 Delegated auth path in IsAuthorized exposed over network boundary, violating SECURITY comment R1 Kurapika Yes
CRF-4 P2 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:198 Active DRPC sessions survive gateway key deletion; liveness tracker discards revocation signal R1 Mafuuu P2, Kurapika P2, Chopper P2, Knov P2, Razor P4 Yes
CRF-5 P3 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:122 SetReadLimit(YamuxDefaultStreamWindowSize) is dead code; WebsocketNetConn overrides to 4MiB R1 Hisoka P2, Mafuuu P3, Meruem P3, Zoro Note, Melody Note Yes
CRF-6 P3 Author fixed (65dff84) coderd/aibridgedserver/register.go:22 Error message says "key validator" but service is Authorizer R1 Gon P3, Leorio P3 Yes
CRF-7 P3 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:177 Missing eager request log write for long-lived WebSocket (provisionerDaemonServe does this) R1 Ryosuke P3, Knov P3 Yes
CRF-8 P3 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:192 Liveness update errors logged at Debug, invisible in default production logging R1 Chopper P3 Yes
CRF-9 P3 Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:97 aiGatewayUpdateKeyLastUsed called with AsAIBridged context; relies on internal AsSystemRestricted escalation R1 Hisoka Yes
CRF-10 P4 Author fixed (65dff84) enterprise/coderd/aibridgeserve_test.go:1 No subtest for absent version parameter (empty string hits different apiversion.Parse error) R1 Bisky Yes
CRF-11 Nit Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:121 Comment "Align with yamux's default stream window size" restates the constant name R1 Gon Yes
CRF-12 Nit Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:192 Log message "update aibridge gateway key last used" inconsistent with user-facing "AI Gateway" naming R1 Leorio Yes
CRF-13 Nit Author fixed (65dff84) enterprise/coderd/aibridgeserve.go:100 WebSocket wait group uses IIFE; three existing call sites use bare Lock/Add/Unlock R1 Robin Yes
CRF-14 Note Dropped by orchestrator (liveness helper uses api.Logger, not connection-scoped logger; operationally meaningful but not actionable as standalone fix) enterprise/coderd/aibridgeserve.go:192 Liveness helper logs to base logger, not connection-scoped logger R1 Kite No
CRF-15 Note Dropped by orchestrator (comment verbosity is consistent with codebase style, not a defect) enterprise/coderd/coderd.go:325 Route comment opens with mechanism the route path already shows R1 Gon No
CRF-16 Note Dropped by orchestrator (comment verbosity is consistent with codebase style, not a defect) coderd/aibridged/proto/version.go:15 CurrentVersion doc restates declaration name R1 Gon No
CRF-17 Note Dropped by orchestrator (comment verbosity is consistent with codebase style, not a defect) coderd/aibridgedserver/register.go:10 Register doc enumerates services the body already shows R1 Gon No
CRF-18 P3 Open enterprise/coderd/aibridgeserve.go:236 consecutiveFailures counter not reset after single failure; leaks state across episodes R2 Netero, Hisoka, Chopper Yes
CRF-19 P2 Open enterprise/coderd/aibridgeserve.go:64 DB error details (err.Error()) leaked to unauthenticated caller on key lookup failure R2 Kurapika Yes
CRF-20 P3 Open coderd/aibridgedserver/aibridgedserver.go:627 Inline comment claims in-process-only trust, contradicts updated SECURITY comment at :596 R2 Chopper P3, Razor P3, Knov P2 Yes
CRF-21 P3 Open enterprise/coderd/aibridgeserve.go:91 Liveness update and goroutine start before version validation; rejected connections create false liveness signals R2 Hisoka P3, Razor P3, Knov P2 Yes
CRF-22 P3 Open enterprise/coderd/aibridgeserve.go:198 aiGatewayUpdateKeyLastUsed returns true (active) on error; key state unknown but reported as valid R2 Mafuuu P3, Knov Nit Yes

Contested and acknowledged

CRF-3 (P1, enterprise/coderd/aibridgeserve.go:160) - Delegated auth path exposed over network boundary

  • Finding: IsAuthorized has a delegated auth path (KeyId-only requests) that skips API key secret validation. The SECURITY comment explicitly warns: "Do not bind this DRPCServer to a network listener." This PR binds the same Server to a WebSocket-accessible mux. Additionally, GetMCPServerAccessTokensBatch accepts a caller-supplied user_id and returns external OAuth tokens with no per-call user authorization.
  • Author defense: "This is known gap and conscious decision. Updated comment to reflect that." No code change to restrict the delegated path, no ticket filed for the deferral.
  • Panel re-raised R2: 3 reviewers (Mafuuu, Pariston, Kurapika) maintain P1. 1 reviewer (Knov) rates P2. Rest acknowledge the gap and flag the missing ticket. Universal agreement: deferral without a tracking ticket is a drop. This needs a human decision: file a ticket or explicitly accept the gap.

Round log

Round 1

Panel. CRF-1 through CRF-13 posted. 1 P1, 1 P2, 5 P3, 1 P4, 3 Nit, 4 dropped Notes. Reviewed against 3a82a8f..3ca7c1b. Panel: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Ging-Go, Gon, Leorio, Ryosuke, Kurapika, Takumi, Chopper, Killua, Kite, Knov, Meruem, Komugi, Robin, Zoro, Razor, Melody, Knuckle. Netero pre-panel.

Round 2

Panel. 12 of 13 R1 findings addressed. CRF-3 contested by author, re-raised by panel. 5 new findings (CRF-18 through CRF-22). 1 P2, 4 P3. Reviewed against 65dff84..d1181b3. Panel: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Kurapika, Chopper, Komugi, Razor, Knov. Netero pre-panel.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@pawbana pawbana changed the title feat(enterprise/coderd): add /api/v2/aibridge/serve endpoint feat: add /api/v2/aibridge/serve endpoint Jun 23, 2026

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The transport architecture is clean: WebSocket, yamux, DRPC mirroring provisionerDaemonServe, with the shared Register extraction eliminating service-set drift. The test suite covers the auth boundary well (5 negative cases + full DRPC round trip). The YamuxDefaultStreamWindowSize constant is a good deduplication.

Severity count: 1 P1, 1 P2, 5 P3, 1 P4, 3 Nit.

The P1 is a security boundary violation. The IsAuthorized DRPC method has a delegated auth path (KeyId-only requests skip secret validation) that was designed exclusively for the in-process MemTransportPipe. The SECURITY comment on that method explicitly says: "Do not bind this DRPCServer to a network listener." This PR registers the same Server on a network-accessible mux via aibridgedserver.Register, exposing the delegated path to any gateway key holder.

The P2 (key revocation not terminating active sessions) is amplified by the P1: a revoked key's session persists with the ability to impersonate users. Five reviewers converged on this independently.

Process note: the commit scope enterprise/coderd is too narrow for a 14-file cross-cutting change spanning coderd/, codersdk/, docs/, enterprise/, and site/. Per project convention, omit the scope for cross-cutting changes.

"The SECURITY comment on IsAuthorized explicitly predicted this attack vector. The comment is the canary, and this PR is the coal mine." - Kurapika

🤖 This review was automatically generated with Coder Agents.

Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread coderd/aibridgedserver/register.go Outdated
Comment thread enterprise/coderd/aibridgeserve_test.go
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go Outdated
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 3a82a8f to 70b1fea Compare June 24, 2026 17:38
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch 2 times, most recently from ea4601e to d1181b3 Compare June 24, 2026 17:44
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 70b1fea to 65dff84 Compare June 24, 2026 17:44
@pawbana

pawbana commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good progress: 12 of 13 R1 findings addressed in a single commit. The CRF-4 fix (key revocation teardown) is well-designed with a controlled-ticker test. The CRF-5 fix (SetReadLimit ordering) correctly resolves a class-of-bug across three call sites. The CRF-8 fix (consecutive failure tracking with log escalation) is a nice touch, though it introduced a bug (CRF-18).

Severity count (new this round): 1 P2, 4 P3. Plus the contested CRF-3 (P1) from R1.

CRF-3 panel vote: 3 reviewers maintain P1 (Mafuuu, Pariston, Kurapika), 1 rates P2 (Knov), rest acknowledge the gap. Universal agreement on one point: deferral without a tracking ticket is a drop. The provisioner daemon analogy understates the risk: provisioner daemons handle infrastructure orchestration, while the AI Bridge path exposes external OAuth tokens for arbitrary users via GetMCPServerAccessTokensBatch. Blocking KeyId-only requests on the network path is a targeted fix with no impact on the in-memory path. This needs a human decision: fix it, file a ticket, or explicitly accept the gap.

"Documentation of a vulnerability is not mitigation." - Pariston


coderd/aibridgedserver/aibridgedserver.go:627

P3 [CRF-20] Inline comment reads: "Trust is established at the in-process transport boundary, not in this RPC." The SECURITY comment at line 596-606 was updated to acknowledge network exposure ("If this RPC is ever exposed over a network boundary... tighten the trust model"). These two comments now contradict each other: one claims in-process-only trust, the other acknowledges network exposure.

Fix: update the inline comment to match the SECURITY block's current state.

(Chopper P3, Razor P3, Knov P2)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go Outdated
defer keyCtxCancel()

// Mark key as used as soon as the request is authenticated.
if _, err := aiGatewayUpdateKeyLastUsed(keyCtx, api, keyID); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3 [CRF-21] aiGatewayUpdateKeyLastUsed runs here before version validation (line 97). A gateway sending an incompatible version gets 400, but the key's last_used_at is already updated. A misconfigured gateway in a connect-fail loop appears "healthy" in the admin dashboard (recent last_used_at) while never establishing a session.

Fix: move the initial liveness update after version validation, or after websocket.Accept.

(Hisoka P3, Razor P3, Knov P2)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My assumption was that last_used should show that given key appeared in request headers regardless connection was successful even with API incompatibility or when WebSocket upgrade failed. This shows that something uses this key, even if unsuccessful. API incompatibility will be visible in response and Gateway logs.
Maybe this is wrong and only successful use should be recorded.

Comment thread enterprise/coderd/aibridgeserve.go Outdated
// nolint:gocritic // Recording AI Gateway key liveness is an internal system write.
rows, err := api.Database.UpdateAIGatewayKeyLastUsedAt(dbauthz.AsSystemRestricted(ctx), keyID)
if err != nil {
return true, err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3 [CRF-22] aiGatewayUpdateKeyLastUsed returns (true, err) on database error. The true means "key is active," but the actual state is unknown. In the ticker loop (line 219), this means a deleted key with a persistent DB error is never detected as deleted, because the active flag stays true and ctxCancel() is never called.

Fix: return (false, err) so unknown state is treated as inactive, or handle the error case separately in the caller.

(Mafuuu P3, Knov Nit)

🤖

@pawbana pawbana Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is conscious decision. I believe error when updating last_used should not break existing DRPC session. I'm open to discussion.

@dannykopping dannykopping Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suggest adding a comment to the func to explain that the bool indicates "active", but if the update func errs then we can't know one way or another, so we assume it to still be active.

@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 4350b16 to 2138d0d Compare June 25, 2026 12:16
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 154ccfc to 156894e Compare June 25, 2026 12:22
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 2138d0d to 754b2bd Compare June 25, 2026 12:22
@pawbana
pawbana marked this pull request as ready for review June 25, 2026 15:38
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from a9ef8a2 to 0241b7a Compare June 26, 2026 09:21
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 156894e to a3269ec Compare June 26, 2026 09:21
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread codersdk/client.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go Outdated
Comment thread enterprise/coderd/aibridgeserve.go
Comment thread enterprise/coderd/aibridgeserve.go
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 0241b7a to 1ff23e2 Compare June 26, 2026 14:31
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from a3269ec to 4fd9fb0 Compare June 26, 2026 14:31
Comment thread enterprise/coderd/aibridgeserve.go
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 branch from 4fd9fb0 to 36e5b2a Compare June 26, 2026 15:15
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from 1ff23e2 to be258ae Compare June 26, 2026 15:15

pawbana commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Jun 26, 4:15 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 26, 4:17 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jun 26, 4:27 PM UTC: @pawbana merged this pull request with Graphite.

@pawbana
pawbana changed the base branch from pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-1 to graphite-base/26506 June 26, 2026 16:15
@pawbana
pawbana changed the base branch from graphite-base/26506 to main June 26, 2026 16:16
@pawbana
pawbana requested a review from Emyrk as a code owner June 26, 2026 16:16
@pawbana
pawbana force-pushed the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch from be258ae to 14c2818 Compare June 26, 2026 16:17
@pawbana
pawbana merged commit 6189d6e into main Jun 26, 2026
35 of 38 checks passed
@pawbana
pawbana deleted the pawel/aigov-308-implement-apiv2aibridgeserve-endpoint-2 branch June 26, 2026 16:27
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 26, 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.

2 participants