Skip to content

feat: wire up unified webhook runtime (Phase 2)#1171

Merged
chaodu-agent merged 11 commits into
mainfrom
feat/unified-webhook-runtime
Jun 22, 2026
Merged

feat: wire up unified webhook runtime (Phase 2)#1171
chaodu-agent merged 11 commits into
mainfrom
feat/unified-webhook-runtime

Conversation

@chaodu-agent

@chaodu-agent chaodu-agent commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements all 4 tasks from tracking issue #1170 (workspace restructure follow-up):

  1. Phase 2: Wire up unified webhook runtimeBUILD_MODE=unified now starts an axum webhook server at runtime, routing events directly to Dispatcher (no WebSocket hop).
  2. Make serenity conditional on discord featureserenity is now optional = true in both openab-core and root Cargo.toml, gated by discord = ["dep:serenity"]. Non-Discord builds skip compiling serenity entirely.
  3. Dockerfile boilerplate DRY — Added Dockerfile.builder as single source of truth for the cargo cache-build pattern + docker-bake.hcl for shared BuildKit layer caching across all 13+ image variants.
  4. Extract serve() in gateway lib — All server logic moved to pub async fn serve(ServeConfig) in lib.rs. Binary is now a 15-line thin wrapper.

Architecture

Before (standalone gateway + WebSocket)

┌──────────────┐         ┌──────────────────┐         ┌──────────────┐
│  Telegram /  │  HTTP   │  openab-gateway  │   WS    │   openab     │
│  LINE / etc  │───────▶│  (separate bin)  │────────▶│  (main bin)  │
│  webhook     │         │  :8080           │         │  Dispatcher  │
└──────────────┘         └──────────────────┘         └──────────────┘
                              │      ▲                      │
                              │      │ JSON serialize/      │
                              ▼      │ deserialize          ▼
                         event_tx ──────── WS ──────── reply

After (unified binary, direct dispatch)

┌──────────────┐         ┌─────────────────────────────────────────┐
│  Telegram /  │  HTTP   │            openab (unified binary)       │
│  LINE / etc  │───────▶│                                           │
│  webhook     │         │  ┌─────────┐    direct fn    ┌────────┐ │
└──────────────┘         │  │  axum   │───────────────▶│Dispatch│ │
                         │  │  :8080  │                 │  er    │ │
                         │  └─────────┘                 └────┬───┘ │
                         │       │                           │      │
                         │       │ event_tx                  ▼      │
                         │       ▼                      ┌────────┐ │
                         │  ┌──────────────────┐        │  ACP   │ │
                         │  │process_gateway_  │        │ Agent  │ │
                         │  │event() (filter,  │        └────┬───┘ │
                         │  │ parse, dispatch) │             │      │
                         │  └──────────────────┘             │      │
                         │                                   ▼      │
                         │  ┌──────────────────┐   adapter.send()  │
                         │  │UnifiedGateway-   │◀──────────────────│
                         │  │Adapter (routes   │                    │
                         │  │reply by platform)│                    │
                         │  └──────────────────┘                    │
                         └─────────────────────────────────────────┘

  ✓ No WebSocket hop          ✓ No serialize/deserialize
  ✓ Single binary, one env    ✓ Same signature verification

Conditional Compilation (serenity)

cargo build                          cargo build --no-default-features
(default: discord + slack)           (gateway-only / unified)

  ┌─────────────────────┐              ┌─────────────────────┐
  │ openab-core         │              │ openab-core         │
  │  ├─ discord.rs  ✅  │              │  ├─ discord.rs  ⬚  │
  │  ├─ remind.rs   ✅  │              │  ├─ remind.rs   ⬚  │
  │  ├─ gateway.rs  ✅  │              │  ├─ gateway.rs  ✅  │
  │  └─ serenity    ✅  │              │  └─ serenity    ⬚  │ ← not compiled
  └─────────────────────┘              └─────────────────────┘

Related

Follow-up Items (non-blocking)

From group review — tracked in #1172:

  • Add GATEWAY_ALLOW_BOT_MESSAGES / GATEWAY_TRUSTED_BOT_IDS env for multi-agent
  • Wire Google Chat in unified mode (google_chat: None TODO)
  • Deduplicate process_gateway_event() / run_gateway_adapter() shared logic
  • Extract AppState::from_env() factory to avoid unified/standalone divergence
  • Document production-must-set env vars in Helm/deploy docs
  • Add user-facing fallback message when create_thread fails
  • Log when audio attachment path/data both empty (silent skip)

chaodu-agent added 10 commits June 21, 2026 20:18
Replaces TODO stub with architecture doc comment.
Implementation in progress.
Step 1 of Phase 2: Extract the gateway event processing logic
(filtering, allowlists, attachments, slash commands, dispatch)
into a standalone public function that can be called from both:
- The existing WebSocket handler (unchanged)
- The new unified mode's embedded axum server (Phase 2)

New public API:
- GatewayEventContext: config/deps needed for event processing
- process_gateway_event(): processes JSON event, returns dispatched/filtered
- Start axum on GATEWAY_LISTEN (default :8080) when any gateway
  platform env vars are detected at runtime
