Files
PaperClipAI/doc/HERMES_GATEWAY_ONBOARDING.md
T
Nicky LeachandPaperclip fdb9a4880d fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip provides CLI commands and guidance for operators and
agents
> - The `pnpm paperclipai` script can pass argument values through a
shell
> - Shell re-parsing can execute command substitutions inside quoted
values
> - This pull request routes guidance through inert-argv `npx
paperclipai` commands and adds regression coverage
> - The benefit is safer operator guidance across documentation and
runtime hints

## Linked Issues or Issue Description

This pull request fixes a command-injection-class defect in Paperclip
CLI guidance.

**What happened?**

The `pnpm paperclipai <sub> --flag "$VALUE"` form can re-parse argument
values through a shell. A command substitution inside a quoted value can
execute on the host.

**Expected behavior**

Paperclip guidance must pass CLI values as inert argument values.
Host-derived values must not appear in copyable commands.

**Steps to reproduce**

1. Run a Paperclip guidance command that uses the `pnpm paperclipai`
script.
2. Provide a quoted value that contains a command substitution.
3. Observe that the shell can evaluate the substitution before the CLI
starts.
4. Compare the result with the `npx paperclipai` form.

**Paperclip version or commit**

`5670984b75d109950c968542a0111ebb6967f4da`

**Deployment mode**

All deployment modes that show or use the affected CLI guidance.

**Installation method**

Built from source and installed CLI guidance.

**Agent adapter(s) involved**

Not adapter-specific (core bug).

**Database mode**

Not database-related.

**Access context**

Both.

**Additional context**

The earlier merged PR
[#11343](https://github.com/paperclipai/paperclip/pull/11343) used the
unsafe `pnpm exec paperclipai` form. This fresh PR replaces that
guidance with the safe `npx paperclipai` form.

## What Changed

- Standardize documentation and runtime hints on `npx paperclipai`.
- Remove the broken `pnpm exec paperclipai` guidance.
- Use a static `<host>` placeholder in private-hostname guidance.
- Add regression tests for unsafe forms, continued lines, static hosts,
and offline guidance.

## Verification

- `git diff --check
origin/master...origin/fix/paperclipai-cli-npx-safe-invocation` passes.
- The branch adds `server/src/__tests__/cli-invocation-safety.test.ts`
and updates private-hostname tests.
- CI must run the new tests, typecheck, lint, and build checks.
- Local Vitest execution was not available because this worktree has no
installed Vitest binary.

## Risks

- The change affects operator and agent documentation text.
- The runtime hints now show `<host>` instead of a request-derived host
value.
- No database schema or migration changes exist.
- CI will detect any missed unsafe invocation or type error.

## Model Used

OpenAI GPT-5, exact model ID `gpt-5`, with tool use and code-review
assistance. The model used repository inspection, Git operations, and PR
preparation.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] CI ran the test suites and they pass; local test execution was
unavailable in this worktree
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I addressed all Greptile and reviewer comments before requesting
merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-14 22:11:16 -07:00

194 lines
6.9 KiB
Markdown

