Skip to content

[Epic] Bridges — vendor suites (DingTalk, WeCom, Feishu) behind the standard protocols the platform speaks: the platform's generic interfaces (webhook channel, inbound webhook, OIDC, SCIM) and the objectstack-ai/bridges repository #27314

Open

Description

@objectstack-fleet

repo:cloud#2 · os-zhuang · session_0123kRqqdpAJWYMgsgdAJX4G (filed on the maintainer's direction, handed off from the #27108 cloud-assessment thread)

Filing gate: ③ maintainer-directed, and ④ the cross-repo parent of its children. The maintainer, in the cloud project thread on 2026-10-28 (verbatim, not translated): 「钉钉这种不支持标准API的,有没有可能我们封装一层中转的微服务,按照标准协议」 (16:03Z) · 「用户映射、身份认证,这个属于单独的需求,能否也可以是另外一套中转服务」 (16:05Z) · 「我说的只是把钉钉作为例子,还会有企业微信,飞书等,你认为是中转服务的方式好,还是开发具体插件的方式好。」 (16:10Z, answered: bridges) · 「我创建了 bridges 仓库,你可以初始化,然后刚才讨论的问题立专题卡」 (16:15Z) · 「这个应该是单独的专题卡吧」 (16:16Z). And on the #27108 decision card 「插件专题 #27108 在中转方案之后怎么处理?」 the maintainer chose 「M1 做完,其余冻结」 (16:18Z). ⛔ Not a claim. ⛔ No domain:*: children span objectstack (server), cloud and objectstack-ai/bridges; triage sets each child's lane.

Dedupe: bridge relay DingTalk WeCom Feishu webhook messaging channel OIDC SCIM on objectstack → 0 hits (open or closed). Related, not duplicates: #27108 (code plugins), #27287 (SDK contract, frozen by this ruling), #27293 (zero-code connectors — this epic's action side reuses it).

维护者速读

钉钉、企业微信、飞书这类不讲标准协议的套件,不再做成代码插件,而是走独立的「中转服务」(bridge):平台只讲标准协议,bridge 负责把标准协议翻译成厂商私有 API。平台补齐几个通用接口一次(出站 webhook 消息通道、入站 webhook、OIDC 登录、SCIM 通讯录),以后每接一家只是多一个 bridge,平台不改。bridge 放在新仓 objectstack-ai/bridges(公开、Apache-2.0,已初始化 80b4abd),每家一个包、各自发版;同一份代码既跑我们托管的多租户实例,也出 Docker 镜像给私有部署客户内网自部署。连带裁决:#27108 只做完 M1,M2–M5 与 SDK 冻结;托管 Cloud 不加载代码插件。

Decisions this epic carries

# Decision Effect
1 Vendor suites without a standard API (DingTalk, WeCom, Feishu, and the like) integrate through bridges, not code plugins. #27108 ruling 6471792611 item 3 (plugin-dingtalk as the first resident of objectstack-ai/plugins) is withdrawn; DingTalk is this epic's first bridge.
2 #27108: M1 finishes; M2, M3, M4, M5 and the SDK contract are frozen until a paying customer asks for third-party code distribution. #27124, #27125, #27284, #27285, #27287 frozen (recorded on #27108). M1 #27123 continues unchanged.
3 Hosted ObjectOS Cloud loads no code plugins. One hosted process serves many environments' kernels, so it is not a dedicated runtime (#27108 ruling item 4's rule, applied to hosted). Follows from decision 2 (no fetch or shelf path while M2/M4 are frozen); the thread's card on this question was closed as this option on that basis. Code plugins are for self-hosted and Enterprise dedicated runtimes only (M1's local install). Hosted reaches vendor suites through bridges.
4 Where the code lives. Bridges: objectstack-ai/bridges (public, independent versions, no import of platform internals). Generic interfaces: objectstack-ai/objectstack. Our hosted bridge instances' deployment config: objectstack-ai/cloud, pinning a bridge release. —

How a bridge meets the platform

