httpPlugin

import { httpPlugin } from '@routecraft/routecraft'

Serves routes over HTTP. Backs the http() source; routes declare .from(http({ path, method })) and the plugin mounts them on a named server.

http is a first-class core config key, so the common path is defineConfig({ http: {...} }) rather than plugins: [httpPlugin(...)]. The factory is exported for programmatic composition.

import { defineConfig, jwt } from '@routecraft/routecraft'

export const craftConfig = defineConfig({
  servers: { public: { port: 8080, host: '0.0.0.0' } },
  http: {
    server: 'public',
    auth: jwt({ secret: process.env.JWT_SECRET!, issuer: '...', audience: '...' }),
  },
})

Options

OptionTypeDefaultRequiredDescription
serverstringdefaultNoSingle-mount sugar: named server for the lone default mount. Mutually exclusive with mounts.
authHttpAuth | falseinheritedNoSingle-mount sugar: the wall for a lone default mount at /. Omit to inherit server auth, false to remove the wall (the inherited validator stays reachable for .authorize()). Mutually exclusive with mounts.
mountsRecord<string, { path, server?, auth? }>one default mount at /NoNamed path-scoped surfaces, each a complete self-description: path, server, auth. See Mounts and authentication and Mounts and servers.
maxBodySizenumber10485760 (10 MB)NoMaximum request body in bytes. Larger requests get 413.
events{ perRequest?: boolean }{ perRequest: true }NoToggle the plugin:http:request:completed event.
builtins{ health?, ready?, openapi?: { enabled?: boolean; requireAuth?: boolean } }see belowNoPer-endpoint config for /health, /ready, /openapi.json. Each takes the same { enabled, requireAuth } shape. See Configuring built-ins on the adapter reference for defaults and the per-endpoint behaviour table.

Per-route authorization uses the existing .authorize({ roles, scopes }) builder. The mount, not the route, decides authentication: routes on a walled mount require a valid credential, routes on a mount with no wall (auth: false) never see one, and such a route that declares .authorize() forces verification through the inherited validator for itself alone. See Mounts and authentication for the full matrix. Built-in endpoints /health, /ready, and /openapi.json serve from the default mount at / unless a user route claims the same path, and describe only routes on that mount's server when mounts span several listeners.

Custom mounts

Custom plugins can join the same listener during bind, by requiring the WEB_INGRESS port the servers plugin provides:

import { definePlugin, requireWebIngress, WEB_INGRESS } from '@routecraft/routecraft'

const healthPlugin = definePlugin({
  id: 'acme.health',
  requires: [WEB_INGRESS],
  bind(c) {
    const ingress = requireWebIngress(c.require(WEB_INGRESS), 'public')
    const unmount = ingress.mountHttp({
      id: 'custom-health',
      claims: () => [{ kind: 'exact', path: '/internal/health', methods: ['GET'] }],
      handler: () => Response.json({ ok: true }),
    })
    c.onDispose(unmount)
  },
})

Claims are thunks evaluated once during startup validation. This lets plugins register dynamic routes during route startup while still detecting cross-mount conflicts before the listener binds.

A mount whose responses stream or stay quiet indefinitely (SSE, MCP-style channels) declares longLived: true; the ingress then exempts its requests from the listener's idle timeout while every other connection keeps the bounded default.

Lifecycle

  • httpPlugin(options) validates its options when it is called, before any lifecycle phase.
  • bind(c) provides the mount table as the HTTP port the http() source reads, and mounts it on the selected named server. It does not bind a socket.
  • During context start, all mount claims on all named servers are validated before any listener binds. The server then emits server:listening { server, port, host }.
  • On context stop, named servers drain active requests, close idle connections, and emit server:closed { server }.
  • A bind failure (EADDRINUSE / EADDRNOTAVAIL) surfaces as RC5019.

Events

See HTTP plugin events for the full list. The plugin also re-uses the framework's auth:success / auth:rejected events with source: "http".