Add durable signed root-action approval relay - #77163
erdemkulunk wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a durable, signed “root-action approval relay” path in the gateway: webhook routes can accept narrowly-scoped, immutable root-action proposals (HMAC V2), deliver approval buttons to Telegram, and then retry a one-shot signed callback to the upstream system until acknowledged—persisting state for idempotency and restart recovery.
Changes:
- Introduces
gateway/root_action_approval.pywith an immutable proposal/decision contract and a durable approval store used across adapters. - Extends the webhook adapter to validate timestamp-bound HMAC-V2 for root-action proposals and to add durable idempotency for a managed incident delivery route.
- Extends the Telegram adapter to render root-action approval buttons and retry signed callback delivery after user decision; adds focused gateway tests for restart/idempotency behavior.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
gateway/root_action_approval.py |
New durable store + proposal/decision signing helpers for root-action approvals. |
gateway/platforms/webhook.py |
Adds root-action proposal route handling (V2 signature), profile-bound Telegram delivery, and durable managed-incident delivery bindings. |
plugins/platforms/telegram/adapter.py |
Adds root-action approval UI + durable callback retry/resume logic. |
tests/gateway/test_root_action_approval.py |
Behavioral tests for immutable proposal validation, signing, and store durability. |
tests/gateway/test_root_action_profile_delivery.py |
Tests profile-bound Telegram routing and callback retry/idempotency semantics. |
tests/gateway/test_webhook_adapter.py |
Adds managed-incident durability/idempotency tests. |
tests/gateway/test_telegram_callback_auth_fail_closed.py |
Adds test ensuring profile-bound callback auth does not fall back to global allowlists. |
web/package.json |
Pins react-router-dom to 7.18.2. |
package-lock.json |
Lockfile updates consistent with dependency bumps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _default_state_path() -> Path: | ||
| configured = os.getenv("HERMES_ROOT_ACTION_STATE", "").strip() | ||
| if configured: | ||
| return Path(configured) | ||
| try: | ||
| from hermes_constants import get_hermes_home | ||
|
|
||
| return get_hermes_home() / "root-action-approvals.json" | ||
| except Exception: | ||
| return Path.home() / ".hermes" / "root-action-approvals.json" |
| def replay_response(existing: dict) -> "web.Response": | ||
| if ( | ||
| existing.get("incident_id") != binding["incident_id"] | ||
| or existing.get("state_revision") != binding["state_revision"] | ||
| ): |
| def _done(completed: asyncio.Task, *, key: str = action_id) -> None: | ||
| self._root_action_delivery_tasks.discard(completed) | ||
| self._root_action_delivery_task_ids.discard(key) | ||
|
|
|
Release evidence (2026-08-03): fork head 2213fa4 is pinned by Pythia and passed the full hosted candidate gate, including immutable source/PEP 610 provenance, SQLite/WAL, npm high/critical, and pip-audit checks: https://github.com/erdemkulunk/pythia/actions/runs/30778323821/job/91577992558. The production scheduler remains disabled pending staged node rollout evidence. |
Closes #77161.\n\nAdds the narrow, deliver-only root-action proposal and callback contract used by Pythia automatic-update recovery. Includes HMAC-bound payloads, profile-authorized Telegram decisions, durable one-shot callback retry, exact managed-incident acknowledgements, and restart/idempotency coverage.\n\nFocused verification: 63 gateway tests passed. Full gateway run on Windows: 4,372 passed, 36 skipped, 64 unrelated platform/dependency/Windows failures.