From c550e8ec0b83d66837bfe4ede7e1e963432b52f0 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Sun, 14 Jun 2026 23:19:59 -0400 Subject: [PATCH 1/5] docs(adr): propose unified single-binary architecture Add ADR for restructuring OpenAB as a Cargo workspace that ships all platform adapters in a single binary, activated at runtime via config. This eliminates the two-process (core + gateway sidecar) deployment model for most users while keeping the standalone gateway available for advanced use cases. --- docs/adr/unified-binary.md | 200 +++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 docs/adr/unified-binary.md diff --git a/docs/adr/unified-binary.md b/docs/adr/unified-binary.md new file mode 100644 index 000000000..77bbf911f --- /dev/null +++ b/docs/adr/unified-binary.md @@ -0,0 +1,200 @@ +# ADR: Unified Single-Binary Architecture + +- **Status:** Proposed +- **Date:** 2026-06-15 +- **Author:** @pahud +- **Supersedes:** Deployment model from [ADR: Custom Gateway](./custom-gateway.md) + +--- + +## 1. Context & Problem + +Today, supporting webhook-based platforms (Telegram, LINE, Feishu, Google Chat, WeCom, Teams) requires running **two processes** — `openab` core and `openab-gateway` — wired together via WebSocket, often in the same pod with a shared volume for colocate-mode media passing. + +This creates operational friction: + +- **Two containers** in a single pod (or two separate services) +- **Shared volume** required for media colocate mode +- **WebSocket wiring** between core and gateway (auth token, reconnect logic) +- **Version matrix** — gateway releases independently, version mismatches cause subtle bugs +- **Double serialization** — every message is serialized to JSON, sent over WS, then deserialized + +For most users who just want "Discord + Telegram in one bot", the two-process model is unnecessary complexity. + +--- + +## 2. Decision + +Restructure the project as a **Cargo workspace** with the final binary shipping **all adapters compiled in**, activated at runtime via config. The standalone gateway remains available for advanced deployments. + +### Workspace Layout + +``` +openab/ +├── Cargo.toml (workspace root) +├── crates/ +│ ├── openab-core/ (ChatAdapter trait, ACP, Dispatcher, SessionPool, +│ │ Discord adapter, Slack adapter) +│ └── openab-gateway/ (platform adapters: Telegram, LINE, Feishu, +│ Google Chat, WeCom, Teams — impl ChatAdapter) +├── src/ (final binary — thin main.rs wiring both crates) +└── gateway/ (standalone gateway binary — kept for backward compat) +``` + +### Feature Flags (on the final binary crate) + +```toml +[features] +default = ["discord", "slack", "telegram", "line", "feishu", "googlechat", "wecom", "teams"] + +discord = ["openab-core/discord"] +slack = ["openab-core/slack"] +telegram = ["dep:openab-gateway", "openab-gateway/telegram"] +line = ["dep:openab-gateway", "openab-gateway/line"] +feishu = ["dep:openab-gateway", "openab-gateway/feishu"] +googlechat = ["dep:openab-gateway", "openab-gateway/googlechat"] +wecom = ["dep:openab-gateway", "openab-gateway/wecom"] +teams = ["dep:openab-gateway", "openab-gateway/teams"] +``` + +### Runtime Activation + +Adapters start **only if their config section is present and has required fields** (e.g., `bot_token`). Compiled-in but unconfigured adapters have zero runtime overhead. + +```toml +# Only Discord and Telegram start — others dormant +[discord] +bot_token = "${DISCORD_BOT_TOKEN}" +allowed_channels = ["123456789"] + +[telegram] +bot_token = "${TELEGRAM_BOT_TOKEN}" +``` + +--- + +## 3. Architecture — Before & After + +### Before (two-process model) + +``` +┌─────────────────────────────┐ ┌───────────────────────────────────┐ +│ openab core │ │ openab-gateway (sidecar) │ +│ │ │ │ +│ Discord ──┐ │ │ Telegram ──┐ │ +│ Slack ────┤► Dispatcher │◄─WS─┤ LINE ──────┤► axum → GatewayEvent│ +│ │ │ │ Feishu ────┘ │ +│ GatewayAdapter (WS client) │ │ │ +└─────────────────────────────┘ └───────────────────────────────────┘ + shared volume for media colocate +``` + +### After (single binary) + +``` +┌────────────────────────────────────────────────────────────────┐ +│ openab (single binary) │ +│ │ +│ Discord ────┐ │ +│ Slack ──────┤ │ +│ Telegram ───┤► Dispatcher → SessionPool → ACP (child process) │ +│ LINE ───────┤ │ +│ Feishu ─────┘ │ +│ │ +│ axum HTTP (:9090) — only starts if webhook adapters active │ +└────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. Message Flow Change + +``` +BEFORE: + Platform → HTTP → gateway/telegram.rs → serialize GatewayEvent + → WebSocket → core/gateway.rs → deserialize → Dispatcher.submit() + +AFTER: + Platform → HTTP → src/telegram.rs → Dispatcher.submit() (direct call) +``` + +Reply path is similarly direct — the adapter calls the platform API in its `ChatAdapter` impl without WS round-trip. + +--- + +## 5. Published Artifacts + +| Image | Contents | Use case | +|-------|----------|----------| +| `openab:latest` | All adapters compiled in | Default — one image for everyone | +| `openab:slim` | Discord + Slack only | Minimal deploy (no axum/crypto deps) | +| `openab-gateway:latest` | Standalone gateway (unchanged) | Advanced: geo-distributed gateway, legacy compat | + +Custom builds via feature flags: +```bash +cargo build --no-default-features --features telegram # Telegram-only ~10MB +``` + +--- + +## 6. Migration Path + +| Phase | Description | +|-------|-------------| +| **Phase 1** | Restructure into workspace. Move adapter code behind feature flags. Ship `openab:latest` with all adapters. Keep standalone gateway as-is. | +| **Phase 2** | Users migrate from two-container to single-container. Helm chart defaults to single binary; gateway sidecar becomes opt-in. | +| **Phase 3** | Deprecate standalone gateway after 2 minor releases. Remove `gateway.url` config field (or keep as hidden legacy). | + +### Backward Compatibility + +- Existing `[gateway]` config section + standalone gateway continues to work throughout all phases +- The `GatewayAdapter` (WebSocket client in core) remains available for users who need remote gateway +- No breaking change to config schema — new `[telegram]`, `[line]` sections are additive + +--- + +## 7. Trade-offs + +### Advantages + +- **One container, one config, one release** — dramatically simpler deployment +- **Lower latency** — no WS serialization hop +- **One log stream** — easier debugging +- **No shared volume** — media passed in-process +- **Smaller attack surface** — no exposed WS port between containers + +### Disadvantages + +- **Larger default binary** — ~25MB vs ~12MB (Discord-only). Mitigated by `slim` image. +- **Coupled release cadence** — platform adapter fix requires full release. Mitigated by workspace allowing independent crate versioning. +- **More deps in tree** — `axum`, `jsonwebtoken`, `prost`, `quick-xml`, `aes/cbc` pulled in even if unused at runtime. Mitigated by feature flags for custom builds. +- **Build time** — full build is longer. Mitigated by workspace incremental compilation (only changed crate recompiles). + +--- + +## 8. Core Changes Required + +| Area | Change | Scope | +|------|--------|-------| +| `main.rs` | Start axum server + register adapter routes | ~50 lines | +| `config.rs` | Add `TelegramConfig`, `LineConfig`, etc. | ~100 lines (additive) | +| `Cargo.toml` | Workspace restructure + feature flags | Medium | +| Adapter code | Move from `gateway/src/adapters/` → `crates/openab-gateway/src/` | Mechanical move | +| Per-adapter glue | Replace WS broadcast with `Dispatcher.submit()` | ~10 lines each | +| Existing modules | **Zero changes** — ACP, pool, dispatcher, discord, slack untouched | None | + +--- + +## 9. Rejected Alternatives + +### A. Compile-time only (no fat binary) + +Users must `docker build` themselves with desired features. Poor UX — rejected. + +### B. Merge all code into one crate + +Couples platform-specific complexity (Feishu AES-CBC, WeCom XML) with clean core abstractions. Rejected in favor of workspace separation. + +### C. Keep current architecture, improve docs + +The operational complexity is inherent to the two-process model. Better docs don't eliminate the shared volume requirement or WS failure modes. Rejected. From c83c36032d2feeb7db473bb876bd7e3dc69b5de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E6=B8=A1=E6=B3=95=E5=B8=AB?= Date: Mon, 15 Jun 2026 21:14:58 +0000 Subject: [PATCH 2/5] docs(adr): flip default to separate binaries, unified as opt-in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Default: two separate binaries (core + gateway), unchanged from today - Opt-in: --features unified compiles all adapters into single binary - Zero code changes needed — just a build flag or image swap - Published: openab:latest (core), openab:unified (all-in-one) --- docs/adr/unified-binary.md | 102 ++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/docs/adr/unified-binary.md b/docs/adr/unified-binary.md index 77bbf911f..294f62b46 100644 --- a/docs/adr/unified-binary.md +++ b/docs/adr/unified-binary.md @@ -1,4 +1,4 @@ -# ADR: Unified Single-Binary Architecture +# ADR: Separate Binaries with Opt-In Unified Build - **Status:** Proposed - **Date:** 2026-06-15 @@ -25,7 +25,7 @@ For most users who just want "Discord + Telegram in one bot", the two-process mo ## 2. Decision -Restructure the project as a **Cargo workspace** with the final binary shipping **all adapters compiled in**, activated at runtime via config. The standalone gateway remains available for advanced deployments. +Restructure the project as a **Cargo workspace** that keeps the two-binary model by default (core + standalone gateway), while allowing users to compile everything into a **single unified binary** via a feature flag — requiring zero code changes, only `--features unified` or a Cargo config toggle. ### Workspace Layout @@ -45,16 +45,30 @@ openab/ ```toml [features] -default = ["discord", "slack", "telegram", "line", "feishu", "googlechat", "wecom", "teams"] +# Default: core only (Discord + Slack). Gateway ships as separate binary. +default = ["discord", "slack"] -discord = ["openab-core/discord"] -slack = ["openab-core/slack"] -telegram = ["dep:openab-gateway", "openab-gateway/telegram"] -line = ["dep:openab-gateway", "openab-gateway/line"] -feishu = ["dep:openab-gateway", "openab-gateway/feishu"] +# Opt-in: compile all gateway adapters into a single unified binary +unified = ["telegram", "line", "feishu", "googlechat", "wecom", "teams"] + +discord = ["openab-core/discord"] +slack = ["openab-core/slack"] +telegram = ["dep:openab-gateway", "openab-gateway/telegram"] +line = ["dep:openab-gateway", "openab-gateway/line"] +feishu = ["dep:openab-gateway", "openab-gateway/feishu"] googlechat = ["dep:openab-gateway", "openab-gateway/googlechat"] -wecom = ["dep:openab-gateway", "openab-gateway/wecom"] -teams = ["dep:openab-gateway", "openab-gateway/teams"] +wecom = ["dep:openab-gateway", "openab-gateway/wecom"] +teams = ["dep:openab-gateway", "openab-gateway/teams"] +``` + +Users who want the unified single-binary experience: +```bash +cargo build --features unified # all adapters in one binary +``` + +Or pick specific adapters: +```bash +cargo build --features telegram,line # just these two added to core ``` ### Runtime Activation @@ -89,7 +103,20 @@ bot_token = "${TELEGRAM_BOT_TOKEN}" shared volume for media colocate ``` -### After (single binary) +### After — Default (two binaries, same as today but workspace-structured) + +``` +┌─────────────────────────────┐ ┌───────────────────────────────────┐ +│ openab core │ │ openab-gateway │ +│ │ │ │ +│ Discord ──┐ │ │ Telegram ──┐ │ +│ Slack ────┤► Dispatcher │◄─WS─┤ LINE ──────┤► axum → GatewayEvent│ +│ │ │ │ Feishu ────┘ │ +│ GatewayAdapter (WS client) │ │ │ +└─────────────────────────────┘ └───────────────────────────────────┘ +``` + +### After — Opt-in Unified (`--features unified`) ``` ┌────────────────────────────────────────────────────────────────┐ @@ -126,13 +153,14 @@ Reply path is similarly direct — the adapter calls the platform API in its `Ch | Image | Contents | Use case | |-------|----------|----------| -| `openab:latest` | All adapters compiled in | Default — one image for everyone | -| `openab:slim` | Discord + Slack only | Minimal deploy (no axum/crypto deps) | -| `openab-gateway:latest` | Standalone gateway (unchanged) | Advanced: geo-distributed gateway, legacy compat | +| `openab:latest` | Discord + Slack only (core) | Default — lightweight, same as today | +| `openab-gateway:latest` | Standalone gateway (all webhook adapters) | Default companion for webhook platforms | +| `openab:unified` | All adapters in single binary | Simplified deployment for users who want one container | Custom builds via feature flags: ```bash -cargo build --no-default-features --features telegram # Telegram-only ~10MB +cargo build --features unified # all-in-one binary +cargo build --features telegram,line # core + specific adapters only ``` --- @@ -141,21 +169,30 @@ cargo build --no-default-features --features telegram # Telegram-only ~10MB | Phase | Description | |-------|-------------| -| **Phase 1** | Restructure into workspace. Move adapter code behind feature flags. Ship `openab:latest` with all adapters. Keep standalone gateway as-is. | -| **Phase 2** | Users migrate from two-container to single-container. Helm chart defaults to single binary; gateway sidecar becomes opt-in. | -| **Phase 3** | Deprecate standalone gateway after 2 minor releases. Remove `gateway.url` config field (or keep as hidden legacy). | +| **Phase 1** | Restructure into workspace. Keep two-binary default. Add `unified` feature flag. Ship `openab:unified` image for early adopters. | +| **Phase 2** | Gather feedback from unified adopters. Improve single-binary DX (combined health endpoint, unified log format). | +| **Phase 3** | If community consensus shifts toward unified-by-default, flip the default in a future major release. | ### Backward Compatibility -- Existing `[gateway]` config section + standalone gateway continues to work throughout all phases -- The `GatewayAdapter` (WebSocket client in core) remains available for users who need remote gateway -- No breaking change to config schema — new `[telegram]`, `[line]` sections are additive +- Default behavior is **unchanged** — existing two-binary deployments continue to work with no migration +- The `unified` feature is purely additive — opting in requires only a build flag or image swap +- No breaking change to config schema — `[telegram]`, `[line]` sections work in both modes +- `[gateway]` config section continues to work for users who keep the two-binary model --- ## 7. Trade-offs -### Advantages +### Advantages (of this approach) + +- **Zero disruption** — default behavior unchanged; existing deployments need no migration +- **Opt-in simplicity** — users who want a single binary get it with one flag (`--features unified`) +- **Smaller default binary** — `openab:latest` stays ~12MB without webhook adapter deps +- **Independent release cadence** — gateway can still release independently by default +- **Progressive adoption** — community can move to unified at their own pace + +### Advantages (of unified mode, when opted in) - **One container, one config, one release** — dramatically simpler deployment - **Lower latency** — no WS serialization hop @@ -165,10 +202,9 @@ cargo build --no-default-features --features telegram # Telegram-only ~10MB ### Disadvantages -- **Larger default binary** — ~25MB vs ~12MB (Discord-only). Mitigated by `slim` image. -- **Coupled release cadence** — platform adapter fix requires full release. Mitigated by workspace allowing independent crate versioning. -- **More deps in tree** — `axum`, `jsonwebtoken`, `prost`, `quick-xml`, `aes/cbc` pulled in even if unused at runtime. Mitigated by feature flags for custom builds. -- **Build time** — full build is longer. Mitigated by workspace incremental compilation (only changed crate recompiles). +- **Two images to maintain** — CI must build both default and unified variants +- **Unified binary is larger** — ~25MB vs ~12MB. Acceptable as opt-in. +- **Feature flag complexity** — conditional compilation adds `#[cfg]` gates. Mitigated by clean workspace boundary (all gateway code lives in `openab-gateway` crate). --- @@ -187,14 +223,18 @@ cargo build --no-default-features --features telegram # Telegram-only ~10MB ## 9. Rejected Alternatives -### A. Compile-time only (no fat binary) +### A. Unified binary as default + +Ship all adapters compiled in by default; users opt out for slim builds. Rejected — forces a larger binary and more deps on users who don't need webhook adapters, and deprecates the standalone gateway prematurely. + +### B. Compile-time only (no pre-built unified image) -Users must `docker build` themselves with desired features. Poor UX — rejected. +Users must `docker build` themselves with desired features. Poor UX — rejected. We publish `openab:unified` as a pre-built image. -### B. Merge all code into one crate +### C. Merge all code into one crate Couples platform-specific complexity (Feishu AES-CBC, WeCom XML) with clean core abstractions. Rejected in favor of workspace separation. -### C. Keep current architecture, improve docs +### D. Keep current architecture, no workspace restructure -The operational complexity is inherent to the two-process model. Better docs don't eliminate the shared volume requirement or WS failure modes. Rejected. +The workspace restructure is needed regardless — it enables feature flags, cleaner builds, and the opt-in unified path. Rejected. From c86950a198d4292302da30a41927a8ced9664c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E6=B8=A1=E6=B3=95=E5=B8=AB?= Date: Mon, 15 Jun 2026 21:16:23 +0000 Subject: [PATCH 3/5] docs(adr): add Dockerfile BUILD_MODE arg for unified opt-in --- docs/adr/unified-binary.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/adr/unified-binary.md b/docs/adr/unified-binary.md index 294f62b46..56f41d388 100644 --- a/docs/adr/unified-binary.md +++ b/docs/adr/unified-binary.md @@ -163,6 +163,41 @@ cargo build --features unified # all-in-one binary cargo build --features telegram,line # core + specific adapters only ``` +### Dockerfile Build Arg + +A single Dockerfile supports both modes via `BUILD_MODE` arg: + +```dockerfile +ARG BUILD_MODE=default +ARG FEATURES="" + +FROM rust:1.87 AS builder +ARG BUILD_MODE +ARG FEATURES + +WORKDIR /src +COPY . . + +RUN if [ "$BUILD_MODE" = "unified" ]; then \ + cargo build --release --features unified; \ + elif [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi +``` + +```bash +# Default: separate core binary +docker build -t openab:latest . + +# Unified: all adapters in one binary +docker build --build-arg BUILD_MODE=unified -t openab:unified . + +# Custom: pick specific adapters +docker build --build-arg FEATURES=telegram,line -t openab:custom . +``` + --- ## 6. Migration Path From 9b391b729cff3e1bf5d2ebb04da7f3272756b3a2 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Fri, 19 Jun 2026 15:11:03 +0000 Subject: [PATCH 4/5] docs(adr): align unified-binary ADR with #1146 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Status: Proposed → Accepted - Workspace layout: core stays in root crate, openab-core deferred to Phase 2 - Feature flags: discord = ["dep:serenity"] (not openab-core/discord) - Dockerfile: document root (--no-default-features) vs agent (additive) semantics - Add cross-reference to docs/image-tags.md for tagging convention - Add implementation link to PR #1146 Consensus: team unanimously chose option 2 (structural alignment only, operational details stay in dedicated docs). --- docs/adr/unified-binary.md | 63 +++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/docs/adr/unified-binary.md b/docs/adr/unified-binary.md index 56f41d388..e867e4a71 100644 --- a/docs/adr/unified-binary.md +++ b/docs/adr/unified-binary.md @@ -1,9 +1,10 @@ # ADR: Separate Binaries with Opt-In Unified Build -- **Status:** Proposed +- **Status:** Accepted - **Date:** 2026-06-15 - **Author:** @pahud - **Supersedes:** Deployment model from [ADR: Custom Gateway](./custom-gateway.md) +- **Implementation:** [PR #1146](https://github.com/openabdev/openab/pull/1146) --- @@ -31,28 +32,38 @@ Restructure the project as a **Cargo workspace** that keeps the two-binary model ``` openab/ -├── Cargo.toml (workspace root) +├── Cargo.toml (workspace root + binary crate) +├── src/ (core modules — Discord, Slack, ACP, Dispatcher, +│ SessionPool, ChatAdapter trait) ├── crates/ -│ ├── openab-core/ (ChatAdapter trait, ACP, Dispatcher, SessionPool, -│ │ Discord adapter, Slack adapter) -│ └── openab-gateway/ (platform adapters: Telegram, LINE, Feishu, +│ └── openab-gateway/ (webhook adapters: Telegram, LINE, Feishu, │ Google Chat, WeCom, Teams — impl ChatAdapter) -├── src/ (final binary — thin main.rs wiring both crates) └── gateway/ (standalone gateway binary — kept for backward compat) ``` -### Feature Flags (on the final binary crate) +> **Note:** Extracting `openab-core` as a separate library crate was considered but deferred +> to Phase 2 — it would require changing visibility on 30+ internal modules. The current +> approach keeps core in the root crate and only extracts gateway adapters. + +### Feature Flags (on the root binary crate) ```toml [features] -# Default: core only (Discord + Slack). Gateway ships as separate binary. -default = ["discord", "slack"] +# Default: core adapters (Discord + Slack) + infrastructure. +default = ["discord", "slack", "secrets-aws", "agentcore"] # Opt-in: compile all gateway adapters into a single unified binary unified = ["telegram", "line", "feishu", "googlechat", "wecom", "teams"] -discord = ["openab-core/discord"] -slack = ["openab-core/slack"] +# Core adapters (directly in root crate) +discord = ["dep:serenity"] +slack = [] + +# Infrastructure +secrets-aws = ["dep:aws-sdk-secretsmanager", "dep:aws-config"] +agentcore = ["dep:aws-config", "dep:aws-sigv4", ...] + +# Gateway adapters (each pulls in the gateway crate as optional dep) telegram = ["dep:openab-gateway", "openab-gateway/telegram"] line = ["dep:openab-gateway", "openab-gateway/line"] feishu = ["dep:openab-gateway", "openab-gateway/feishu"] @@ -165,39 +176,47 @@ cargo build --features telegram,line # core + specific adapters only ### Dockerfile Build Arg -A single Dockerfile supports both modes via `BUILD_MODE` arg: +The root Dockerfile supports both modes via `BUILD_MODE` and `FEATURES` args: ```dockerfile ARG BUILD_MODE=default ARG FEATURES="" -FROM rust:1.87 AS builder +FROM rust:1-bookworm AS builder ARG BUILD_MODE ARG FEATURES -WORKDIR /src +WORKDIR /build COPY . . RUN if [ "$BUILD_MODE" = "unified" ]; then \ cargo build --release --features unified; \ elif [ -n "$FEATURES" ]; then \ - cargo build --release --features "$FEATURES"; \ + cargo build --release --no-default-features --features "$FEATURES"; \ else \ cargo build --release; \ fi ``` -```bash -# Default: separate core binary -docker build -t openab:latest . +**Build semantics differ between root and agent Dockerfiles:** -# Unified: all adapters in one binary -docker build --build-arg BUILD_MODE=unified -t openab:unified . +| Dockerfile | `FEATURES` behavior | Rationale | +|------------|--------------------|-----------| +| Root (`Dockerfile`) | `--no-default-features --features "$FEATURES"` | Explicit control — user specifies exactly which adapters | +| Agent (`Dockerfile.`) | `--features "$FEATURES"` (additive) | Adds adapters on top of defaults (Discord + Slack) | -# Custom: pick specific adapters -docker build --build-arg FEATURES=telegram,line -t openab:custom . +```bash +# Root Dockerfile examples: +docker build -t openab:latest . # default +docker build --build-arg BUILD_MODE=unified -t openab:unified . # all adapters +docker build --build-arg FEATURES=telegram,line -t openab:custom . # ONLY these (no Discord/Slack) + +# Agent Dockerfile examples (additive — Discord + Slack always included): +docker build -f Dockerfile.claude --build-arg FEATURES=telegram -t openab-claude:tg . ``` +For image tagging conventions (`stable/beta/latest/semver/pr`), see [docs/image-tags.md](../image-tags.md). + --- ## 6. Migration Path From 1c7b13f6d127fb6aea0ce131617cfee96bd60908 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Fri, 19 Jun 2026 15:15:26 +0000 Subject: [PATCH 5/5] docs(adr): address external review findings (F2, F3, F5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - F2: Supersedes → Amends (custom-gateway ADR is still Proposed) - F3: Clarify config contract — [telegram]/[line] are unified-mode additions, [gateway] remains the two-binary contract - F5: Add optional dependency declaration example for dep:openab-gateway --- docs/adr/unified-binary.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/adr/unified-binary.md b/docs/adr/unified-binary.md index e867e4a71..e28ef08ad 100644 --- a/docs/adr/unified-binary.md +++ b/docs/adr/unified-binary.md @@ -3,7 +3,7 @@ - **Status:** Accepted - **Date:** 2026-06-15 - **Author:** @pahud -- **Supersedes:** Deployment model from [ADR: Custom Gateway](./custom-gateway.md) +- **Amends:** Deployment model from [ADR: Custom Gateway](./custom-gateway.md) — outbound-only + gateway sidecar remains the default; unified mode is an opt-in alternative path. - **Implementation:** [PR #1146](https://github.com/openabdev/openab/pull/1146) --- @@ -72,6 +72,13 @@ wecom = ["dep:openab-gateway", "openab-gateway/wecom"] teams = ["dep:openab-gateway", "openab-gateway/teams"] ``` +The `dep:openab-gateway` syntax requires the gateway crate as an optional dependency: + +```toml +[dependencies] +openab-gateway = { path = "crates/openab-gateway", default-features = false, optional = true } +``` + Users who want the unified single-binary experience: ```bash cargo build --features unified # all adapters in one binary @@ -231,8 +238,8 @@ For image tagging conventions (`stable/beta/latest/semver/pr`), see [docs/ima - Default behavior is **unchanged** — existing two-binary deployments continue to work with no migration - The `unified` feature is purely additive — opting in requires only a build flag or image swap -- No breaking change to config schema — `[telegram]`, `[line]` sections work in both modes -- `[gateway]` config section continues to work for users who keep the two-binary model +- No breaking change to config schema — `[gateway]` section continues to work for users who keep the two-binary model +- Platform-specific config sections (`[telegram]`, `[line]`, etc.) are **unified-mode additions** — they are only read when the corresponding adapter is compiled in via feature flags ---