# Hermes Gateway Onboarding
Use this guide when a Hermes runtime should join Paperclip as an external
`hermes_gateway` employee. This mirrors the OpenClaw gateway invite path, but
Hermes uses the generic agent invite/onboarding flow instead of the
OpenClaw-specific invite prompt endpoint.
## Choose The Adapter
Paperclip ships both Hermes adapters as built-ins:
- `hermes_local` runs the local `hermes` CLI as a child process on the
Paperclip host.
- `hermes_gateway` calls an already-running Hermes API server over HTTP/SSE.
No Adapter manager installation is required for normal use. Adapter manager is
only needed when you intentionally install an external
`@paperclipai/hermes-paperclip-adapter` package to override or shadow a built-in
adapter while developing the Hermes package. If the external override is paused
or removed, Paperclip restores the built-in `hermes_local` / `hermes_gateway`
adapter.
## Required Credentials
Keep these credentials distinct:
- Hermes inference provider key: set at least one provider key for Hermes, such
as `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`,
`GEMINI_API_KEY`, `GOOGLE_API_KEY`, or `MISTRAL_API_KEY`.
- Hermes gateway key: set `API_SERVER_KEY` before starting Hermes. Paperclip
stores the same value as `agentDefaultsPayload.apiKey` so it can call Hermes.
- Paperclip agent key: created after the board approves the join request and
claimed once by the Hermes agent. Hermes uses this key as
`PAPERCLIP_API_KEY` when it calls Paperclip.
Do not reuse the Hermes gateway key as the Paperclip agent key. The Hermes
gateway key authenticates Paperclip-to-Hermes traffic; the claimed Paperclip key
authenticates Hermes-to-Paperclip traffic.
## Start Hermes Gateway
Install and configure Hermes first:
```sh
pip install hermes-agent
export OPENROUTER_API_KEY='<provider-key>'
export API_SERVER_KEY='<random-gateway-key>'
API_SERVER_ENABLED=true hermes gateway run --replace --accept-hooks
```
The default Hermes API server port is `8642`. For local loopback testing,
Paperclip can usually store `http://127.0.0.1:8642` as the gateway URL. For
Docker, LAN, tailnet, or reverse-proxy setups, use a URL reachable by the
Paperclip server process.
Plain HTTP is accepted for loopback. Non-loopback HTTP is denied by default in
the join flow; use HTTPS for real remote gateways. For private local
development only, the join payload can set
`dangerouslyAllowInsecureRemoteHttp: true`, and the smoke scripts expose the
same escape hatch as `HERMES_GATEWAY_ALLOW_INSECURE_HTTP=1`.
## Invite From Paperclip
In the board UI:
1. Open the target company.
2. Use the add-agent button in the agent sidebar.
3. Generate an agent onboarding prompt/invite.
4. Give the generated onboarding text to the Hermes runtime.
The UI prompt points Hermes at the same machine-readable onboarding endpoints:
- `GET /api/invites/:token`
- `GET /api/invites/:token/onboarding`
- `GET /api/invites/:token/onboarding.txt`
- `GET /api/skills/index`
- `GET /api/skills/paperclip`
For CLI-driven setup, create and inspect the invite directly:
```sh
npx paperclipai invite create --company-id <company-id> --payload-json '{"requestType":"agent"}'
npx paperclipai invite show <token>
npx paperclipai invite onboarding:text <token>
```
Hermes should submit a join request with `requestType: "agent"` and
`adapterType: "hermes_gateway"`:
```json
{
"requestType": "agent",
"agentName": "Hermes Gateway Engineer",
"adapterType": "hermes_gateway",
"capabilities": "Hermes gateway agent with code, browser, web, and file tools.",
"agentDefaultsPayload": {
"apiBaseUrl": "http://127.0.0.1:8642",
"apiKey": "<same-value-as-API_SERVER_KEY>",
"paperclipApiUrl": "http://127.0.0.1:3100",
"sessionKeyStrategy": "issue"
}
}
```
Important URL roles:
- `agentDefaultsPayload.apiBaseUrl` is the Hermes gateway URL that Paperclip
calls.
- `agentDefaultsPayload.paperclipApiUrl` is the Paperclip base URL that Hermes
can call after approval and key claim.
- `PAPERCLIP_API_URL` / `PAPERCLIP_API_KEY` are injected runtime values for
Hermes-originated Paperclip API calls after the agent is approved.
## Approve And Claim
After Hermes submits the join request:
1. In Paperclip, review the pending agent join request.
2. Approve it from the board UI, or use:
```sh
npx paperclipai join list --company-id <company-id> --status pending_approval
npx paperclipai join approve <request-id> --company-id <company-id>
```
3. Hermes claims the one-time agent API key:
```sh
npx paperclipai join claim-key <request-id> --claim-secret <secret>
```
4. Store the claimed Paperclip key in Hermes runtime state or secrets. The claim
secret and claimed key are sensitive and should not be pasted into issue
comments, logs, or prompt text.
Once the key is claimed, create an issue assigned to the new Hermes gateway
agent and wake it through the normal Paperclip heartbeat path.
## Local Fresh-State Smoke
For a fresh Docker-backed Hermes gateway and end-to-end Paperclip join/run
verification, use:
```sh
PAPERCLIP_API_URL=http://127.0.0.1:3100 \
PAPERCLIP_AUTH_HEADER='Bearer <board-token>' \
pnpm smoke:hermes-gateway-e2e
```
The E2E smoke:
- builds a fresh Hermes gateway container
- seeds a minimal non-secret Hermes model config
- passes provider keys from the host environment without printing them
- verifies Hermes `/health`, `/v1/capabilities`, `/v1/runs`, SSE, and stop
- creates and approves a Paperclip agent-only invite
- joins as `hermes_gateway`
- wakes the agent on a smoke issue
- removes Paperclip and Docker test state on success
If a Hermes gateway is already running and you only need to validate the invite
and stored adapter config, use the join-only helper:
```sh
API_SERVER_ENABLED=true API_SERVER_KEY='<gateway-key>' hermes gateway run --replace --accept-hooks
PAPERCLIP_API_URL=http://127.0.0.1:3100 \
PAPERCLIP_AUTH_HEADER='Bearer <board-token>' \
HERMES_GATEWAY_API_BASE_URL=http://127.0.0.1:8642 \
HERMES_GATEWAY_API_KEY='<gateway-key>' \
pnpm smoke:hermes-gateway-join
```
See [HERMES_GATEWAY_SMOKE.md](./HERMES_GATEWAY_SMOKE.md) for Docker Desktop,
Linux, same-network Docker, LAN/private-network, and reverse-proxy/TLS examples.
## Install Entry Points
Use these entry points depending on who is driving setup:
- Board UI: add-agent button in the agent sidebar, then generate the agent
onboarding prompt.
- Invite API: `GET /api/invites/:token/onboarding.txt` for the generated
llm.txt-style setup instructions.
- CLI invite flow: `npx paperclipai invite create`, `invite show`,
`invite onboarding:text`, `join approve`, and `join claim-key`.
- Smoke helpers: `pnpm smoke:hermes-gateway-e2e` for fresh-state Docker
verification and `pnpm smoke:hermes-gateway-join` for an already-running
gateway.
- Adapter development override: Adapter manager can install
`@paperclipai/hermes-paperclip-adapter` as an external override, but normal
operators should use the built-in `hermes_local` and `hermes_gateway`
adapters.