fix(api): scope runtime env to agent runs - #81976
Conversation
|
Security follow-up at b10f62e closes the local background/PTY gap found in independent review. Both process_registry spawn paths now use the authoritative request-scoped child environment, so they receive the current scoped token and cannot inherit stale ambient PAPERCLIP_* identity. Focused API, snapshot, local environment, foreground/background/PTY, and redaction verification: 217 passed. |
|
Security follow-up at db028c0 addresses two additional independent-review findings. Exact run secrets are now captured on queued log records before asynchronous formatting crosses into the QueueListener thread, preventing opaque token leakage to file logs. Reserved environment names are stripped case-insensitively from inherited environments and both current and legacy shell snapshots, preventing mixed-case aliases on Windows. Regression tests were written red-first. Updated focused verification: 247 passed; Ruff, py_compile, and git diff checks passed. |
|
Security follow-up at db028c0 addresses the remaining ad0e4e4 review findings. Queued log records now carry request-local exact redactions across the QueueListener thread boundary. Reserved Paperclip names are stripped case-insensitively from inherited child environments and from both legacy and newly written shell snapshots. Regression tests were written red-first for the async log leak, mixed-case ambient inheritance, and legacy snapshot alias. Updated focused verification: 247 passed; Ruff, py_compile, and git diff checks passed. |
|
Further security review follow-up is pushed at db028c0. Exact opaque redactions are now captured on queued log records before formatting crosses into the QueueListener thread. Reserved PAPERCLIP_* names are stripped case-insensitively from inherited local environments and from legacy/shared snapshot source and dump paths. Regression tests cover asynchronous logging, mixed-case ambient aliases, and a pre-existing mixed-case snapshot. Focused verification: 247 passed; Ruff, py_compile, and git diff checks passed. |
|
Final-review follow-up at ad60a51 closes the background-output leak. Local pipe and PTY sessions now capture their originating exact-redaction tuple and perform boundary-safe streaming replacement before output reaches buffers, live sinks, watch events, completion events, process responses, or later agent prompts. Tests split opaque tokens across chunks and verify pipe output, PTY output, watch_match, notify_on_complete, and live output contain only the exact-redaction sentinel. Exact replacement also now precedes generic masking in run status/output handling. Updated focused verification: 249 passed; Ruff, py_compile, and git diff checks passed. |
|
This was generated by AI during triage. Summary: Problems:
Solution: Checked against |
|
Final-review follow-up at 0cdf71f closes the remaining db028c0 findings. POST /v1/runs now rejects non-object JSON with a controlled 400. Local managed pipe and PTY spawns now use a non-login, non-interactive shell ( |
|
Full current-head focused verification completed at 0cdf71f: 252 passed. Ruff, py_compile, and git diff checks also pass. |
|
Follow-up at 1fc4a65 closes the remaining db028c0 review findings. Managed local background and PTY commands now use non-login, non-interactive shells, while unscoped terminal runs retain the existing -lic behavior. BASH_ENV and ENV are stripped case-insensitively for managed scopes so non-interactive startup hooks cannot reintroduce or overwrite Paperclip credentials. Non-object /v1/runs JSON now returns a controlled HTTP 400. Real-shell tests cover empty and populated scopes, canonical and mixed-case stale startup exports, pipe and PTY argv selection, BASH_ENV, and preservation of unmanaged login-shell behavior. Current focused verification: 252 passed; Ruff, py_compile, and git diff checks passed. |
Addresses review finding on PR NousResearch#81976. The api-server.md page now documents the runtime_env parameter accepted by POST /v1/runs: - Accepts either environment or runtime_env (mutually exclusive) - Only eight PAPERCLIP_* keys permitted - Must be object with string values - NUL bytes rejected - 32 KiB size cap - All validation error messages documented Includes two request examples showing typical usage.
|
Review finding addressed. The API server documentation at website/docs/user-guide/features/api-server.md now documents the runtime_env contract:
The documentation follows the existing page style with a prose paragraph followed by request examples. |
|
Documentation updated. The POST /v1/runs section now describes the runtime_env parameter contract, including:
Two examples added showing typical Paperclip orchestration usage. |
|
Documentation updated. The |
|
Documentation updated. Added a single paragraph describing the runtime_env contract at line 358 of api-server.md with two clear examples. All requirements from the review finding have been addressed: the eight permitted keys, mutual exclusivity with environment, validation rules, error messages, and the 32 KiB size cap. |
What does this PR do?
Adds a request-scoped environment contract to
POST /v1/runs. The endpoint accepts only eight explicit Paperclip runtime keys throughenvironmentorruntime_env. It rejects ambiguous dual-field requests, malformed values, and oversized payloads. Accepted values are stored in aContextVarfor one run and merged into local foreground, background, and PTY tool subprocesses without changingos.environ.This fixes authenticated control-plane calls from a Paperclip-triggered Hermes run. The bearer stays outside prompts and process-global state. Exact-value redaction covers tool output, progress events, errors, status, logs, and final results. Credentialed runs suppress token-unsafe streaming deltas and emit the fully assembled redacted completion.
Related Issue
No public Hermes issue exists. This is the Hermes half of the coordinated fix in paperclipai/paperclip#10625.
Type of Change
Changes Made
gateway/platforms/api_server.py.environmentfield and the explicitruntime_envspelling, but never both.gateway/runtime_context.py.PAPERCLIP_*credentials cannot fall through into a managed run.tools/environments/local.py.tools/process_registry.py.BASH_ENV/ENVstartup hooks so startup files cannot read or override request-scoped credentials; ordinary unscoped terminal behavior remains unchanged./v1/runsJSON bodies with a controlled HTTP 400.How to Test
/home/agent/.hermes/hermes-agent/venv/bin/python -m pytest -q tests/test_hermes_logging.py tests/tools/test_process_registry.py tests/gateway/test_api_server_runs.py tests/tools/test_snapshot_session_id_leak.py tests/tools/test_local_env_session_leak.py tests/tools/test_terminal_env_bridge.py tests/agent/test_redact.pywithPYTHONPATH=../v1/runsrequests with different allowlisted values and confirm each tool subprocess sees only its own values.LocalEnvironmentacross two scoped runs and confirm its shell snapshot contains noPAPERCLIP_API_KEY.Checklist
Code
pytest tests/ -qand all tests passDocumentation & Housekeeping
cli-config.yaml.exampleis not applicable because this adds no config keyCONTRIBUTING.mdandAGENTS.mdare not applicableScreenshots / Logs
Focused verification at commit
1fc4a65a179acae44c641e94969ebbfac7f8acf1:py_compilepassed for all changed Python files.git diff --checkpassed.The full repository test suite was not run. The focused set covers the changed API, request context, local subprocess bridge, shell snapshot behavior, and redaction paths.
Deployment note: deploy this Hermes receiver before enabling paperclipai/paperclip#10625. An older Hermes gateway can ignore the new
environmentfield, so sender-first rollout would create runs without the intended credential.Residual limitation: a subprocess explicitly launched into the background can outlive the request context and retain the short-lived credential until that process exits or the Paperclip JWT expires.