- Only compiled in when gateway features are enabled
- Creates dedicated Dispatcher for unified events
- Adds /health endpoint
- Adapter webhook routes to be wired in Step 3

Dependencies: axum added as optional dep, enabled per gateway feature.
Complete unified mode wiring:
- Reuse gateway crate's AppState + all adapter webhook handlers as-is
- Mount platform-specific routes (telegram, line, feishu, wecom, teams)
- Bridge: event_tx → process_gateway_event() → Dispatcher
- Each adapter uses #[cfg(feature)] gating
- Same signature verification as standalone gateway

Architecture:
  Webhook → adapter handler → event_tx.send(json)
         → bridge task → process_gateway_event()
         → Dispatcher.submit() → ACP Agent
… gating

- Fix send_text -> send_message compile error in process_gateway_event
- Implement UnifiedGatewayAdapter that routes ChatAdapter calls through
  in-process gateway platform adapters (telegram, line, feishu, etc.)
  based on ChannelRef.platform field
- Replace hardcoded allow_all=true with env-var based config:
  GATEWAY_ALLOW_ALL_CHANNELS, GATEWAY_ALLOWED_CHANNELS,
  GATEWAY_ALLOW_ALL_USERS, GATEWAY_ALLOWED_USERS, GATEWAY_BOT_USERNAME

Addresses: F1 (outbound reply path missing), F2 (security gating bypass)
from architecture review.
- Replace uuid::Uuid::new_v4() with std::time nanos (uuid not in root deps)
- Add reqwest to root Cargo.toml (needed for AppState.client construction)
- Fix Dispatcher::new → Dispatcher::with_idle_timeout (correct API)
In openab-core/Cargo.toml: serenity is now optional = true, gated by
discord = ["dep:serenity"]. In root Cargo.toml: same pattern.

Modules that use serenity (discord.rs, remind.rs) are already behind
#[cfg(feature = "discord")]. Added cfg gates to ctl.rs ShardSlot and
agent.status handler.

This reduces compile time for non-Discord builds (gateway-only, etc).
Move all server setup logic (adapter initialization, axum router,
background tasks, WebSocket handler) from main.rs into a
pub async fn serve(ServeConfig) in lib.rs.

The gateway binary is now a thin 15-line wrapper that calls
openab_gateway::serve(ServeConfig::default()).

This makes the gateway server embeddable and testable as a library.
Add Dockerfile.builder as the single source of truth for the cargo
cache-build pattern repeated across all 13+ Dockerfiles.

Add docker-bake.hcl for local development — enables BuildKit to share
a single cached builder layer across all image variants, dramatically
speeding up multi-image rebuilds.

CI continues to use plain 'docker build -f Dockerfile.X .' which
remains self-contained. The bake file is opt-in for local iteration.
@chaodu-agent

chaodu-agent commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator Author
⚠️ Outdated — superseded by final review summary below

(Round 1 partial review — see final comment for complete summary)

Addresses review finding from 擺渡法師 (F3).
@chaodu-agent

chaodu-agent commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator Author
⚠️ Outdated — superseded by final review summary below

(Previous draft — see latest comment for final summary)

@chaodu-agent chaodu-agent marked this pull request as ready for review June 21, 2026 23:59
@chaodu-agent chaodu-agent requested a review from thepagent as a code owner June 21, 2026 23:59
@chaodu-agent chaodu-agent enabled auto-merge June 21, 2026 23:59
@chaodu-agent

Copy link
Copy Markdown
Collaborator Author

Final Group Review — All LGTM ✅

HEAD: 627deec | CI: ✅ All pass (clippy + 16/16 smoke-tests)

Review Result

4 independent reviewers approved from different angles (Security/CI, Architecture, Code Quality, Correctness). No 🔴 Critical findings.

Findings Resolved in This PR

# Finding Resolution
Teams unwrap redundancy ✅ Fixed in 627deec — uses matched ref teams directly

Follow-up Items (non-blocking, tracked in PR description)

  1. Add GATEWAY_ALLOW_BOT_MESSAGES / GATEWAY_TRUSTED_BOT_IDS env for multi-agent
  2. Wire Google Chat in unified mode (google_chat: None TODO)
  3. Deduplicate process_gateway_event() / run_gateway_adapter() shared logic
  4. Extract AppState::from_env() factory to avoid unified/standalone divergence
  5. Document production-must-set env vars in Helm/deploy docs
  6. Add user-facing fallback message when create_thread fails
  7. Log when audio attachment path/data both empty (silent skip)

Praise Highlights

  • 🟢 Clean serve() extraction — binary 170→15 lines
  • 🟢 ShardSlot type alias pattern for conditional compilation
  • 🟢 Dockerfile.builder + docker-bake.hcl DRY
  • 🟢 Event bridge eliminates WebSocket hop cleanly
  • 🟢 Feature-gated UnifiedGatewayAdapter with full ChatAdapter impl

Ready to merge. All reviewers approved, CI green, no 🔴 blockers.

@chaodu-agent chaodu-agent merged commit fd53af4 into main Jun 22, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Workspace restructure follow-ups (post #1146)

2 participants