Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion reference/components/plugin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion reference/http/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
40 changes: 36 additions & 4 deletions reference/http/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,50 @@ Request and response objects follow the [WHATWG Fetch API](https://developer.moz

<VersionBadge version="v5.2.0" />

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overstates the failure scope. fastifyRoutes.ts does throw in #1964, but the loader's catch is per plugin inside the app's config loop (componentLoader.ts#L737) — it marks the fastifyRoutes component failed and continues; the code comment says as much ("contained to this component by the loader's per-component try/catch, so it does not take down the rest of the application"). So the app's other handlers (rest, static, …) still load and serve on the host mount; only the legacy routes fail.

As written, a reader expects the whole app to refuse to start and would misdiagnose a partially-running app — the same class of mismatch @cb1kenobi already caught once in the opposite direction (docs said "warns", code throws). Suggested wording: "Harper fails the application's fastifyRoutes with a load error rather than silently serving them unconstrained; the application's other handlers still load."

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.

Expand Down
5 changes: 4 additions & 1 deletion reference/operations-api/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` <VersionBadge version="v5.2.0" /> — 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:
Expand Down
4 changes: 3 additions & 1 deletion release-notes/v5-lincoln/5.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading