Add request sanitizer, background token refresh, and OpenCode support

Sanitizer renames tool names and replaces system prompt patterns
that Anthropic fingerprints to detect non-Claude-Code clients.
Lowercase tool names (bash, read, glob, etc.) combined together
trigger rejection — renaming to PascalCase bypasses this.
Configurable via YAML sanitize rules for tools, system, and body.

Background OAuth token refresh every 30s with 5-minute pre-expiry
lead. Uses Chrome TLS fingerprint for refresh endpoint too.

Adds /messages route (without /v1 prefix) for OpenCode compat.
This commit is contained in:
Alexander
2026-04-09 22:52:43 +02:00
parent c4c1d4daa4
commit 909c8b1894
11 changed files with 428 additions and 89 deletions
+10 -2
View File
@@ -2,6 +2,7 @@ package server
import (
"fmt"
"log"
"net/http"
"strings"
@@ -24,10 +25,17 @@ func New(cfg *config.Config, pool *auth.Pool, profile *proxy.SniffedProfile) *Se
engine.Use(corsMiddleware())
engine.Use(authMiddleware(cfg.APIKeys))
engine.POST("/v1/messages", proxy.HandleMessages(pool, profile))
handler := proxy.HandleMessages(pool, profile, cfg.Sanitize)
engine.POST("/v1/messages", handler)
engine.POST("/messages", handler)
engine.GET("/healthz", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
engine.NoRoute(func(c *gin.Context) {
log.Printf("unmatched route: %s %s", c.Request.Method, c.Request.URL.Path)
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
})
return &Server{engine: engine, port: cfg.Port}
}
@@ -41,7 +49,7 @@ func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization, x-api-key")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization, x-api-key, anthropic-version, anthropic-beta")
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)