aichteeteapee: A Go HTTP Library Named By Someone Who Thinks Spelling Things Out Is Funny

Say it out loud: aichteeteapee.
Go on. Sound it out. Aitch-tee-tee-pee.
Yeah. That’s HTTP. I named my Go HTTP library by spelling “HTTP” phonetically and published it with a straight face. And the WebSocket package? It’s called dabluveees. Dub-ell-vee-ess. Double-v-s. WS. I did it twice.
But wait. The HTTP server package inside it? serbewr. Pronounced “server”. The proxy package? prawxxey. Pronounced “proxy”.
I did it four times.
No regrets.

Okay but what is it

aichteeteapee is a Go library that wraps net/http and gives you everything you’d otherwise spend the first three hours of every project setting up. You know the ritual. You’ve done it forty times. Make the mux. Configure the timeouts. Figure out where to put CORS again. Wire up graceful shutdown. Remember that one timeout you always forget. Add the panic recovery middleware. Copy the request ID snippet from that other project. Die a little inside.
That’s not hard. It’s just tedious as hell, and you do it for every single project, and you’re going to do it again next month for the next one.

The usage

package main
import (
    "context"
    "net/http"
    "github.com/psyb0t/aichteeteapee"
    "github.com/psyb0t/aichteeteapee/serbewr"
    "github.com/psyb0t/aichteeteapee/serbewr/middleware"
)
func main() {
    srv, _ := serbewr.New()
    router := &serbewr.Router{
        GlobalMiddlewares: []middleware.Middleware{
            middleware.RequestID(),
            middleware.Logger(),
            middleware.Recovery(),
            middleware.SecurityHeaders(),
            middleware.CORS(),
        },
        Groups: []serbewr.GroupConfig{{
            Path: "/",
            Routes: []serbewr.RouteConfig{{
                Method:  http.MethodGet,
                Path:    "/hello",
                Handler: func(w http.ResponseWriter, r *http.Request) {
                    aichteeteapee.WriteJSON(w, http.StatusOK, map[string]string{"msg": "hi"})
                },
            }},
        }},
    }
    srv.Start(context.Background(), router)
}

That’s it. You have request logging, panic recovery, CORS, HSTS/CSP/X-Frame-Options, request ID tracing, graceful shutdown, and sane timeouts. You wrote none of it. Import serbewr — pronounced “server”, obviously — and call New().
The root aichteeteapee package gives you the utilities: WriteJSON, GetClientIP, GetRequestID, every HTTP header name as a constant so you stop hardcoding strings, error codes for every HTTP status, pre-built error response structs. The stuff you reach for in every handler.

Security

Secure by default. CORS blocks unknown origins. WebSocket validates Origin against Host. File uploads sanitize filenames — no path traversal. Uploaded files get 0600 permissions and won’t overwrite existing files. Proxy responses are size-limited. Sensitive headers are filtered from echo responses.
For local dev when you want none of that:

aichteeteapee.FuckSecurity()

CORS allows all origins. WebSocket accepts any origin. One call. When you’re done fucking around:

aichteeteapee.UnfuckSecurity()

If you need per-component overrides without going full anarchy:

middleware.CORS(middleware.WithAllowAllOrigins())
wshub.UpgradeHandler(hub,
    wshub.WithUpgradeHandlerCheckOrigin(
        aichteeteapee.GetPermissiveWebSocketCheckOrigin,
    ),
)

Middleware

The built-ins aren’t toy implementations. CORS handles preflight correctly. The security headers middleware lets you enable or disable individual headers — you don’t have to take all-or-nothing defaults if you have a custom CSP or handle XSS at the app level. Timeout middleware ships with presets (short: 5s, default: 30s, long: 5min). Content-type enforcement for APIs:

serbewr.GroupConfig{
    Path: "/api",
    Middlewares: []middleware.Middleware{
        middleware.EnforceRequestContentTypeJSON(),
    },
}

Anything that posts non-JSON gets rejected before it ever hits your handler. The middleware chain builds the logger progressively — RequestID adds requestId to context, Logger adds method/path/IP, and downstream code calls slogging.GetLogger(ctx) and gets all fields automatically. No explicit logger passing anywhere.

prawxxey — the proxy package

serbewr/prawxxey. Spelled how it sounds. Pronounced like a normal person would say “proxy” if they were also insane.
Forward requests upstream with optional response caching, hop-by-hop header stripping, response size limits, and deterministic request fingerprinting. Cache key is derived from the request so identical requests hit cache instead of your upstream. Useful when you’re proxying external APIs with rate limits and don’t want every request burning quota.

dabluveees — the WebSocket stuff

serbewr/dabluvee-es. Dub-ell-vee-ess. WS. You get it.
The wshub package inside it gives you a full multi-client WebSocket hub. Create a hub, register event handlers by type, it manages connection lifecycle, broadcasting, and per-connection metadata. You’re not reinventing the client map and the select loop for the eighth time.

chatHub := wshub.NewHub("chat")
chatHub.RegisterEventHandlers(map[dabluveees.EventType]wshub.EventHandler{
    EventTypeChatMessage: func(hub wshub.Hub, client *wshub.Client, event *dabluveees.Event) error {
        event.Metadata.Set("timestamp", time.Now().Unix())
        hub.BroadcastToAll(event)
        return nil
    },
})
{Method: "GET", Path: "/ws/chat", Handler: wshub.UpgradeHandler(chatHub)}

Then there’s wsunixbridge. It bridges WebSocket connections to Unix domain sockets — your WebSocket client connects, a pair of Unix sockets appear server-side, any process on the machine can now talk to that browser client by reading and writing those sockets. Shell scripts, Python scripts, legacy binaries that know nothing about WebSockets can suddenly speak to one.

echo "hello from shell" | socat - UNIX-CONNECT:./sockets/connection-id_input

The naming, one final time

  • HTTP → aichteeteapee
  • server → serbewr
  • proxy → prawxxey
  • WS → dabluveees

The README says: “Pronounced ‘HTTP’. The name is the whole joke. Moving on.”
It did not move on. There are three more packages named the same way.
I stand by every single one of them.
github.com/psyb0t/aichteeteapee