packages/cloud-shared/docs/messaging-onboarding-gateway-design.md
Status: design proposal
Owner boundary: this document is a docs-only design. It references existing gateway, cloud, and plugin code but does not require implementation changes in this patch.
Eliza Cloud needs one messaging onboarding gateway that accepts first contact from iMessage, Telegram, Discord, and WhatsApp, runs the same chat-first onboarding flow, links the platform identity to an authenticated cloud identity, provisions exactly one user agent, copies the onboarding transcript into that agent, and then routes all later messages from the same messaging identity to the provisioned agent.
The design must keep the onboarding worker stateless, use Cerebras
gpt-oss-120b only as a fast response generator, and preserve the gateway
separation already present in the repo.
packages/cloud-services/gateway-webhook/src/index.ts exposes shared webhook
routes for telegram, blooio, twilio, and whatsapp, plus per-agent
routes under /webhook/:project/:platform/:agentId.packages/cloud-services/gateway-webhook/src/webhook-handler.ts already
implements the required high-level branch: verify webhook, extract event,
dedupe, resolve identity, route linked users to agent-server, and route
unlinked users to /api/eliza-app/onboarding/chat.packages/cloud-services/gateway-webhook/src/adapters/types.ts defines the
current PlatformAdapter, ChatEvent, and WebhookConfig shape.packages/cloud-services/gateway-webhook/src/adapters/telegram.ts and
packages/cloud-services/gateway-webhook/src/adapters/whatsapp.ts are the
webhook adapter patterns for signed inbound DMs and outbound replies.packages/cloud-services/gateway-webhook/src/adapters/blooio.ts is the
existing cloud iMessage-style adapter. It should be treated as the current
hosted provider path, not as the Mac-hosted BlueBubbles path.packages/cloud-services/gateway-discord/src/gateway-manager.ts manages
Discord WebSocket connections, leader election, failover, and message
forwarding. Discord should remain a gateway service, not be forced through
webhook-only code.packages/cloud-shared/src/lib/services/agent-gateway-router.ts contains
post-link routing logic for Discord, Telegram, WhatsApp, Twilio, and Blooio,
including room IDs, sender metadata, and sandbox/local-session dispatch.packages/cloud-api/internal/identity/resolve/route.ts is the internal
identity lookup used by the webhook gateway.packages/cloud-shared/src/lib/services/eliza-app/onboarding-chat.ts is the
current onboarding worker. It stores session state in cache, uses Cerebras
gpt-oss-120b when configured, triggers provisioning, and copies the
transcript to /api/memory/remember after the managed agent is running.packages/cloud-shared/src/lib/services/eliza-app/provisioning.ts is the
existing one-agent provisioning entry point for Eliza App onboarding.plugins/plugin-bluebubbles/src/setup-routes.ts and
plugins/plugin-bluebubbles/src/data-routes.ts document the local
BlueBubbles setup contract and public webhook receiver used by an agent
runtime.plugins/plugin-bluebubbles/src/service.ts maps BlueBubbles chats, handles
incoming messages, and sends iMessage/SMS/RCS through the BlueBubbles bridge.packages/cloud-services/headscale/README.md and
packages/cloud-services/tunnel-proxy/README.md define the current Headscale
and tunnel-proxy tag model.packages/test/scenarios/gateway/telegram-gateway.bot-routes-to-user-agent.scenario.ts,
packages/test/scenarios/gateway/discord-gateway.bot-routes-to-user-agent.scenario.ts,
and
packages/test/scenarios/gateway/whatsapp-gateway.bot-routes-to-user-agent.scenario.ts.The current repo has the major gateway surfaces, but the launch plan is not complete until these gaps are closed:
user_identities; auth/link paths that still write only canonical users
columns must also upsert the provider identity projection, or the resolver
must fall back to canonical columns until projection sync is guaranteed.handled:false because the Discord identity is unknown, the cloud
route should call the shared onboarding worker and return the onboarding
reply to the gateway.agentId must route to
onboarding/provisioning status rather than throwing in the gateway resolver.bluebubbles relay
contract: Headscale node tag, relay registration record, signed inbound
events, outbound relay delivery, and degraded-health state.Messaging platform
-> platform adapter or gateway service
-> normalized ChatEvent
-> identity resolver
-> linked: agent-server / sandbox / local-session route
-> unlinked: stateless onboarding worker
-> authenticated identity link
-> ensure one agent
-> transcript handoff
-> mark route ready
-> platform reply
The gateway owns ingress, verification, dedupe, and reply delivery. The cloud API owns identity, provisioning, and transcript handoff. The worker owns only the next onboarding response and must reconstruct all state from durable/cache records on every call.
Use the existing webhook adapter pattern in
packages/cloud-services/gateway-webhook/src/adapters/telegram.ts.
x-telegram-bot-api-secret-token.senderId to Telegram user ID and chatId to Telegram chat ID.platform: "telegram"platformUserId: senderIdplatformDisplayName: first_nametelegram_id is written.Use the existing webhook adapter pattern in
packages/cloud-services/gateway-webhook/src/adapters/whatsapp.ts.
x-hub-signature-256 with the configured app secret.senderId and chatId to the WhatsApp ID/phone.Keep Discord in packages/cloud-services/gateway-discord/.
The Mac-hosted iMessage path is separate from the existing hosted blooio
adapter.
Reference implementation surfaces:
plugins/plugin-bluebubbles/src/setup-routes.ts
and plugins/plugin-bluebubbles/src/data-routes.ts.plugins/plugin-bluebubbles/src/service.ts.packages/cloud-services/headscale/README.md
and packages/cloud-services/tunnel-proxy/README.md.Proposed topology:
User iPhone number / Apple ID
-> user-owned Mac running BlueBubbles
-> Eliza BlueBubbles relay on the Mac
-> Headscale tailnet
-> cloud messaging gateway
Requirements:
tag:imessage-gateway, not tag:agent.Normalized platform fields:
platform: "bluebubbles" for the Mac-hosted path.platform: "blooio" remains the existing hosted/provider path.senderId: normalized phone/email handle from BlueBubbles.chatId: BlueBubbles chat GUID when available, otherwise deterministic
direct room key.messageId: BlueBubbles message GUID.Durable linking rules:
The current worker is
packages/cloud-shared/src/lib/services/eliza-app/onboarding-chat.ts.
Design constraints:
sessionId, platform, and
platform user ID from cache/durable storage.gpt-oss-120b is only used to generate the next short response.Session fields should remain close to the existing OnboardingSession shape:
idplatformplatformUserIdplatformDisplayNameuserIdorganizationIdagentIdhandoffCopiedAtlaunchUrlhistoryAdditional proposed fields:
identityLinkStatus: "none" | "pending" | "verified" | "linked"identityLinkIdhandoffRouteActivatedAtlastGatewayMessageIdprovisioningRequestIdThe linking service should be explicit and idempotent.
Proposed internal contract:
POST /api/eliza-app/identity-link/start
POST /api/eliza-app/identity-link/confirm
POST /api/internal/identity/resolve
start creates a pending link challenge for an authenticated cloud session or
for a trusted onboarding session. It returns a short code or OAuth URL.
confirm verifies the platform proof:
Only confirm writes durable platform identifiers to the user record or linked
identity table. A gateway delivery can create or update a pending onboarding
session, but it must not silently merge identities across users.
After a successful link:
identity:${platform}:${platformId} negative caches in the
gateway;userId, organizationId, and selected agentId in the
onboarding session;Use ensureElizaAppProvisioning in
packages/cloud-shared/src/lib/services/eliza-app/provisioning.ts as the
canonical path.
Rules:
agent_provision job.The design intentionally does not add a second provisioning queue for messaging onboarding.
The existing handoff in onboarding-chat.ts is the right shape:
running.launchManagedElizaAgent./api/memory/remember.handoffCopiedAt on the onboarding session.Refinements for gateway handoff:
handoff:${sessionId}:${agentId}:${transcriptHash}.handoffCopiedAt only after a successful memory write.Post-handoff routing is purely structural.
ChatEvent./api/internal/identity/resolve with platform and platform ID.resolveAgentServer and forwardToServer in
packages/cloud-services/gateway-webhook/src/server-router.ts;packages/cloud-shared/src/lib/services/agent-gateway-router.ts for
sandbox/local-session routing.Negative identity cache entries must be short-lived and invalidated on link confirmation. Otherwise the message immediately after linking may incorrectly continue onboarding.
GATEWAY_BOOTSTRAP_SECRET.packages/cloud-services/gateway-webhook/src/auth.ts and
packages/cloud-api/internal/_auth.AGENT_SERVER_SHARED_SECRET through
X-Server-Token.{
"platform": "telegram",
"messageId": "platform-message-id",
"chatId": "platform-chat-id",
"senderId": "platform-user-id",
"senderName": "display name",
"text": "message text",
"mediaUrls": [],
"rawPayloadRef": "optional-redacted-debug-reference"
}
{
"sessionId": "platform:telegram:123",
"message": "hello",
"platform": "telegram",
"platformUserId": "123",
"platformDisplayName": "Ada"
}
Internal gateway callers may include internal auth, which marks the delivery as trusted for transport authenticity. Transport authenticity is not the same as durable account linking.
{
"success": true,
"userId": "user-id",
"organizationId": "org-id",
"agentId": "agent-id",
"data": {
"user": { "id": "user-id", "organizationId": "org-id" },
"agent": { "id": "agent-id", "status": "running" },
"identity": { "telegramId": "123" }
}
}
agentId should be nullable in the route response schema, but the gateway
should treat a missing agentId as an onboarding/provisioning condition, not
as an agent-server routing target.
forwardWithRetry.handoffCopiedAt unset so the next onboarding turn retries.bluebubbles cloud gateway adapter or relay service distinct from
blooio.users or move to a
first-class linked identities table for multi-account-per-platform support.