Skip to content

Cloud support operator guide

The optional plugins.cloud_support service backs the OSS console Help entry and lazy Lit widget on the public Cloud home, pricing and signup pages. It is disabled until instance configuration and operator validation succeed. Self-hosted public pages do not load the widget by default. This change does not install a provider app, send a real operator notification, merge, or deploy anything.

Configure the service

Apply core database migrations and use the standard EE plugin installation, which discovers cloud_support under preloop.plugins.proprietary. Proprietary plugin loading and RBAC must be enabled. Background outbox and operator listener tasks run with the plugin; database leases coordinate API replicas. Set these instance variables, never tenant chat-connection credentials:

Variable Purpose
CLOUD_SUPPORT_ORIGIN Mattermost HTTPS origin
CLOUD_SUPPORT_BOT_TOKEN Dedicated bot access token secret
CLOUD_SUPPORT_BOT_USER_ID Instance operator bot identity
CLOUD_SUPPORT_TEAM_ID Private operator team's ID
CLOUD_SUPPORT_CHANNEL_ID Private channel's ID
CLOUD_SUPPORT_OPERATOR_ID Authorized human Mattermost user ID
CLOUD_SUPPORT_CONSOLE_USER_ID Authorized active Preloop console user UUID
CLOUD_SUPPORT_PUBLIC_ORIGIN Canonical Cloud HTTPS origin for operator links
CLOUD_SUPPORT_OPERATOR_NAME Visitor-facing human name, for example Support

Startup verifies bot identity, the active console operator, the Mattermost human, private channel/team boundaries, and both users' team/channel membership. Changes require restarting the service. Mattermost traffic uses the core HTTPS/DNS-pinning policy; allow a self-hosted private subnet with MODEL_ENDPOINT_ALLOWED_CIDRS only when needed. System trust/SSL_CERT_FILE supports a private CA. Secrets are not included in feature flags, provider errors, analytics, or browser responses.

Documentation and independent support budget

Retrieval uses only a committed build snapshot, with source paths and SHA-256 checksums. Rebuild it from the pinned OSS checkout and public plan/brand sources:

python scripts/build_support_corpus.py
python scripts/build_support_corpus.py --check

The index reads public docs/overview/installation plus current build pricing and self-hosted terms. It never fetches visitor URLs or indexes tenant audit data. Answers return real documentation/pricing citations; unsupported questions offer human help. With no explicit support model configuration, answers are cited excerpts. Pricing snapshots describe the build and direct users to current terms.

For model-written documentation answers, configure a dedicated Preloop-funded service account/model/key, separate from customers' defaults, BYOK and credits:

  • CLOUD_SUPPORT_FUNDING_ACCOUNT_ID, CLOUD_SUPPORT_MODEL_ID, and CLOUD_SUPPORT_GATEWAY_KEY explicitly select the service account/model/key.
  • CLOUD_SUPPORT_DAILY_MICROUSD defaults to 2,000,000 ($2/day).
  • CLOUD_SUPPORT_REQUEST_MICROUSD defaults to 20,000 ($0.02/request).

Known pricing is required. A conservative maximum-input/output tariff reservation is atomic across replicas; unknown pricing, caps, or unavailable funding fall back to excerpts without a model call. There is one model round, no MCP tools, at most three bounded source excerpts, and at most 700 output tokens. Reservations remain charged after uncertain calls; the existing gateway meters actual usage against the support service account. In-flight model HTTP requests are cancelled promptly when ownership changes; generation checks still fence staged responses and sends. Configure the dedicated service account's gateway budgets as an additional spend control. Human replies and handoff work without model credit.

Inspect my setup is an explicit signed-in action. It calls existing authorized read APIs and forwards only bounded chat setup/worker/link statuses and visible model/default-configuration counts. It never authorizes by contact email and never returns tokens, external identity IDs, raw audit history, or unrestricted tools.

Mattermost operation

