From 7f09592d4fd50704640d899c762145de54497625 Mon Sep 17 00:00:00 2001 From: David C Date: Tue, 6 Oct 2026 11:39:23 -0400 Subject: [PATCH 1/6] Document agent.configScope and the agent's filesystem scopes, including the key-material refusal Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/configuration/options.md | 35 ++++++++++++++++++++++++-- reference/operations-api/operations.md | 4 +-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index b540e067..b1b44373 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -397,11 +397,42 @@ agent: - `autoApprove` — Run without per-action approval gates; _Default_: `false` - `allowDestructive` — Include the tools marked destructive in the agent's toolset: `write_file`, the inspector's code-evaluation tools, and any operations tool carrying MCP's [`destructiveHint`](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). When `false` they are removed entirely rather than gated. That hint comes from a curated set in core which does not cover every damaging operation, so this is not a complete safety boundary on its own — see [Agent operations](../operations-api/operations.md#agent); _Default_: `false` - `user` — Harper user the agent's **operations** tools run as; the filesystem, HTTP, schedule, and inspector tools always run at process privilege regardless. If it cannot be resolved and it is not the default, the agent fails closed and runs with no operations tools; _Default_: `hdb_agent`, which falls back to a `super_user` bootstrap identity -- `componentsScope` — Filesystem write scope for component edits, relative to `rootPath`; _Default_: the full `componentsRoot` +- `componentsScope` — The agent's `components` filesystem scope, which it reads and, with `allowDestructive`, writes; relative to `rootPath`. Read at startup only; _Default_: the full `componentsRoot` +- `configScope` — The agent's read-only `config` filesystem scope: a file or a directory, absolute or relative to `rootPath`. Read at startup only. See [Filesystem scopes](#filesystem-scopes); _Default_: the Harper config file only - `httpFetch` — Whether the agent has its `http_fetch` tool, and which hosts it may reach: `true`, `false`, or `{ allow: [...] }`. Read at startup only. See [Restricting `http_fetch`](#restricting-http_fetch); _Default_: `true` - `systemPromptAppend` — Operator text appended to the agent's system prompt -`enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend` can also be changed at runtime with [`set_agent_config`](../operations-api/operations.md#set_agent_config), which applies in memory only. `enabled` is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all. +`enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend` can also be changed at runtime with [`set_agent_config`](../operations-api/operations.md#set_agent_config), which applies in memory only. `enabled` is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all. `httpFetch`, `componentsScope`, and `configScope` are read at startup only, and `set_agent_config` rejects them. + +### Filesystem scopes + + + +The agent's filesystem tools (`read_file`, `list_dir`, `grep_files`, `tail_file`, and `write_file` when `allowDestructive` is on) take a `root` naming one of three scopes: + +| Scope | Reaches | Access | +| ------------ | ------------------------------------------------- | -------------- | +| `components` | `componentsRoot`, or `componentsScope` when set | read and write | +| `logs` | the log directory | read | +| `config` | the Harper config file, or `configScope` when set | read | + +Before v5.3.2, `config` was the directory holding `harper-config.yaml`. On a default install that is `rootPath` itself, so the agent could read `keys/`, `database/`, and the rest of the root. It is now the config file alone, and `list_dir` shows only that file. + +To give the agent more, such as a directory of extra configuration, set `configScope` to a file or a directory, absolute or relative to `rootPath`. It is read at startup only. A path that names no file or directory at startup leaves the `config` scope unavailable and logs an error. + +```yaml +agent: + enabled: true + configScope: /etc/harper/extra +``` + +Key material is refused in every scope, whatever `componentsScope` and `configScope` say: + +- **Harper's key directories**, `/keys` (TLS and JWT keys) and `/ssh` (git deploy keys): nothing in them is read, listed, or written. Paths are compared after resolving symlinks. +- **Key file names**, `*.pem`, `*.key`, and `.jwtPass`: not read. `list_dir` still shows the names, and `write_file` can still create such a file outside the key directories. +- **Text holding a PEM private key** (`-----BEGIN ... PRIVATE KEY-----`), such as an inline `tls.privateKey` in the config file: `read_file` and `tail_file` refuse it, and `grep_files` skips the file. The check looks only at the text about to be returned. + +The config file can still hold secrets that are not PEM keys, such as a model `apiKey` or storage credentials, and the agent reads it. Setting `configScope` to `rootPath` also restores read access to the raw database files and backups. ### Restricting `http_fetch` diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index eb05c42d..0641f6a3 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -1674,7 +1674,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan - `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else. - The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) -- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the log and configuration directories as well as the component tree, so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. +- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file (key material is refused in every [filesystem scope](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. - With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege. - Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt. - That set is an explicit list in core rather than a prefix match, and it is not a list of every damaging operation, so `allowDestructive` and `autoApprove` are not a boundary by themselves. The component operations are the ones to know about: `drop_component` and `deploy_component` are both off the set, so opting either into [`mcp.operations.allow`](../mcp/configuration.md#mcpoperationsallow) puts it in the agent's toolset where `allowDestructive: false` does not remove it and no approval gates it — and `deploy_component` writes code the Harper process then executes. Vet anything you add to that allow list on its own merits rather than assuming these two settings cover it. @@ -1809,7 +1809,7 @@ One gap is worth knowing: changing `allowDestructive` with [`set_agent_config`]( ### `set_agent_config` -Updates agent settings and returns the resulting configuration. Accepts any of `enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend`; keys not supplied are left unchanged. Each field is described under [`agent`](../configuration/options.md#agent). A request that includes `httpFetch` is rejected with a 400 and nothing in it is applied: the [`http_fetch` policy](../configuration/options.md#restricting-http_fetch) is read at startup only. +Updates agent settings and returns the resulting configuration. Accepts any of `enabled`, `provider`, `model`, `maxTurns`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend`; keys not supplied are left unchanged. Each field is described under [`agent`](../configuration/options.md#agent). A request that includes `httpFetch`, `componentsScope`, or `configScope` is rejected with a 400 and nothing in it is applied: the [`http_fetch` policy](../configuration/options.md#restricting-http_fetch) and the [filesystem scopes](../configuration/options.md#filesystem-scopes) are read at startup only. ```json { "operation": "set_agent_config", "autoApprove": false, "maxTurns": 20 } From b3339e697174e25ce81d45fc597e7476984bfdae Mon Sep 17 00:00:00 2001 From: David C Date: Tue, 6 Oct 2026 12:25:54 -0400 Subject: [PATCH 2/6] Correct the agent filesystem-scope docs: write_file takes no root, widened scopes reach further, and the badge marks a change Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/configuration/options.md | 12 +++++++----- reference/operations-api/operations.md | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index b1b44373..37a64561 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -397,7 +397,7 @@ agent: - `autoApprove` — Run without per-action approval gates; _Default_: `false` - `allowDestructive` — Include the tools marked destructive in the agent's toolset: `write_file`, the inspector's code-evaluation tools, and any operations tool carrying MCP's [`destructiveHint`](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). When `false` they are removed entirely rather than gated. That hint comes from a curated set in core which does not cover every damaging operation, so this is not a complete safety boundary on its own — see [Agent operations](../operations-api/operations.md#agent); _Default_: `false` - `user` — Harper user the agent's **operations** tools run as; the filesystem, HTTP, schedule, and inspector tools always run at process privilege regardless. If it cannot be resolved and it is not the default, the agent fails closed and runs with no operations tools; _Default_: `hdb_agent`, which falls back to a `super_user` bootstrap identity -- `componentsScope` — The agent's `components` filesystem scope, which it reads and, with `allowDestructive`, writes; relative to `rootPath`. Read at startup only; _Default_: the full `componentsRoot` +- `componentsScope` — The agent's `components` filesystem scope, which it reads and, with `allowDestructive`, writes; absolute or relative to `rootPath`. Read at startup only; _Default_: the full `componentsRoot` - `configScope` — The agent's read-only `config` filesystem scope: a file or a directory, absolute or relative to `rootPath`. Read at startup only. See [Filesystem scopes](#filesystem-scopes); _Default_: the Harper config file only - `httpFetch` — Whether the agent has its `http_fetch` tool, and which hosts it may reach: `true`, `false`, or `{ allow: [...] }`. Read at startup only. See [Restricting `http_fetch`](#restricting-http_fetch); _Default_: `true` - `systemPromptAppend` — Operator text appended to the agent's system prompt @@ -406,9 +406,9 @@ agent: ### Filesystem scopes - + -The agent's filesystem tools (`read_file`, `list_dir`, `grep_files`, `tail_file`, and `write_file` when `allowDestructive` is on) take a `root` naming one of three scopes: +The agent's read tools (`read_file`, `list_dir`, `grep_files`, and `tail_file`) take a `root` naming one of three scopes. `write_file`, available when `allowDestructive` is on, takes no `root` and always writes to `components`. | Scope | Reaches | Access | | ------------ | ------------------------------------------------- | -------------- | @@ -426,11 +426,13 @@ agent: configScope: /etc/harper/extra ``` -Key material is refused in every scope, whatever `componentsScope` and `configScope` say: +Whatever `componentsScope` and `configScope` say, every scope refuses: - **Harper's key directories**, `/keys` (TLS and JWT keys) and `/ssh` (git deploy keys): nothing in them is read, listed, or written. Paths are compared after resolving symlinks. - **Key file names**, `*.pem`, `*.key`, and `.jwtPass`: not read. `list_dir` still shows the names, and `write_file` can still create such a file outside the key directories. -- **Text holding a PEM private key** (`-----BEGIN ... PRIVATE KEY-----`), such as an inline `tls.privateKey` in the config file: `read_file` and `tail_file` refuse it, and `grep_files` skips the file. The check looks only at the text about to be returned. +- **Text holding a PEM private key** (`-----BEGIN ... PRIVATE KEY-----`), such as an inline `tls.privateKey` in the config file: `read_file` refuses the file, `tail_file` refuses lines that hold one or a file that ends partway through one, and `grep_files` skips the file. + +A key with neither a key file name nor PEM armor, such as a `.p12` bundle or raw DER, is not recognized outside the key directories. The config file can still hold secrets that are not PEM keys, such as a model `apiKey` or storage credentials, and the agent reads it. Setting `configScope` to `rootPath` also restores read access to the raw database files and backups. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index 0641f6a3..1964fe4b 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -1674,7 +1674,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan - `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else. - The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) -- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file (key material is refused in every [filesystem scope](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. +- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file, and further wherever `componentsScope` or `configScope` widens a scope (key material is refused in every [filesystem scope](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. - With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege. - Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt. - That set is an explicit list in core rather than a prefix match, and it is not a list of every damaging operation, so `allowDestructive` and `autoApprove` are not a boundary by themselves. The component operations are the ones to know about: `drop_component` and `deploy_component` are both off the set, so opting either into [`mcp.operations.allow`](../mcp/configuration.md#mcpoperationsallow) puts it in the agent's toolset where `allowDestructive: false` does not remove it and no approval gates it — and `deploy_component` writes code the Harper process then executes. Vet anything you add to that allow list on its own merits rather than assuming these two settings cover it. From 8bf8b464700373318182cf4b415511ae9494e260 Mon Sep 17 00:00:00 2001 From: David C Date: Tue, 6 Oct 2026 12:30:21 -0400 Subject: [PATCH 3/6] Name what each agent filesystem scope refuses on the operations page instead of claiming all key material Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/operations-api/operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index 1964fe4b..f70d8be4 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -1674,7 +1674,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan - `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else. - The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) -- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file, and further wherever `componentsScope` or `configScope` widens a scope (key material is refused in every [filesystem scope](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. +- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file, and further wherever `componentsScope` or `configScope` widens a scope (every scope refuses Harper's key directories, key file names, and PEM private keys; see [Filesystem scopes](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. - With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege. - Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt. - That set is an explicit list in core rather than a prefix match, and it is not a list of every damaging operation, so `allowDestructive` and `autoApprove` are not a boundary by themselves. The component operations are the ones to know about: `drop_component` and `deploy_component` are both off the set, so opting either into [`mcp.operations.allow`](../mcp/configuration.md#mcpoperationsallow) puts it in the agent's toolset where `allowDestructive: false` does not remove it and no approval gates it — and `deploy_component` writes code the Harper process then executes. Vet anything you add to that allow list on its own merits rather than assuming these two settings cover it. From 5d3f19d948e654a0626aef9c862f0a7c5bf2155d Mon Sep 17 00:00:00 2001 From: David C Date: Tue, 6 Oct 2026 13:40:19 -0400 Subject: [PATCH 4/6] Date the agent filesystem-scope change to 5.4.0, and add it to the 5.4 release notes Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/configuration/options.md | 6 +++--- reference/operations-api/operations.md | 2 +- release-notes/v5-lincoln/5.4.md | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index 37a64561..86a63966 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -398,7 +398,7 @@ agent: - `allowDestructive` — Include the tools marked destructive in the agent's toolset: `write_file`, the inspector's code-evaluation tools, and any operations tool carrying MCP's [`destructiveHint`](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). When `false` they are removed entirely rather than gated. That hint comes from a curated set in core which does not cover every damaging operation, so this is not a complete safety boundary on its own — see [Agent operations](../operations-api/operations.md#agent); _Default_: `false` - `user` — Harper user the agent's **operations** tools run as; the filesystem, HTTP, schedule, and inspector tools always run at process privilege regardless. If it cannot be resolved and it is not the default, the agent fails closed and runs with no operations tools; _Default_: `hdb_agent`, which falls back to a `super_user` bootstrap identity - `componentsScope` — The agent's `components` filesystem scope, which it reads and, with `allowDestructive`, writes; absolute or relative to `rootPath`. Read at startup only; _Default_: the full `componentsRoot` -- `configScope` — The agent's read-only `config` filesystem scope: a file or a directory, absolute or relative to `rootPath`. Read at startup only. See [Filesystem scopes](#filesystem-scopes); _Default_: the Harper config file only +- `configScope` — The agent's read-only `config` filesystem scope: a file or a directory, absolute or relative to `rootPath`. Read at startup only. See [Filesystem scopes](#filesystem-scopes); _Default_: the Harper config file only - `httpFetch` — Whether the agent has its `http_fetch` tool, and which hosts it may reach: `true`, `false`, or `{ allow: [...] }`. Read at startup only. See [Restricting `http_fetch`](#restricting-http_fetch); _Default_: `true` - `systemPromptAppend` — Operator text appended to the agent's system prompt @@ -406,7 +406,7 @@ agent: ### Filesystem scopes - + The agent's read tools (`read_file`, `list_dir`, `grep_files`, and `tail_file`) take a `root` naming one of three scopes. `write_file`, available when `allowDestructive` is on, takes no `root` and always writes to `components`. @@ -416,7 +416,7 @@ The agent's read tools (`read_file`, `list_dir`, `grep_files`, and `tail_file`) | `logs` | the log directory | read | | `config` | the Harper config file, or `configScope` when set | read | -Before v5.3.2, `config` was the directory holding `harper-config.yaml`. On a default install that is `rootPath` itself, so the agent could read `keys/`, `database/`, and the rest of the root. It is now the config file alone, and `list_dir` shows only that file. +Before v5.4.0, `config` was the directory holding `harper-config.yaml`. On a default install that is `rootPath` itself, so the agent could read `keys/`, `database/`, and the rest of the root. It is now the config file alone, and `list_dir` shows only that file. To give the agent more, such as a directory of extra configuration, set `configScope` to a file or a directory, absolute or relative to `rootPath`. It is read at startup only. A path that names no file or directory at startup leaves the `config` scope unavailable and logs an error. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index f70d8be4..caeb9476 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -1674,7 +1674,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan - `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else. - The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) -- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file, and further wherever `componentsScope` or `configScope` widens a scope (every scope refuses Harper's key directories, key file names, and PEM private keys; see [Filesystem scopes](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. +- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file (before v5.4.0, the whole directory holding it), and further wherever `componentsScope` or `configScope` widens a scope (every scope refuses Harper's key directories, key file names, and PEM private keys; see [Filesystem scopes](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. - With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege. - Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt. - That set is an explicit list in core rather than a prefix match, and it is not a list of every damaging operation, so `allowDestructive` and `autoApprove` are not a boundary by themselves. The component operations are the ones to know about: `drop_component` and `deploy_component` are both off the set, so opting either into [`mcp.operations.allow`](../mcp/configuration.md#mcpoperationsallow) puts it in the agent's toolset where `allowDestructive: false` does not remove it and no approval gates it — and `deploy_component` writes code the Harper process then executes. Vet anything you add to that allow list on its own merits rather than assuming these two settings cover it. diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index 32120367..cc0a4d19 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -23,3 +23,9 @@ A durable MQTT session now resumes through the same checked replay. When a recon ### Certifying a Release in a Canary Worker A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its release before rolling it out. The first worker started on the release, the canary, is held out of traffic until it reports whether the deployed component loaded, and the rollout goes on only if it did. Each later replacement is checked the same way. When the node that received the deploy rejects the release, the deploy fails with `400`. The release it replaced is made live again, or, with nothing to go back to, the component fails closed on that node. A new `certification` field in the response reports the outcome, and says when a restarting deploy went out unchecked (`uncertified` or `unavailable`, as for a `package: file:` deploy). With `"rolling"`, each other node then certifies the release with a canary of its own, and a node that rejects it fails the deploy's job, not the deploy: poll its `restartJobId` with `get_job` for the outcome. A node running a version before 5.4.0 activates without a canary. A deploy that restarts nothing is not test-loaded. If its release throws when it loads, `get_status` reports the failure. See [Certifying a release in a canary worker](/reference/v5/operations-api/operations#certifying-a-release-in-a-canary-worker). + +## Agent + +### The `config` Scope Is the Config File + +The built-in agent's read-only `config` filesystem scope used to be the directory holding `harper-config.yaml`. On a default install that is `rootPath`, so the agent could read `keys/`, `ssh/`, and the raw database files. It is now the config file alone, and the new `agent.configScope` widens it to a file or a directory at startup. In every scope, the agent's filesystem tools now refuse to read Harper's key directories (`/keys` and `/ssh`), files named `*.pem`, `*.key`, or `.jwtPass`, and text holding a PEM private key, and they refuse to write into the key directories. `set_agent_config` now rejects `configScope` and `componentsScope` with a `400`, as it already did `httpFetch`. An agent that read other files through `config` needs `configScope` set to keep reading them. See [Filesystem scopes](/reference/v5/configuration/options#filesystem-scopes) ([harper#3041](https://github.com/HarperFast/harper/issues/3041)). From fef8e56880402d1bde4e8dbc956e2dbb85866c8a Mon Sep 17 00:00:00 2001 From: David C Date: Tue, 6 Oct 2026 13:45:46 -0400 Subject: [PATCH 5/6] Say that agent.configScope replaces the config file as the scope rather than adding to it Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/configuration/options.md | 2 +- release-notes/v5-lincoln/5.4.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index 86a63966..d9a618e6 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -418,7 +418,7 @@ The agent's read tools (`read_file`, `list_dir`, `grep_files`, and `tail_file`) Before v5.4.0, `config` was the directory holding `harper-config.yaml`. On a default install that is `rootPath` itself, so the agent could read `keys/`, `database/`, and the rest of the root. It is now the config file alone, and `list_dir` shows only that file. -To give the agent more, such as a directory of extra configuration, set `configScope` to a file or a directory, absolute or relative to `rootPath`. It is read at startup only. A path that names no file or directory at startup leaves the `config` scope unavailable and logs an error. +To point the agent somewhere else, such as a directory of extra configuration, set `configScope` to a file or a directory, absolute or relative to `rootPath`. It replaces the config file as the scope: the config file stays readable only if the new target contains it, as `rootPath` does. It is read at startup only. A path that names no file or directory at startup leaves the `config` scope unavailable and logs an error. ```yaml agent: diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index cc0a4d19..c1fea5f8 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -28,4 +28,4 @@ A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its ### The `config` Scope Is the Config File -The built-in agent's read-only `config` filesystem scope used to be the directory holding `harper-config.yaml`. On a default install that is `rootPath`, so the agent could read `keys/`, `ssh/`, and the raw database files. It is now the config file alone, and the new `agent.configScope` widens it to a file or a directory at startup. In every scope, the agent's filesystem tools now refuse to read Harper's key directories (`/keys` and `/ssh`), files named `*.pem`, `*.key`, or `.jwtPass`, and text holding a PEM private key, and they refuse to write into the key directories. `set_agent_config` now rejects `configScope` and `componentsScope` with a `400`, as it already did `httpFetch`. An agent that read other files through `config` needs `configScope` set to keep reading them. See [Filesystem scopes](/reference/v5/configuration/options#filesystem-scopes) ([harper#3041](https://github.com/HarperFast/harper/issues/3041)). +The built-in agent's read-only `config` filesystem scope used to be the directory holding `harper-config.yaml`. On a default install that is `rootPath`, so the agent could read `keys/`, `ssh/`, and the raw database files. It is now the config file alone. The new `agent.configScope` replaces it at startup with another file or directory, which keeps the config file readable only if it contains it. In every scope, the agent's filesystem tools now refuse to read Harper's key directories (`/keys` and `/ssh`), files named `*.pem`, `*.key`, or `.jwtPass`, and text holding a PEM private key, and they refuse to write into the key directories. `set_agent_config` now rejects `configScope` and `componentsScope` with a `400`, as it already did `httpFetch`. An agent that read other files through `config` needs `configScope` set to a directory that holds them. See [Filesystem scopes](/reference/v5/configuration/options#filesystem-scopes) ([harper#3041](https://github.com/HarperFast/harper/issues/3041)). From 2f1475507dbfd46c23c9d890b6b16b505a907cbc Mon Sep 17 00:00:00 2001 From: David C Date: Wed, 7 Oct 2026 14:33:38 -0400 Subject: [PATCH 6/6] Split the agent privilege-boundary bullet into separate sentences Refs HarperFast/harper#3041 Co-Authored-By: Claude Opus 5.5 --- reference/operations-api/operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index caeb9476..e2b832cb 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -1674,7 +1674,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan - `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else. - The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) -- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`, and it checks host names, not resolved addresses: a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host, including anything private or internal the server can route to, is reachable. Reading is ungated too: `read_file` covers the component tree, the log directory, and the Harper config file (before v5.4.0, the whole directory holding it), and further wherever `componentsScope` or `configScope` widens a scope (every scope refuses Harper's key directories, key file names, and PEM private keys; see [Filesystem scopes](../configuration/options.md#filesystem-scopes)), so an enabled agent puts a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. +- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`. It checks host names, not resolved addresses, so a name that resolves to a blocked address is not caught. Since v5.3.2, it also checks every redirect hop before following it. Every other host is reachable, including anything private or internal the server can route to. Reading is ungated too. By default, `read_file` covers the component tree, the log directory, and the Harper config file. Before v5.4.0, it covered the whole directory holding the config file. `componentsScope` and `configScope` can point it elsewhere, including somewhere wider. Every scope refuses Harper's key directories, key file names, and PEM private keys. See [Filesystem scopes](../configuration/options.md#filesystem-scopes). An enabled agent therefore has a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host. - With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege. - Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt. - That set is an explicit list in core rather than a prefix match, and it is not a list of every damaging operation, so `allowDestructive` and `autoApprove` are not a boundary by themselves. The component operations are the ones to know about: `drop_component` and `deploy_component` are both off the set, so opting either into [`mcp.operations.allow`](../mcp/configuration.md#mcpoperationsallow) puts it in the agent's toolset where `allowDestructive: false` does not remove it and no approval gates it — and `deploy_component` writes code the Harper process then executes. Vet anything you add to that allow list on its own merits rather than assuming these two settings cover it.