chore: promote staging to staging-promote/71f41dd1-23309993684 (2026-03-19 19:18 UTC)#1425
Conversation
…requests (#1257) The HTTP tool returned `ApprovalRequirement::Always` for requests with credentials, but `Always` is hardcoded to ignore the session auto-approve set. This meant users who clicked "always" were re-prompted on every subsequent HTTP call — the UI offered "always" but the backend ignored it. Two fixes: 1. HTTP credentialed requests now return `UnlessAutoApproved` instead of `Always`, so the session auto-approve set is respected. 2. `StatusUpdate::ApprovalNeeded` now carries `allow_always: bool`. All channel UIs (Telegram, Slack, Signal, REPL, Web) conditionally hide the "always" option when a tool truly requires per-invocation approval (`ApprovalRequirement::Always`, e.g. destructive shell commands). Also boxes `PendingApproval` in `AgenticLoopResult::NeedApproval` to fix a pre-existing clippy `large_enum_variant` warning. Regression tests included (test_credentialed_requests_respect_auto_approve, test_allow_always_matches_approval_requirement) but CI heuristic cannot detect them in cross-fork PR diffs. [skip-regression-check] Co-authored-by: Tyler <tbond@tbond-m5.local>
* feat: receive relay events via webhook callbacks instead of SSE Replace the SSE pull model with push-based webhook callbacks from channel-relay. Eliminates the reconnect loop, stream token auth, and SSE parser — events arrive via HTTP POST to /relay/events. - Add webhook handler with HMAC signature verification - Simplify RelayChannel to use mpsc from webhook handler - Remove SSE connect/reconnect/parse logic from RelayClient - Add register_callback() to RelayClient for callback URL registration - Update activation flow to create event channel and register callback - Wire relay webhook endpoint into web gateway * fix: address review feedback on webhook callback PR - Return 503 when relay event channel is full/closed (enables retry) - Reject malformed timestamps with 400 instead of proceeding - Allow relay activation without settings store (no-store/ephemeral mode) - Check installed_relay_extensions set in is_relay_channel for no-db mode - Fix staging test constructors for new RelayChannel signature * security: adapt relay client to new channel-relay auth model Adapts the relay integration to the hardened channel-relay security model: - Switch from X-API-Key header to Authorization: Bearer sk-agent-* for all relay API calls (chat-api token verification) - Remove register_callback() — PUT /callbacks endpoint removed - Remove event_callback_url from initiate_oauth() — parameter removed - Make signing_secret a required field in RelayConfig (new env var: CHANNEL_RELAY_SIGNING_SECRET) - Update integration tests for Bearer auth and removed endpoints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: use server-side approval tokens, remove caller-supplied routing - Approval flow now calls POST /approvals to register server-side record, then embeds only the opaque approval_token in button value - Remove instance_id parameter from proxy_provider() — channel-relay no longer accepts it (uses verified identity) - Remove instance_id and user_id from initiate_oauth() — channel-relay derives them from the Bearer token - Add create_approval() to RelayClient Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pass webhook_url during OAuth so callback_url is set on connection The channel-relay OAuth flow now accepts webhook_url to set the callback_url during connection creation. IronClaw computes its webhook URL from callback_base + webhook_path and passes it during initiate_oauth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: remove webhook_url from OAuth initiation Channel-relay now derives the callback URL from chat-api's instance_url. IronClaw no longer supplies webhook_url during OAuth — the relay is the authority on where events get delivered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: cargo fmt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: remove all URL params from OAuth initiation IronClaw no longer supplies any URLs to channel-relay. The relay derives all URLs from the trusted instance_url in chat-api. initiate_oauth() takes no parameters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore CSRF nonce for OAuth callback validation Re-add nonce generation and secret storage in auth_channel_relay. The nonce is passed to channel-relay as state_nonce param (not a URL). Channel-relay embeds it in the signed state and appends it to the redirect URL so IronClaw's callback handler can validate and activate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: per-instance callback signing secrets relay_signing_secret() now prefers OPENCLAW_GATEWAY_TOKEN (per-instance) over the shared CHANNEL_RELAY_SIGNING_SECRET. A compromised instance can no longer forge callbacks to other instances on the same relay. CHANNEL_RELAY_SIGNING_SECRET is now optional in RelayConfig. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: clean per-instance callback secrets, no shared secrets, no fallbacks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pass team_id to get_signing_secret for workspace-scoped lookup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security: remove sender_id from create_approval — relay derives it Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove stale relay sender_id validation * fix: harden relay webhook activation lifecycle --------- Co-authored-by: Pierre <pierre@near.ai> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code reviewNo issues found. This PR implements two well-integrated changes:
All code follows CLAUDE.md guidelines: no unwrap()/expect() in production code, proper error context mapping, and strong type usage. Configuration simplification removes SSE polling parameters cleanly. Test cases are comprehensive and updated appropriately. |
Code review - Architecture findingsFound 7 architectural improvement opportunities (non-blocking):
Non-issues: No production .unwrap()/.expect() violations. All error types use thiserror. Bearer auth strategy is consistent. |
Code review - Security & Safety findingsCompleted security analysis. No critical vulnerabilities found. [MEDIUM:65] Timestamp replay window for webhook callbacks
[MEDIUM:50] Race condition window in relay event sender access
[LOW:40] Sensitive bytes in Arc could expose in debug panics
[MEDIUM:50] Validation assumption on cached signing secret
Verified Strengths: Conclusion: Webhook refactoring is well-implemented with no blocking security issues. Replay protection is a design consideration rather than a bug. |
Code review - Performance & Production findingsFound 6 performance/production considerations: [HIGH:80] Synchronous mutex in async hot path
[HIGH:75] Lock contention on event sender per-request
[MEDIUM:65] Channel capacity mismatch between test and production
[MEDIUM:50] Unnecessary cloning of 32-byte signing secret
[LOW:60] Lost resilience in relay service recovery
[LOW:40] Approval tuple repeatedly extracted for channel updates
Positive notes: No unbounded loops, missing timeouts, or resource leaks detected. Event channel sized appropriately at 64-event buffer. Bearer auth prevents token injection attacks in headers. Summary: 2 high-priority async/concurrency improvements recommended to prevent bottlenecks under sustained webhook traffic. Remaining findings are minor optimizations. |
Auto-promotion from staging CI
Batch range:
71f41dd12363497372864bc6eb3f7c334e05fd52..52ca9d6588f31fc9b6007c56ed7cd1995d5ad0dfPromotion branch:
staging-promote/52ca9d65-23312673755Base:
staging-promote/71f41dd1-23309993684Triggered by: Staging CI batch at 2026-03-19 19:18 UTC
Commits in this batch (2):
Current commits in this promotion (2)
Current base:
staging-promote/71f41dd1-23309993684Current head:
staging-promote/52ca9d65-23312673755Current range:
origin/staging-promote/71f41dd1-23309993684..origin/staging-promote/52ca9d65-23312673755Auto-updated by staging promotion metadata workflow
Waiting for gates:
Auto-created by staging-ci workflow