diff --git a/reference/components/plugin-api.md b/reference/components/plugin-api.md index ce801923..43211396 100644 --- a/reference/components/plugin-api.md +++ b/reference/components/plugin-api.md @@ -42,7 +42,9 @@ General plugin configuration options: - `host` — `string` _(optional)_ — Virtual hostname used to route the plugin's HTTP, WebSocket, and upgrade handlers - `timeout` — `number` _(optional)_ — Timeout in milliseconds for plugin operations. Takes precedence over the plugin's `defaultTimeout` and the system default (30 seconds) -`urlPath` and `host` are available in v5.2.0. Harper automatically passes them to handlers registered through the scoped `server` API. See [Middleware routing](../http/overview#middleware-routing) for an example and [`HttpOptions`](../http/api#httpoptions) for matching behavior. +`urlPath` and `host` are available in v5.2.0. Harper automatically passes them to handlers registered through the scoped `server` API. + +These position a plugin **within** its application. Where the application itself is served is set on the application's entry in the root `harper-config.yaml`; that mount is prefixed onto each plugin's `urlPath`, and a `host` there overrides one set here. See [Middleware routing](../http/overview#middleware-routing) for the full picture and [`HttpOptions`](../http/api#httpoptions) for matching behavior. ### File Entries diff --git a/reference/http/api.md b/reference/http/api.md index 3bc9d321..f21ae18f 100644 --- a/reference/http/api.md +++ b/reference/http/api.md @@ -48,7 +48,7 @@ To continue the middleware chain, call `next(request)`. To short-circuit, return | `before` | string | - | Run this entry before the named middleware entry | | `after` | string | - | Run this entry after the named middleware entry | | `urlPath` | string | - | Only handle requests whose pathname matches this prefix on a segment boundary (`/api` matches `/api` and `/api/x`, not `/apinews`). Harper removes the prefix before passing the request to the handler. | -| `host` | string | - | Only handle requests whose `Host` header matches this virtual hostname (case-sensitive, port ignored) | +| `host` | string | - | Only handle requests whose `Host` header matches this virtual hostname (case-insensitive, port ignored). IPv6 hosts are given as a bare literal (`::1`), not bracketed. | | `runFirst` | boolean | `false` | Deprecated. Insert this handler at the front of the chain. Use `before` or `after` for explicit ordering. | | `port` | number | `http.port` | Target the HTTP server on this port | | `securePort` | number | `http.securePort` | Target the HTTPS server on this port | diff --git a/reference/http/overview.md b/reference/http/overview.md index 3b60d086..7772b2a7 100644 --- a/reference/http/overview.md +++ b/reference/http/overview.md @@ -30,18 +30,50 @@ Request and response objects follow the [WHATWG Fetch API](https://developer.moz -Harper can route middleware by URL prefix, virtual hostname, or both. Set `urlPath` or `host` in a component's `config.yaml` to create a routed middleware chain without writing dispatch code: +Harper can route middleware by URL prefix, virtual hostname, or both, with no dispatch code in the application. Where an application is served is a deployment concern, so declare it on that application's entry in the root `harper-config.yaml`: ```yaml -rest: +# harper-config.yaml +my-app: host: api.example.com urlPath: /v1 +``` + +Every handler the application registers — HTTP, WebSocket, and upgrade — is then served under `api.example.com/v1`, and Harper removes `/v1` from the pathname before invoking the chain. Requests that match no routed chain use the default middleware chain. + +Because routing lives in the root config, the same application package can be mounted at a different hostname or path per environment without editing the application. The entry does not need a `package` — routing applies to any application in the components root, however it was deployed. + +You can also set it at deploy time: + +```bash +harper deploy project=my-app package=@my/app host=api.example.com urlPath=/v1 +``` + +#### Routing individual plugins + +A plugin's own `urlPath` sets where it sits **within** the application, and is configured in the application's `config.yaml`: + +```yaml +# my-app/config.yaml static: files: 'web/**' - host: www.example.com + urlPath: assets ``` -The `rest` handler receives requests under `api.example.com/v1`; Harper removes `/v1` from the pathname before invoking the chain. The `static` handler receives requests for `www.example.com`. Unmatched requests use the default middleware chain. +The application's mount composes with it rather than replacing it, so app-internal structure survives being relocated. With the root config above, the static files are served at `api.example.com/v1/assets/`. A plugin that configures no `urlPath` of its own is served at the mount itself (`api.example.com/v1`). + +An application can also set `host` per plugin, but a `host` on the root-config entry overrides it — the operator's choice of hostname wins over one the application shipped. + +Handlers see the request with the mount already removed from the pathname, so application code addresses itself mount-relative and does not need to know where it is mounted. + +#### What a mount does not do + +A mount is a routing prefix, not an isolation boundary: + +- **It does not namespace resources.** Exported tables live in one instance-wide registry, so a table exported by one application is reachable through any mounted REST route, not only its own. +- **It does not constrain legacy Fastify routes by host.** `fastifyRoutes` registers as a global fallback outside the routed middleware chain, so those routes answer on every hostname. A `urlPath` mount does apply (it becomes the Fastify route prefix); a `host` mount does not, and Harper refuses to load a host-mounted application that declares `fastifyRoutes` rather than silently serving it unconstrained. Port them to `server.http()` for host routing. + +Host matching ignores the port and is case-insensitive. IPv6 hosts are configured as a bare literal (`::1`), not bracketed. Custom components can configure the same behavior programmatically with `server.http(listener, { host, urlPath })`. See [`HttpOptions`](./api#httpoptions) for matching priority and middleware ordering options. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index 7a52b122..1f653829 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -604,10 +604,13 @@ Deploys a component. The `package` option accepts any valid NPM reference includ Additional parameters: -- `urlPath` — override the HTTP URL path the component is mounted at (e.g. `"/api/v2"`) +- `urlPath` — the HTTP URL path the component is mounted at (e.g. `"/api/v2"`). Must not contain `..` or `.` path segments. Persisted on the component's root-config entry; see [HTTP middleware routing](../http/overview.md#middleware-routing). +- `host` — the virtual hostname the component is served on (e.g. `"api.example.com"`). Must be a bare hostname or IPv6 literal — no scheme, port, path, or brackets. Persisted alongside `urlPath`. - `install_allow_scripts` — set to `true` to allow npm pre/post install scripts (disabled by default) - `credentials` — credentials for installing a component from a private npm registry or private git repository (see below) +`urlPath` and `host` both require `package` and are rejected on a payload-only deploy. To mount a payload-deployed component, add `host`/`urlPath` to its entry in the root `harper-config.yaml` instead. + #### Deploy credentials (`credentials`) When a component is installed from a private source, `credentials` supplies the authentication. It is an array of entries; each entry is one of two kinds, identified by its key: diff --git a/release-notes/v5-lincoln/5.2.md b/release-notes/v5-lincoln/5.2.md index 54bed7e3..f51339c8 100644 --- a/release-notes/v5-lincoln/5.2.md +++ b/release-notes/v5-lincoln/5.2.md @@ -30,7 +30,9 @@ The `set_configuration` operation now accepts `"replicated": true` to apply a co ### Middleware routing and ordering -Components can now declare `host` and `urlPath` in `config.yaml`, or pass them to `server.http()`, `server.ws()`, and `server.upgrade()`, to create middleware chains routed by virtual hostname, URL prefix, or both. The new `name`, `before`, and `after` options provide explicit middleware ordering. See [HTTP middleware routing](/reference/v5/http/overview#middleware-routing) and [`HttpOptions`](/reference/v5/http/api#httpoptions). +Applications can now be routed by virtual hostname, URL prefix, or both, with no dispatch code. Declare `host` and `urlPath` on the application's entry in the root `harper-config.yaml` — or pass them to `deploy_component` — and every handler the application registers is served under that hostname and path. Because the routing lives in the root config, the same application can be mounted differently per environment without editing it. A plugin's own `urlPath` still positions it within the application, and the application's mount is prefixed onto it. + +Components can also pass `host` and `urlPath` directly to `server.http()`, `server.ws()`, and `server.upgrade()`. The new `name`, `before`, and `after` options provide explicit middleware ordering. See [HTTP middleware routing](/reference/v5/http/overview#middleware-routing) and [`HttpOptions`](/reference/v5/http/api#httpoptions). ## Security