Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@ title: "Environment Variables"
The following environment variables are accepted by the router, and they override other
configurations that can be set in the router's configuration file (`router.config.yaml`):

| Name | Accepted Values | Overrides (YAML) |
| --------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------- |
| `LOG_LEVEL` | `trace` / `debug` / `info` / `warn` / `error` | [`log.level`](./log#level) |
| `LOG_FORMAT` | `json` / `pretty-tree` / `pretty-compact` | [`log.format`](./log#format) |
| `LOG_FILTER` | `string` | [`log.filter`](./log#filter) |
| `LABORATORY_ENABLED` | `true` / `false` | [`laboratory.enabled`](./laboratory#enabled) |
| `PORT` | `number` (port number) | [`http.port`](./http#port) |
| `HOST` | `string` (host name or IP address) | [`http.host`](./http#host) |
| `ROUTER_HTTP_WORKERS` | `number` (worker thread count) | [`http.workers`](./http#workers) |
| `SUPERGRAPH_FILE_PATH` | `string` (relative or absolute path to a local file) | [`supergraph[file].path`](./supergraph#path) |
| `HIVE_CDN_ENDPOINT` | `string` (full url to Hive CDN supergraph artifact) | [`supergraph[hive].endpoint`](./supergraph#endpoint) |
| `HIVE_CDN_KEY` | `string` (Hive CDN key) | [`supergraph[hive].key`](./supergraph#key) |
| `HIVE_CDN_POLL_INTERVAL` | `string` (human-readable, for example: `10s`) | [`supergraph[hive].poll_interval`](./supergraph#poll_interval) |
| `SUBSCRIPTIONS_ENABLED` | `true` / `false` | [`subscriptions.enabled`](../subscriptions#configuration) |
| `WEBSOCKET_ENABLED` | `true` / `false` | [`websocket.enabled`](../subscriptions/websockets#enabled) |
| `TELEMETRY_TRACING_SAMPLING_RATE` | `number` (between `0.0` and `1.0`) | [`telemetry.tracing.collect.sampling`](./telemetry#tracing) |
| Name | Accepted Values | Overrides (YAML) |
| --------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------- |
| `LOG_LEVEL` | `trace` / `debug` / `info` / `warn` / `error` | [`log.level`](./log#level) |
| `LOG_FORMAT` | `json` / `pretty-tree` / `pretty-compact` | [`log.format`](./log#format) |
| `LOG_FILTER` | `string` | [`log.filter`](./log#filter) |
| `LABORATORY_ENABLED` | `true` / `false` | [`laboratory.enabled`](./laboratory#enabled) |
| `PORT` | `number` (port number) | [`http.port`](./http#port) |
| `HOST` | `string` (host name or IP address) | [`http.host`](./http#host) |
| `ROUTER_HTTP_WORKERS` | `number` (worker thread count) | [`http.workers`](./http#workers) |
| `SUPERGRAPH_FILE_PATH` | `string` (relative or absolute path to a local file) | [`supergraph[file].path`](./supergraph#path) |
| `HIVE_CDN_ENDPOINT` | `string` (full url to Hive CDN supergraph artifact) | [`supergraph[hive].endpoint`](./supergraph#endpoint) |
| `HIVE_CDN_KEY` | `string` (Hive CDN key) | [`supergraph[hive].key`](./supergraph#key) |
| `HIVE_CDN_POLL_INTERVAL` | `string` (human-readable, for example: `10s`) | [`supergraph[hive].poll_interval`](./supergraph#poll_interval) |
| `SUBSCRIPTIONS_ENABLED` | `true` / `false` | [`subscriptions.enabled`](../subscriptions#configuration) |
| `WEBSOCKET_ENABLED` | `true` / `false` | [`websocket.enabled`](../subscriptions/websockets#enabled) |
| `TELEMETRY_TRACING_SAMPLING_RATE` | `number` (between `0.0` and `1.0`) | [`telemetry.tracing.collect.sampling`](./telemetry#tracing) |
| `DISABLE_SUBGRAPH_ERROR_MASKING` | `true` / `false` | [`error_masking.all.error_message`](./error_masking#allerror_message) |

> In cases where a configuration file (`router.config.yaml`) is used with environment variables at
> the same time, environment variables will only override the specific configuration values.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
---
title: "error_masking"
---

import { Callout } from "@hive/design-system/hive-components/callout";

The `error_masking` configuration controls how errors originating from your subgraphs are rewritten
before they are returned to clients. It lets you redact error messages and strip sensitive
`extensions` fields, globally or per subgraph.

For conceptual guidance and rollout strategy, see
[Error Masking](/docs/router/security/error-masking).

<Callout type="info">
Error masking only applies to **subgraph** errors — any error carrying an
`extensions.service` field, whether it was returned by the subgraph or
synthesized by the router for a network/HTTP failure. Errors produced by the
router itself (for example `GRAPHQL_PARSE_FAILED`) are never masked.
</Callout>

## Configuration Structure

```yaml title="router.config.yaml"
error_masking:
redacted_error_message: "Unexpected error" # message used when a subgraph error is masked
all:
error_message: true # mask the error message for every subgraph
extensions: # optional — omit to leave extensions untouched
mode: allow # allow | deny
keys:
- code
subgraphs: # optional per-subgraph overrides
products:
error_message: false
extensions:
mode: deny
keys: []
```

Error masking is **enabled by default**. Even with no `error_masking` block, subgraph error messages
are replaced with `"Unexpected error"`.

## Options

### `redacted_error_message`

- **Type:** `string`
- **Default:** `"Unexpected error"`

The message that replaces a subgraph error's original message whenever masking is applied.

### `all`

- **Type:** `object`
- **Default:** `{ error_message: true }`

The default masking rules applied to every subgraph. Individual subgraphs can override these under
[`subgraphs`](#subgraphs).

#### `all.error_message`

- **Type:** `boolean`
- **Default:** `true`

Whether to replace the error `message` with [`redacted_error_message`](#redacted_error_message).
Set to `false` to pass the original subgraph message through to clients.

This can also be forced off with the
[`DISABLE_SUBGRAPH_ERROR_MASKING`](#disabling-via-environment-variable) environment variable.

#### `all.extensions`

- **Type:** `object`
- **Default:** unset (extensions are **not** masked)

Controls which keys of the error `extensions` object are kept or removed. When omitted, the router
leaves `extensions` untouched — only the message is masked.

<Callout type="warning">
Masking the message alone does **not** redact `extensions`. Sensitive detail
is often carried in `extensions` (stack hints, internal codes, upstream HTTP
bodies), so configure `extensions` explicitly when the default message-only
masking is not enough.
</Callout>

##### `all.extensions.mode`

- **Type:** `string`
- **Allowed values:** `allow`, `deny`
- **Required:** yes (when `extensions` is set)

- `allow`: an allowlist — only the keys in [`keys`](#allextensionskeys) are kept, everything else is
removed. `keys: []` removes **all** extensions.
- `deny`: a denylist — only the keys in `keys` are removed, everything else is kept. `keys: []`
removes **nothing**.

##### `all.extensions.keys`

- **Type:** `string[]`
- **Required:** yes (when `extensions` is set)

The list of `extensions` keys the `mode` operates on. Only **root-level** keys are supported.

The router's well-known extension keys are:

| Key | Description |
| :------------- | :---------------------------------------------------- |
| `code` | The error code (for example `SUBREQUEST_HTTP_ERROR`). |
| `service` | The subgraph the error originated from. |
| `affectedPath` | The response path affected by the error. |

Any other key a subgraph puts under `extensions` (for example `http`) is matched by its exact name.

### `subgraphs`

- **Type:** `object` (map of subgraph name → config)
- **Default:** unset

Per-subgraph overrides. A subgraph listed here overrides the matching field from [`all`](#all).

#### `subgraphs.<name>.error_message`

- **Type:** `boolean`
- **Default:** inherits [`all.error_message`](#allerror_message)

Overrides message masking for this subgraph only. When omitted, the value from `all` is used.

#### `subgraphs.<name>.extensions`

- **Type:** `object`
- **Default:** unset

Overrides extensions masking for this subgraph only, using the same `mode`/`keys` shape as
[`all.extensions`](#allextensions). When omitted, the subgraph inherits [`all.extensions`](#allextensions).

<Callout type="info">
Each field is inherited independently. A subgraph that sets only
`error_message` still uses `all.extensions`, and vice versa — you only
override the fields you specify.
</Callout>

## Disabling via environment variable

Setting `DISABLE_SUBGRAPH_ERROR_MASKING=true` forces `all.error_message` to `false`, overriding the
config file. It is a convenient escape hatch for local debugging.

| Name | Accepted Values | Overrides (YAML) |
| :------------------------------- | :--------------- | :----------------------------------------------------- |
| `DISABLE_SUBGRAPH_ERROR_MASKING` | `true` / `false` | [`error_masking.all.error_message`](#allerror_message) |

<Callout type="info">
This variable only affects **message** masking (`all.error_message`). It does
not touch `extensions` masking or any per-subgraph override.
</Callout>

## Behavior

- Masking is the **last** step of the response pipeline, applied after metrics, tracing, and logging.
Your observability tools always see the original, unmasked error — only the client-facing response
is redacted.
- Only errors with an `extensions.service` field are considered subgraph errors and are eligible for
masking. This includes both errors returned by the subgraph and errors the router synthesizes for
network/HTTP failures (`SUBREQUEST_HTTP_ERROR`, `DOWNSTREAM_SERVICE_ERROR`).
- Message masking and extensions masking are independent: you can redact the message while keeping
some extensions, or keep the message while stripping extensions.

## Examples

### Fully disable masking

```yaml title="router.config.yaml"
error_masking:
all:
error_message: false
```

### Custom redaction message

```yaml title="router.config.yaml"
error_masking:
redacted_error_message: "Something went wrong"
```

### Mask the message and strip all extensions

```yaml title="router.config.yaml"
error_masking:
all:
error_message: true
extensions:
mode: allow
keys: [] # allow none — remove every extension
```

### Keep only the error code

```yaml title="router.config.yaml"
error_masking:
all:
error_message: true
extensions:
mode: allow
keys:
- code
```

### Remove specific sensitive keys

```yaml title="router.config.yaml"
error_masking:
all:
error_message: true
extensions:
mode: deny
keys:
- code
- service
```

### Per-subgraph override

Mask everything globally, but trust the internal `products` subgraph enough to pass its original
messages and extensions through unmasked:

```yaml title="router.config.yaml"
error_masking:
all:
error_message: true
extensions:
mode: allow
keys: [] # allow none
subgraphs:
products:
error_message: false
extensions:
mode: deny
keys: [] # deny none — keep every extension
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"environment-variables",
"expressions",
"demand_control",
"error_masking",
"..."
]
}
Loading
Loading