Commit Graph
16 Commits
Author SHA1 Message Date
Henry ParkandGitHub 878a67cdb6 Refactor owner scope across channels and fix default routing fallback (#1151)
* refactor: add explicit owner scope across channels

* fix: tighten routine owner target routing

* fix: address owner scope review feedback

* Fix owner-scope onboarding and event trigger isolation

* Tighten routing fallback and wizard owner validation

* fix: address owner-scope follow-up review

* fix: tighten owner-scope follow-up details

* fix: import Channel trait in telegram test

* fix: normalize http webhook sender ids

* fix: address remaining owner-scope review issues

* fix: reconcile config rebase fallout

* fix: reconcile extension manager rebase drift

* fix: address current copilot review regressions

* fix: restore clippy matrix after rebase
2026-03-16 13:31:03 -07:00
8fb2f70258 fix: HTTP webhook secret transmitted in request body rather than via header, docs inconsistency and security concern (#1162)
Implement industry-standard HMAC-SHA256 header-based webhook authentication
to resolve issue #722. The X-Hub-Signature-256 header follows GitHub's
webhook security model, replacing the non-standard X-IronClaw-Signature header.

**Changes:**
- Rename HTTP webhook signature header from X-IronClaw-Signature to X-Hub-Signature-256
- X-Hub-Signature-256 is the standard used by GitHub, Stripe, and other webhook providers
- HMAC-SHA256 signatures continue to use sha256=<hex> format
- Body 'secret' field remains supported as deprecated fallback for backward compatibility
- All error messages and documentation updated to reflect new header name

**Security impact:**
- Signatures verified via HTTP header instead of request body
- Signature visible in Authorization header only, not logged in request body
- Follows industry best practices for webhook authentication
- Fail-closed policy: rejects requests without authentication

**Backward compatibility:**
- Requests without X-Hub-Signature-256 header fall back to 'secret' field in body (with deprecation warning)
- Deprecation path: migrate to header-based auth, body field support will be removed in a future release

**Test coverage:**

Unit tests (20 tests in src/channels/http.rs):
- 6 header-based auth tests (valid/invalid/malformed signatures, header encoding)
- 2 backward compatibility tests (deprecated body secret fallback)
- 3 error handling tests (missing auth, invalid JSON, content-type validation)
- 4 signature verification unit tests (valid digest, invalid digest, missing prefix, invalid hex)
- 5 advanced tests (concurrency, dynamic updates, header precedence, no deadlocks, runtime clearing)

E2E tests (12 tests in tests/e2e/scenarios/test_webhook.py):
- Valid HMAC-SHA256 signature acceptance
- Invalid/wrong/malformed signature rejection
- Header precedence over body secret
- Deprecated body secret backward compatibility
- Missing auth rejection (fail-closed)
- Content-Type validation
- Invalid JSON handling
- Case-insensitive header lookup
- Message queuing and processing
- Fixture for running server with HTTP_WEBHOOK_SECRET configured

All 3,033 lib tests pass with zero clippy warnings.

**Example usage after fix:**

BODY='{"content": "hello"}'
SECRET="your-webhook-secret"
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

curl -X POST http://127.0.0.1:9090/webhook \
  -H "Content-Type: application/json" \
  -H "X-Hub-Signature-256: sha256=$SIG" \
  -d "$BODY"

Co-authored-by: Claude Haiku 4.5 <[email protected]>
2026-03-14 12:01:38 -07:00
bc6725205a fix(http): replace .expect() with match in webhook handler (#1133)
* fix(http): replace .expect() with match in webhook handler

Replace `.expect("checked is_none above")` with a proper `match` on
`webhook_secret.as_ref()`. The is_none-then-expect pattern was logically
safe but violates the project rule against .expect() in production code.

Update pre-existing test to expect SERVICE_UNAVAILABLE (503) instead of
UNAUTHORIZED (401) when the secret is cleared, since the None check now
returns early before signature verification.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(ci): formatting + suppress no-panics false positive in test

- Collapse multi-line Some() to single line per rustfmt
- Add // safety: comment on test assert_eq to suppress CI grep

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-13 17:01:34 +00:00
e805ec61aa fix: 5 critical/high-priority bugs (auth bypass, relay failures, unbounded recursion, context growth) (#1083)
* fix: address 5 critical and high-priority bugs from issue tracker

- #1033: reject webhook requests when secret is cleared at runtime via
  update_secret(None), preventing auth bypass through SIGHUP hot-swap
- #908: reset consecutive_failures counter on successful SSE stream
  reconnection in relay channel, so circuit breaker counts truly
  consecutive failures
- #975: add depth limit (16) to validate_tool_schema() to prevent
  stack overflow on deeply nested schemas
- #974: add depth limit (8) to resolve_nested() to prevent stack
  overflow on deeply nested capabilities wrappers
- #826: truncate oversized tool outputs (>8KB) in routine lightweight
  loop to prevent unbounded context growth across iterations

Each fix includes a regression test.

Closes #1033, #908, #975, #974, #826

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: 5 more high-priority bugs (routine cache, job signals, input limits)

- #1077: recompute next_fire_at when re-enabling cron routines via web
  toggle, mirroring CLI behavior so cron ticker picks them up
- #1076: refresh event trigger cache after web toggle/delete operations
  so event/system_event routines reflect changes immediately
- #892: remove Stuck from check_signals() stop-states in JobDelegate
  since Stuck is recoverable (Stuck -> InProgress via self-repair)
- #976: truncate oversized description strings in CapabilitiesFile to
  4KB to prevent memory abuse from malicious capabilities files
- #977: drop oversized parameters schema JSON (>64KB) in
  CapabilitiesFile to prevent unbounded memory growth

Each fix includes regression tests where applicable.

Closes #1077, #1076, #892, #976, #977

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: prevent ReDoS in event trigger regex patterns

- #825: use RegexBuilder with 64KB size limit when compiling
  user-supplied event trigger patterns, both at creation time
  (routine tool) and at cache refresh (routine engine)

Note: Rust's regex crate already guarantees O(n) matching, so the
size limit prevents excessive memory use during compilation rather
than catastrophic backtracking at match time.

Closes #825

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Harden HTTP SSRF IP filtering

* Apply rustfmt after staging merge

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-13 16:04:55 +00:00
NigeandGitHub 1ba6a83ca4 fix(http): fail closed when webhook secret is missing at runtime (#1075) 2026-03-12 15:26:40 -07:00
ef34943c14 fix: release lock guards before awaiting channel send (#869) (#1003)
* fix: release lock guards before awaiting channel send (#869)

Clone `mpsc::Sender` out of `RwLock` before `.send().await` to prevent
read guards from blocking write lock acquisition (shutdown/start) when
the channel buffer is full.

Fixed call sites:
- src/channels/http.rs: process_message()
- src/channels/web/server.rs: chat_send_handler(), chat_approval_handler()
- src/channels/web/handlers/chat.rs: chat_send_handler(), chat_approval_handler()
- src/channels/web/ws.rs: handle_client_message() (2 sites)
- src/channels/wasm/wrapper.rs: process_emitted_messages() (2 impls, also
  scoped rate_limiter write lock per-iteration)

Includes regression test: shutdown_completes_while_process_message_blocked

Co-Authored-By: Claude Opus 4.6 <[email protected]>
(cherry picked from commit 84802e1b89aaf07ba976db20bdbfdf749edbe332)

* ci: fetch base branch before regression test check

The regression-test-check workflow failed because origin/main wasn't
available as a ref in the CI environment. actions/checkout@v4 fetches
the PR merge ref history but doesn't make the base branch ref available
for three-dot diff comparisons.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
(cherry picked from commit 1d5a7bdc8ec071cdddf0e69d6053d49ca20a2b18)

* chore(ci): rerun regression gate [skip-regression-check]

(cherry picked from commit 784d444701471a1311b1f985e2f07be6f0527abf)

---------

Co-authored-by: Umesh Kumar Singh <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-12 11:10:04 -07:00
Zaki ManianandGitHub 195ff44b1a fix(security): migrate webhook auth to HMAC-SHA256 signature header (#970) 2026-03-12 01:10:26 +00:00
873322f2fb fix: staging CI review issues (batch 1) (#883)
* fix: address staging-ci-review issues (batch 1)

- #811: Fix unreachable error handling in worker — restructure .await?
  to explicit match on nested Result so token budget errors are properly
  logged and marked as failed
- #813: Combine metadata + token budget into single update_context()
  call to prevent concurrent worker observing partial state
- #814: Persist max_tokens and total_tokens_used to both PostgreSQL and
  libSQL backends — add V12 migration, update save_job/get_job
- #815: Cap user-supplied max_tokens at configured max_tokens_per_job
  to prevent budget bypass via metadata injection
- #869: Release locks before async I/O in webhook handler (http.rs) and
  SIGHUP handler (main.rs) to prevent blocking concurrent requests

Fixes: #811, #813, #814, #815, #869

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR #883 review feedback

- Fix min(user_val, 0) bug: guard for unlimited config (max_tokens_per_job == 0)
- Remove duplicate columns from libSQL base SCHEMA (v12 migration is sole source)
- Use get_i64() helper for consistency in libsql/jobs.rs
- Add regression tests for scheduler token budget capping

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-10 14:01:07 -07:00
ebb22094a5 fix: promote to main (#878)
* fix: replace unsafe env::set_var with thread-safe inject_single_var in SIGHUP handler

Fixes race condition where SIGHUP handler modifies global environment variables
while other threads may be reading them via Config::from_env().

Changes:
- Replace unsafe { std::env::set_var() } with ironclaw::config::inject_single_var()
- Uses INJECTED_VARS mutex instead of unsafe global state modification
- All reads via optional_env() check the thread-safe overlay first
- Prevents data races between SIGHUP reload and concurrent config reads

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* fix: spawn webhook restart as background task to avoid blocking I/O across lock

Prevents holding Mutex lock during async I/O operations (TcpListener::bind,
task shutdown). The SIGHUP handler no longer blocks webhook processing during
listener restart.

Changes:
- Read old_addr and drop lock immediately
- Spawn restart_with_addr() as background task via tokio::spawn
- Lock is only held during the actual restart operation, not the signal handler

Benefits:
- SIGHUP handler returns immediately without blocking
- Webhook requests not delayed by listener restart I/O
- Lock contention significantly reduced

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* fix: add graceful shutdown mechanism for SIGHUP handler background task

Prevents unbounded loop without cancellation token. The SIGHUP handler now
listens for a shutdown signal and exits cleanly during graceful termination.

Changes:
- Create broadcast channel for shutdown signaling
- SIGHUP handler uses tokio::select! to wait for shutdown or SIGHUP
- Send shutdown signal to all background tasks after agent.run() completes
- Ensures clean task lifecycle and no orphaned background tasks

Benefits:
- Proper task cancellation during graceful shutdown
- Follows Tokio best practices for background task management
- No background tasks orphaned when runtime shuts down

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* refactor: replace stringly-typed parameter filtering with typed enum and single helper

Fixes DRY violation where unsupported parameter filtering was duplicated across
rig_adapter.rs and anthropic_oauth.rs using string contains checks.

Changes:
- Add UnsupportedParam typed enum in provider.rs (Temperature, MaxTokens, StopSequences)
- Create strip_unsupported_completion_params() helper function
- Create strip_unsupported_tool_params() helper function
- Update rig_adapter.rs to use shared helpers
- Update anthropic_oauth.rs to use shared helpers
- Replace 60+ lines of duplicate stringly-typed logic

Benefits:
- Type safety: parameter names checked at compile time
- Single source of truth: adding a new param updates one place
- Reduced maintenance burden: no duplicate logic to keep in sync
- Better code clarity: named enum variant is self-documenting

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* docs: clarify intentional parameter asymmetry between completion and tool requests

Add documentation explaining why strip_unsupported_tool_params does not handle
StopSequences: the field doesn't exist in ToolCompletionRequest.

Changes:
- Add clarifying comments to strip_unsupported_tool_params()
- Explain why StopSequences is only in CompletionRequest
- Note that ToolCompletionRequest only supports Temperature and MaxTokens
- Inline comment confirms no action needed for StopSequences

This addresses the appearance of incomplete implementation without changing logic,
as the asymmetry is intentional and correct (ToolCompletionRequest lacks the field).

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* perf: isolate webhook_secret to reduce lock contention on hot path

Move webhook_secret from shared HttpChannelState RwLock into its own Arc<RwLock<>>.
This eliminates contention between secret validation and other state operations.

Changes:
- Change webhook_secret field type from RwLock<Option<SecretString>> to Arc<RwLock<Option<SecretString>>>
- Update initialization in HttpChannel::new()
- Update comments to explain isolation rationale

Benefits:
- Reduce lock contention on webhook request hot path (secret validation)
- Rarely-changing field (SIGHUP only) isolated from frequent state accesses
- Other state operations (tx, pending_responses) no longer wait behind secret reads
- Minimal code change: only field declaration and initialization

The Arc wrapper allows cloning the RwLock handle to separate concerns. With this
change, every webhook request acquires its own isolated lock for secret validation,
not the shared HttpChannelState lock. This scales better under high request volume.

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* fix: prevent partial state corruption on SIGHUP restart failure

Ensure atomicity of configuration reload: if webhook listener restart fails,
secret update is skipped to prevent inconsistent state.

Changes:
- Wait for restart_with_addr() to complete (don't spawn background task)
- Track restart result with restart_failed flag
- Only update secret if restart succeeded or wasn't needed
- Ensure listener and secret stay synchronized

Problem addressed:
- Before: restart spawned as background task, secret updated immediately
- If restart failed, secret was changed but listener still on old address
- This left system in inconsistent state (partial corruption)

Solution:
- Make restart blocking (SIGHUP handler can wait, it's not on request hot path)
- Atomically update secret only after successful restart
- Flag prevents race between restart and secret update

Benefits:
- Configuration changes are atomic (both succeed or both fail together)
- No partial state corruption on restart failure
- Failed restarts don't silently leave inconsistent state
- Secret and listener address stay in sync

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* refactor: generalize hot-secret-swapping with ChannelSecretUpdater trait

Decouple SIGHUP handler from HTTP channel internals by introducing a trait
for channels that support zero-downtime secret updates.

Changes:
- Add ChannelSecretUpdater trait in channels/channel.rs
- Implement ChannelSecretUpdater for HttpChannelState
- Export trait from channels module
- Update SIGHUP handler to use trait-based secret updater collection
- Replace explicit HTTP channel knowledge with generic updater loop

Benefits:
- SIGHUP handler no longer depends on HttpChannelState details
- Tight coupling removed: main.rs doesn't need HTTP channel imports
- Extensible: new channels can opt-in by implementing the trait
- Scalable: multiple channels supported without main.rs changes
- Maintainable: adding channels requires only trait implementation, not SIGHUP handler edits

Pattern:
- ChannelSecretUpdater trait defines the interface for all updaters
- Channels that support hot-secret-swapping implement the trait
- SIGHUP handler loops through all registered updaters generically

Verification:
- All 2,787 tests pass
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* feat: validate parameter names at deserialization time, not just tests

Add custom serde deserializer for unsupported_params that validates parameter
names at runtime when loading providers.json (or user overrides).

Changes:
- Add unsupported_params_de module with custom deserializer
- Only allows: "temperature", "max_tokens", "stop_sequences"
- Invalid parameter names cause immediate deserialization error
- Update ProviderDefinition to use custom deserializer
- Enhanced test with explicit parameter name validation
- Add new test that verifies invalid parameters are rejected

Problem solved:
- Before: Invalid param names (e.g., "temperrature") silently ignored
- Now: Rejected at deserialization time with clear error message
- Prevents runtime failures caused by typos in configuration

Example error:
  unsupported parameter name 'temperrature': must be one of: temperature, max_tokens, stop_sequences

Benefits:
- Fail-fast: errors caught when loading config, not at runtime
- Clear feedback: error message lists valid parameter names
- Type safety: validators run during deserialization
- Configuration errors detected immediately, not silently ignored

Verification:
- All 2,788 tests pass (including new validation test)
- Zero clippy warnings
- Code compiles successfully

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

---------

Co-authored-by: Claude Haiku 4.5 <[email protected]>
2026-03-10 11:15:49 -07:00
Nick PismenkovandGitHub f8c56727c6 fix: Channel HTTP: server doesn't start after config change (no hot-r… (#779)
* fix: Channel HTTP: server doesn't start after config change (no hot-reload)

* review fixes

* review fixes

* fix linter

* fix code style
2026-03-10 08:11:21 -07:00
553c306c52 feat: full image support across all channels (#725)
* feat: full image support across all channels

End-to-end image handling: upload, generation, analysis, editing, and
rendering across web gateway, HTTP webhook, WASM (Telegram/Slack), and
REPL channels. Builds on the attachment infrastructure from #596 and
draws inspiration from PR #641's image pipeline approach — credit to
that PR's author for the sentinel JSON pattern and base64-in-JSON
upload design.

Key changes:
- Image upload in web UI (file picker, paste, preview strip)
- Image generation tool (FLUX/DALL-E via /v1/images/generations)
- Image edit tool (multipart /v1/images/edits with fallback)
- Image analysis tool (vision model for workspace images)
- Model detection utilities (image_models.rs, vision_models.rs)
- Sentinel JSON detection in dispatcher for generated image rendering
- StatusUpdate::ImageGenerated → SSE/WS/REPL/WASM broadcast
- HTTP webhook attachment support (base64, 5MB/file, 10MB total)
- WASM channel image download (Telegram via file API, Slack via host HTTP)
- Tool registration wiring in app.rs

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR #725 review comments (16 issues)

- SecretString for API keys in all image tools (image_gen, image_edit, image_analyze)
- Binary image read via tokio::fs::read instead of DB-backed workspace.read()
- Replace Arc<Workspace> with Option<PathBuf> base_dir (workspace has no filesystem API)
- ApprovalRequirement::UnlessAutoApproved for cost-sensitive image tools
- Scope sentinel detection to image_generate/image_edit tool names only
- Skip ToolResult preview broadcast for image sentinels (avoids multi-MB base64 in SSE)
- Extract shared media_type_from_path() to builtin/mod.rs
- Rename fallback_chat_edit → fallback_generate with tracing::warn
- Increase gateway body limit from 1MB to 10MB for image uploads
- Increase webhook body limit to 15MB (base64 overhead)
- Log warning on invalid base64 in images_to_attachments
- Client-side image size limits (5MB/file, 5 images max) in app.js
- aria-label on attach button for accessibility
- Update body_too_large test for new 10MB limit

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: add Slack file size check before download (PR review item #15)

Skip downloading files larger than 20 MB in the Slack WASM channel to
avoid excessive memory use and slow downloads in the WASM runtime.
Logs a warning when a file is skipped. Also bumps channel versions
for Slack and Telegram (prior branch changes).

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(security): add path validation and approval requirement to image tools

Add sandbox path validation via validate_path() to both ImageAnalyzeTool
and ImageEditTool to prevent path traversal attacks that could exfiltrate
arbitrary files through external vision/edit APIs. Also fix
ImageAnalyzeTool::requires_approval to return UnlessAutoApproved,
consistent with ImageEditTool and ImageGenerateTool.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: post-download size guards and empty data_url sentinel check

- Slack: add post-download size check on actual bytes when metadata
  size_bytes is absent, preventing bypass of the 20MB limit
- Telegram: add 20MB download size limit (matching Slack) enforced
  in download_telegram_file() after receiving response bytes
- Dispatcher: skip broadcasting ImageGenerated SSE event when
  data_url is empty from unwrap_or_default(), log warning instead

Closes correctness issues #3, #4, #5 from PR #725 review.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: use mime_guess for media type detection, add alt attrs and media_type validation

- Replace hardcoded media type mapping with mime_guess crate (already in deps)
- Add alt attributes to img elements in web UI for accessibility
- Validate media_type starts with "image/" in images_to_attachments()
- Update bmp test assertion to match mime_guess behavior

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Zaki <[email protected]>
2026-03-09 03:41:27 +00:00
e42b1e5ec1 fix: Network Security Findings (#201)
* docs(security): add network security reference for all listeners

Catalogs every network-facing surface (web gateway, webhook server,
orchestrator API, OAuth callback, sandbox proxy) with auth mechanisms,
bind addresses, egress controls, known findings, and a review checklist
for PRs that touch network-facing code.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(security): address three network security findings

- Use constant-time comparison (ct_eq) for webhook secret validation,
  matching the pattern in web gateway and orchestrator auth
- Add X-Content-Type-Options and X-Frame-Options security headers to
  the web gateway via SetResponseHeaderLayer
- Warn at startup when HTTP webhook server binds to 0.0.0.0
- Update NETWORK_SECURITY.md to mark findings 1, 4, 5 as resolved

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(security): address PR #201 review findings

- Reorder web gateway layers so security headers (X-Content-Type-Options,
  X-Frame-Options) are outermost and apply to all responses including
  DefaultBodyLimit 413 rejections
- Move 0.0.0.0 warning to final bind address resolution so it fires for
  WASM-only webhook servers that fall back to the default address
- Add webhook handler auth tests: correct secret -> 200, wrong secret
  -> 401, missing secret -> 401
- Rewrite NETWORK_SECURITY.md: replace brittle line-number references
  with function/struct name anchors, add threat model section, document
  graceful shutdown per listener, fill content gaps (health endpoint
  responses, content-type validation, CSRF analysis, WS auth flow, MCP
  trust boundary, orchestrator rate limiting), change findings F-4/F-5
  from "Resolved" to "Mitigated" with caveats

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* style: fix rustfmt and clippy warnings from main merge

Fix formatting in llm/mod.rs and llm/rig_adapter.rs introduced by
PR #132, and collapse nested if in rig_adapter.rs per clippy.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-02-19 22:01:57 +00:00
Illia PolosukhinandClaude Opus 4.6 9d156411fc Unify webhook servers into single WebhookServer
Replace the dual-server architecture (HttpChannel + WasmChannelServer both
competing for port 8080) with a single WebhookServer that composes route
fragments from all sources. Channels define routes but never spawn servers.

- Add WebhookServer struct that collects Router fragments and binds one listener
- Extract routes() from HttpChannel, remove server-spawning from start/shutdown
- Delete WasmChannelServer (keep WasmChannelRouter and route builder)
- Rewire main.rs to compose all webhook routes into one server

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-06 11:12:17 -08:00
Illia Polosukhin 3e6dfb8409 Addressing vareity of security issues 2026-02-05 10:40:24 -08:00
Illia Polosukhin 7210470544 Wiring more 2026-02-03 10:08:06 -08:00
Illia Polosukhin 8c38566378 Initial implementation of the agent framework 2026-02-02 20:41:05 -08:00