## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Paperclip provides CLI guidance to agents and operators through documentation and runtime messages. > - Content-bearing `pnpm paperclipai` examples send arguments through a shell. > - Shell evaluation can execute command substitutions in untrusted argument content. > - Runtime hostname guidance can also place request-derived content inside a shell command. > - This pull request uses `npx paperclipai` for content-bearing guidance and uses a static hostname placeholder. > - The benefit is safer copy-paste guidance for agents and operators. ## Linked Issues or Issue Description **Issue type** Incorrect information **Where is the issue?** CLI guidance in `doc/CLI.md`, `skills/paperclip/SKILL.md`, documentation, and runtime-generated hints. **What's wrong?** Content-bearing `pnpm paperclipai` commands can pass argument text through `/bin/sh`. Shell command substitution in an argument can execute before the CLI receives the value. **Suggested fix** Use `npx paperclipai` for content-bearing commands. Use a static `<host>` placeholder when runtime guidance displays the allowed-hostname command. ## What Changed - Replace content-bearing `pnpm paperclipai` examples with `npx paperclipai` across the documentation and agent-facing guidance. - Update runtime-generated CLI hints to use a static `<host>` placeholder. - Add safety notes to `doc/CLI.md` and `skills/paperclip/SKILL.md`. - Add scans and regression tests for unsafe invocation and hostile hostname headers. - Keep fixed lifecycle commands and `pnpm --filter @paperclipai/*` build commands unchanged. ## Verification - Run `tsc --noEmit` for the changed server files. - Run `cli-invocation-safety.test.ts`. - Run `private-hostname-guard.test.ts`. - Confirm that hostile hostname headers do not enter shown shell commands. - Confirm that the three commits contain the required Paperclip co-author trailer. ## Risks - This change updates documentation and diagnostic text across many surfaces. - Fixed lifecycle and setup commands remain unchanged. - The tests fail if content-bearing `pnpm paperclipai` guidance returns. - The change does not alter the CLI argument parser. ## Model Used OpenAI Codex, GPT-5, tool use, code execution, and repository review assistance. ## 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] I have run tests locally and they pass - [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 will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
6.9 KiB
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_localruns the localhermesCLI as a child process on the Paperclip host.hermes_gatewaycalls 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, orMISTRAL_API_KEY. - Hermes gateway key: set
API_SERVER_KEYbefore starting Hermes. Paperclip stores the same value asagentDefaultsPayload.apiKeyso 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_KEYwhen 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:
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:
- Open the target company.
- Use the add-agent button in the agent sidebar.
- Generate an agent onboarding prompt/invite.
- Give the generated onboarding text to the Hermes runtime.
The UI prompt points Hermes at the same machine-readable onboarding endpoints:
GET /api/invites/:tokenGET /api/invites/:token/onboardingGET /api/invites/:token/onboarding.txtGET /api/skills/indexGET /api/skills/paperclip
For CLI-driven setup, create and inspect the invite directly:
pnpm exec paperclipai invite create --company-id <company-id> --payload-json '{"requestType":"agent"}'
pnpm exec paperclipai invite show <token>
pnpm exec paperclipai invite onboarding:text <token>
Hermes should submit a join request with requestType: "agent" and
adapterType: "hermes_gateway":
{
"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.apiBaseUrlis the Hermes gateway URL that Paperclip calls.agentDefaultsPayload.paperclipApiUrlis the Paperclip base URL that Hermes can call after approval and key claim.PAPERCLIP_API_URL/PAPERCLIP_API_KEYare injected runtime values for Hermes-originated Paperclip API calls after the agent is approved.
Approve And Claim
After Hermes submits the join request:
-
In Paperclip, review the pending agent join request.
-
Approve it from the board UI, or use:
pnpm exec paperclipai join list --company-id <company-id> --status pending_approval pnpm exec paperclipai join approve <request-id> --company-id <company-id> -
Hermes claims the one-time agent API key:
pnpm exec paperclipai join claim-key <request-id> --claim-secret <secret> -
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:
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:
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 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.txtfor the generated llm.txt-style setup instructions. - CLI invite flow:
pnpm exec paperclipai invite create,invite show,invite onboarding:text,join approve, andjoin claim-key. - Smoke helpers:
pnpm smoke:hermes-gateway-e2efor fresh-state Docker verification andpnpm smoke:hermes-gateway-joinfor an already-running gateway. - Adapter development override: Adapter manager can install
@paperclipai/hermes-paperclip-adapteras an external override, but normal operators should use the built-inhermes_localandhermes_gatewayadapters.