New builds the v1 server. It installs the e2a error envelope globally, stands up the Huma API on a chi router under the `/v1` documentation paths, registers the ported operations, and points chi's not-found/ method-not-allowed handlers at the legacy surface.
(deps Deps)
| 272 | // paths, registers the ported operations, and points chi's not-found/ |
| 273 | // method-not-allowed handlers at the legacy surface. |
| 274 | func New(deps Deps) *Server { |
| 275 | installErrorEnvelope() |
| 276 | |
| 277 | root := chi.NewRouter() |
| 278 | root.Use(requestID) |
| 279 | root.Use(securityHeaders) |
| 280 | root.Use(authChallenge(deps.AuthChallenge)) |
| 281 | root.Use(withRawRequest) |
| 282 | |
| 283 | config := huma.DefaultConfig("e2a API", APIVersion) |
| 284 | // Serve the spec and human docs under the versioned prefix so they sit |
| 285 | // beside the operations (api-v1-redesign §1: everything lives under the |
| 286 | // api host; here, under /v1). |
| 287 | config.OpenAPIPath = "/v1/openapi" |
| 288 | config.DocsPath = "/v1/docs" |
| 289 | config.SchemasPath = "/v1/schemas" |
| 290 | // Drop Huma's default schema-link transformer: it injects a `$schema` |
| 291 | // field and Link header into response bodies, which would change the |
| 292 | // clean contract shape this redesign is standardizing. Keep only our |
| 293 | // request-id stamper. |
| 294 | config.CreateHooks = nil |
| 295 | config.Transformers = []huma.Transformer{stampRequestID} |
| 296 | config.Info.Description = "e2a — authenticated email gateway for AI agents. v1 contract." |
| 297 | // Canonical production host (api-v1-redesign §1: "Canonical base URL |
| 298 | // https://api.e2a.dev/v1"). Operations already carry the /v1 prefix, so the |
| 299 | // server URL stops at the host — otherwise clients would double it. Without a |
| 300 | // servers block, generated SDKs default to http://localhost (a |
| 301 | // Bearer-over-cleartext footgun). |
| 302 | config.Servers = []*huma.Server{ |
| 303 | {URL: "https://api.e2a.dev", Description: "Production"}, |
| 304 | } |
| 305 | // One auth scheme across the surface: a Bearer credential that is |
| 306 | // either an API key or an OAuth 2.1 access token (api-v1-redesign §5). |
| 307 | config.Components.SecuritySchemes = map[string]*huma.SecurityScheme{ |
| 308 | "bearer": { |
| 309 | Type: "http", |
| 310 | Scheme: "bearer", |
| 311 | Description: "API key (e2a_…) or OAuth 2.1 access token, sent as `Authorization: Bearer <token>`.", |
| 312 | }, |
| 313 | } |
| 314 | |
| 315 | api := humachi.New(root, config) |
| 316 | |
| 317 | s := &Server{Router: root, API: api, deps: deps} |
| 318 | // Rate limiting runs as Huma middleware so it can stamp the IETF |
| 319 | // RateLimit-* headers on the response and short-circuit a 429 before the |
| 320 | // handler. Registered once; applies to every operation. |
| 321 | api.UseMiddleware(s.rateLimit) |
| 322 | s.registerOperations() |
| 323 | |
| 324 | // WebSocket transport — registered directly on chi (not Huma; it's a raw |
| 325 | // upgrade, not a JSON operation). First-class /v1 inbound transport. |
| 326 | if deps.WSHandle != nil { |
| 327 | root.Get("/v1/agents/{email}/ws", func(w http.ResponseWriter, r *http.Request) { |
| 328 | deps.WSHandle(w, r, chi.URLParam(r, "email")) |
| 329 | }) |
| 330 | } |
| 331 |