Need Platform side (standard) Bridge side
Actions (send a work notice, create a to-do, read the directory) OpenAPI / MCP connector app — #27293, no new card calls the vendor API, reshapes requests and replies
Per-person notifications (approval → the approver's DingTalk) webhook messaging channel — outbox exists, per-recipient channel missing (B1) delivers to the person's vendor account
Callbacks (a button pressed in a vendor card) inbound webhook → flow — exists (flow api trigger) verifies and decrypts the vendor callback, forwards it
Sign-in (QR login, in-app silent login) OIDC — exists both sides; hosted needs one upstream entry per bridge (B3) wraps the vendor's OAuth / auth-code flow as an OIDC provider; no new "create a session from an external identity" API is needed
Directory and user mapping SCIM provisioning into the environment — exists (cloud ADR-0071); gaps in B4 reads the vendor directory; vendor user id → externalId (that IS the mapping; no mapping object)

Children (each filed by the owning lane, Part of this epic)

  • B1 — notification channel over the existing HTTP outbox (objectstack, server, S). The delivery machinery exists: plugin-webhooks (sys_webhook, HMAC X-Objectstack-Signature, durable outbox, retries, cluster dispatcher; packages/services/service-messaging/src/http-outbox.ts) already POSTs record events. What is missing is a per-recipient messaging channel: registerChannelProvider is called for email and sms only (messaging-service-plugin.ts:458, :477), so a notification addressed to a person (recipient, preferences, template) cannot reach a URL. B1 = a webhook channel provider that enqueues into the same outbox, carrying the recipient's externalId, the rendered content and the deep link.
  • B2 — inbound callbacks: no card, already exists. A flow's api start node arms a signed inbound hook (packages/spec/src/automation/api-trigger-secret-ref.zod.ts, flow.zod.ts:1117), and a wait node resumes on an incoming webhook (node-executor.zod.ts:31). The bridge verifies and decrypts the vendor callback, then posts to that hook.
  • B3 — "Sign in with " on the Cloud identity center (cloud, S). The mechanism exists on both sides: environments take an external OIDC/SAML IdP through @better-auth/sso (bring-your-own IdP; Business+ on hosted), and the Cloud identity center already federates social logins — but as a fixed list of Google / GitHub / Microsoft from environment variables (packages/service-cloud/src/control-plane-preset.ts:444-450). B3 = one generic OIDC upstream entry (better-auth genericOAuth) per bridge, so every hosted environment gets the button. Self-hosted: documentation only.
  • B4 — SCIM for bridges: two gaps only (cloud + objectstack). SCIM itself is built: cloud ADR-0071 put @better-auth/scim (SCIM 2.0 /Users, active:false → ban + session revoke) in each environment, and the hosted kernel mounts it by plan (planAllowsScim, packages/objectos-runtime/src/artifact-kernel-factory.ts:103, :315-355; EE turns it on with the FEATURE_SCIM licence, apps/objectos-ee/objectstack.config.ts:763-769). A bridge is an external directory client, the same role as Okta, so ADR-0071 D2's "no cloud-as-SCIM-client" does not apply. What remains: (a) Users only, no /Groups (ADR-0071 D4): DingTalk departments → sys_business_unit and department → role are not covered — ADR-0071's out-of-scope line even names 钉钉 / 企业微信 / 飞书 org-structure sync; (b) tier: SCIM is Enterprise-only (cloud docs/design/pricing.md:127) — a maintainer decision (below) before a bridge relies on it for Team customers.
  • B5 — bridge-kit + bridge-dingtalk (bridges). Kit: OIDC provider shell, SCIM client, webhook relay, vendor callback crypto (the token cache landed in 80b4abd). DingTalk: actions (connector app's OpenAPI description), notifications (B1), sign-in (B3), directory (B4). Ships as our hosted instance and a Docker image. Acceptance carried over from ADR-0025 SDK contract — the three public seams external plugins build on (messaging channel provider, identity-object writes, PluginContext subset), a conformance testkit, the peer-dependency build check, and the objectstack-ai/plugins repository #27287: an approval notification reaches a mapped DingTalk user with a deep link back to the record — plus sign-in with DingTalk and a directory sync.
  • B6 — compatibility (bridges + objectstack). A nightly contract test of each bridge against the platform's next build, and a conformance kit for the standard interfaces in objectstack.
  • WeCom and Feishu bridges are filed after DingTalk proves the kit.

Open maintainer decisions (decision cards when the child is dispatched)

  1. Who registers the vendor app: one ISV third-party app on the DingTalk open platform shared by all customers (better onboarding; needs ISV qualification and listing) vs each customer's own internal app with its key entered in the environment (works today).
  2. Tier: is "Sign in with DingTalk" a social login (every tier, like Google/Microsoft), and is directory sync over SCIM Enterprise-only as SCIM is today — which would leave Team customers in China without it?

Rules every child inherits

Done-when

  • B1 and B3 landed (and B4 for the gaps it carries), in the cloud pin.
  • bridge-dingtalk released, hosted for ObjectOS Cloud and available as a Docker image; on a hosted environment a user signs in with DingTalk, the directory syncs, and an approval notification reaches the approver's DingTalk with a deep link.
  • The nightly contract test runs green against platform next.

Related

#27108 (code plugins; ruling recorded there) · #27287 (frozen) · #27293 (connector apps; cloud#3353 E4 shelf lists each bridge's connector app) · #27230 (credential S1) · ADR-0024 (identity) · objectstack-ai/bridges docs/architecture.md.

Activity

  1. added
    enhancementNew feature or request
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    on Oct 28, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Ruling: #27314 · pause (pm:on-hold) · maintainer 「集成桥接 #27314 暂停」 2026-11-08T16:26Z

    Checkpoint (the #31502 pause rule)

    ⛔ Not a claim.

    • State: no children, no PRs, and no activity since it was filed after the bridges decision (2026-10-28T16:16Z). Nothing is in flight to stop.
    • Restart-when: 「维护者决定重启」. On restart, the owning lane splits the first milestone under A′ (children in lane queues, Blocked-by: for order).
    • Review: the daily epic overview lists this as paused. Past 30 days, the maintainer is asked once whether to keep it.

    Generated by Claude Code

  3. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queuepm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold commentpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions