mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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.
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user