chore: remove experimental endpoints (#21390)

This should've been removed when we cut the Beta release, but we missed it. Adding as a drive-by.
This commit is contained in:
Danny Kopping
2025-12-29 16:17:46 +00:00
committed by GitHub
parent b522c9471a
commit a173c38715
6 changed files with 89 additions and 32 deletions
+77
View File
@@ -0,0 +1,77 @@
# AI Bridge Proxy
A MITM (Man-in-the-Middle) proxy server for intercepting and decrypting HTTPS requests to AI providers.
## Overview
The AI Bridge Proxy intercepts HTTPS traffic, decrypts it using a configured CA certificate, and forwards requests to AI Bridge for processing.
## Configuration
### Certificate Setup
Generate a CA key pair for MITM:
#### 1. Generate a new private key
```sh
openssl genrsa -out mitm.key 2048
chmod 400 mitm.key
```
#### 2. Create a self-signed CA certificate
```sh
openssl req -new -x509 -days 365 \
-key mitm.key \
-out mitm.crt \
-subj "/CN=Coder AI Bridge Proxy CA"
```
### Configuration options
| Environment Variable | Description | Default |
|------------------------------------|---------------------------------|---------|
| `CODER_AIBRIDGE_PROXY_ENABLED` | Enable the AI Bridge Proxy | `false` |
| `CODER_AIBRIDGE_PROXY_LISTEN_ADDR` | Address the proxy listens on | `:8888` |
| `CODER_AIBRIDGE_PROXY_CERT_FILE` | Path to the CA certificate file | - |
| `CODER_AIBRIDGE_PROXY_KEY_FILE` | Path to the CA private key file | - |
### Client Configuration
Clients must trust the proxy's CA certificate and authenticate with their Coder session token.
#### CA Certificate
Clients need to trust the MITM CA certificate:
```sh
# Node.js
export NODE_EXTRA_CA_CERTS="/path/to/mitm.crt"
# Python (requests, httpx)
export REQUESTS_CA_BUNDLE="/path/to/mitm.crt"
export SSL_CERT_FILE="/path/to/mitm.crt"
# Go
export SSL_CERT_FILE="/path/to/mitm.crt"
```
#### Proxy Authentication
Clients authenticate with the proxy using their Coder session token in the `Proxy-Authorization` header via HTTP Basic Auth.
The token is passed as the password (username is ignored):
```sh
export HTTP_PROXY="http://ignored:<coder-session-token>@<proxy-host>:<proxy-port>"
export HTTPS_PROXY="http://ignored:<coder-session-token>@<proxy-host>:<proxy-port>"
```
For example:
```sh
export HTTP_PROXY="http://coder:${CODER_SESSION_TOKEN}@localhost:8888"
export HTTPS_PROXY="http://coder:${CODER_SESSION_TOKEN}@localhost:8888"
```
Most HTTP clients and AI SDKs will automatically use these environment variables.
+1 -10
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-chi/chi/v5"
@@ -58,15 +57,7 @@ func aibridgeHandler(api *API, middlewares ...func(http.Handler) http.Handler) f
return
}
// Strip either the experimental or stable prefix.
// TODO: experimental route is deprecated and must be removed with Beta.
prefixes := []string{"/api/experimental/aibridge", "/api/v2/aibridge"}
for _, prefix := range prefixes {
if strings.Contains(r.URL.String(), prefix) {
http.StripPrefix(prefix, api.aibridgedHandler).ServeHTTP(rw, r)
break
}
}
http.StripPrefix("/api/v2/aibridge", api.aibridgedHandler).ServeHTTP(rw, r)
})
})
}
-5
View File
@@ -674,11 +674,6 @@ func TestAIBridgeRouting(t *testing.T) {
path: "/api/v2/aibridge/openai/v1/chat/completions",
expectedPath: "/openai/v1/chat/completions",
},
{
name: "ExperimentalPrefix",
path: "/api/experimental/aibridge/openai/v1/chat/completions",
expectedPath: "/openai/v1/chat/completions",
},
}
for _, tc := range cases {
-6
View File
@@ -226,12 +226,6 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
return api.refreshEntitlements(ctx)
}
api.AGPL.ExperimentalHandler.Group(func(r chi.Router) {
// Deprecated.
// TODO: remove with Beta release.
r.Route("/aibridge", aibridgeHandler(api, apiKeyMiddleware))
})
api.AGPL.APIHandler.Group(func(r chi.Router) {
r.Route("/aibridge", aibridgeHandler(api, apiKeyMiddleware))
})
+10 -10
View File
@@ -2744,6 +2744,16 @@ class ApiMethods {
setTimeout(() => res(), 500);
});
};
getAIBridgeInterceptions = async (options: SearchParamOptions) => {
const url = getURLWithSearchParams(
"/api/v2/aibridge/interceptions",
options,
);
const response =
await this.axios.get<TypesGen.AIBridgeListInterceptionsResponse>(url);
return response.data;
};
}
export type TaskFeedbackRating = "good" | "okay" | "bad";
@@ -2760,16 +2770,6 @@ export type CreateTaskFeedbackRequest = {
// above the ApiMethods class for a full explanation.
class ExperimentalApiMethods {
constructor(protected readonly axios: AxiosInstance) {}
getAIBridgeInterceptions = async (options: SearchParamOptions) => {
const url = getURLWithSearchParams(
"/api/experimental/aibridge/interceptions",
options,
);
const response =
await this.axios.get<TypesGen.AIBridgeListInterceptionsResponse>(url);
return response.data;
};
}
// This is a hard coded CSRF token/cookie pair for local development. In prod,
+1 -1
View File
@@ -13,7 +13,7 @@ export const paginatedInterceptions = (
return ["aiBridgeInterceptions", payload, pageNumber] as const;
},
queryFn: ({ limit, offset, payload }) =>
API.experimental.getAIBridgeInterceptions({
API.getAIBridgeInterceptions({
offset,
limit,
q: payload,