Every choice or user message creates/updates a durable conversation and ordered private operator thread. Impressions and dismissals create aggregate counters only. The first post contains source-page context, the user's verbatim question, and an authenticated console link. Follow-ups stay in that thread; assistant output is included after delivery to the visitor. Visitor-controlled mentions are neutralized.

In the thread, the configured human can send:

Command Effect
!takeover Claim human ownership and fence pending/in-flight assistant output
Any normal thread reply after takeover Deliver that human reply to the visitor
!resume Explicitly return to the assistant
!close Close the conversation
!reopen Explicitly reopen a closed conversation with human ownership

The console link also offers Take over, Return to assistant, Close and Reopen using authenticated endpoints restricted to the configured console user. No unsigned Mattermost callback's user_id is trusted. Socket events are hints: canonical posts and current operator/team/channel membership establish authority. Wrong users/channels/threads, bot/system echoes and duplicate posts are ignored. Replies before takeover are not relayed. An explicit visitor human request is highlighted and immediately fences assistant work, but does not promise an immediate human response. Optional reply contact is used only for requested offline follow-up, separately from account authorization.

The browser shows the owner/status and reconnects using persisted history. A scoped, unguessable session credential grants access to one anonymous conversation. Account conversations additionally require the original active account/user. The browser keeps this scoped credential in session storage, clears it on identity changes, and does not persist provider credentials.

Delivery and recovery

Notification claims serialize each conversation's messages, preserving one root. Definitive provider rate-limit rejections retry with bounded backoff; other explicit rejections fail visibly. The authenticated operator console can retry a definitively failed delivery after its cause is corrected; later notifications remain ordered behind it. A timeout, interrupted send or ambiguous provider failure becomes uncertain. The worker reconciles the bounded latest channel history using a server-owned delivery marker, author and channel before marking it delivered. It never blindly reposts an ambiguous send. Inspect the console conversation's delivery jobs and the Mattermost channel if a marker is outside that bounded history; do not manually retry until the prior outcome is established.

The operator socket uses a leased durable checkpoint and up to 300 posts for reconnect recovery. An oversized gap stops automatic recovery and leaves operator connectivity unavailable for inspection; it is not silently skipped. Browser history remains durable and available during operator-socket outages; connectivity is reported separately from the configured service availability. The assistant cannot publish an answer for a prior ownership generation after takeover. Transactions cover ownership and claims, never inference or provider network calls.

Public create/message/inspection/aggregate/history routes have bounded per-peer quotas (history allows 3,600 polls per hour per peer), conversation histories are capped, and retries use stable client event IDs. Behind a reverse proxy, configure the ASGI trusted proxy handling so its peer address reflects your intended quota boundary; arbitrary forwarded headers are not trusted. Conversations/messages/contact/outbox are deleted after CLOUD_SUPPORT_RETENTION_DAYS (default 30, allowed 1–90). Aggregate counters expire separately and contain no conversation/contact/chat content.

Optional prompt experiment

CLOUD_SUPPORT_PROACTIVE=true enables a session-level quiet-launcher versus choice experiment. The small prompt requires at least 30 seconds of active-page dwell and 12 seconds idle (CLOUD_SUPPORT_DWELL_MS, CLOUD_SUPPORT_IDLE_MS can increase these thresholds). Those values are configurable starting points, not measured optima. Hidden tabs, focused forms/typing, prior dismissal, conversion and a prior prompt suppress it. No full chat auto-opens; free text is always available and there is no email gate. Initial choices are single-select and page-specific.

Only finite impression/engagement/dismissal/activation dimensions are aggregated. Use them with activation/completed-integration outcomes and answer-quality, latency/cost/support-delivery measures. No chat content, credentials, IPs or contact information belong in experiment analytics.

Verification limits

Local synthetic PostgreSQL, TLS HTTP/WebSocket provider and browser fixtures cover authorization, deduplication, reconnection, leases, takeover/resume and delivery races. Live Mattermost/Slack/Discord installation and production load acceptance require a separate authorized environment; they are not claimed as verified here. Slack's native agent_view behavior and Mattermost Agents model/MCP integration are optional surfaces and are not implemented or required by this service.