platform-docs/product/features/streaming/technical-specification.md

16 KiB

Streaming Companion -- Technical Specification

Last Updated: 2026-02-27

Architecture, API surface, WebSocket protocol, and data model for the Streaming Companion feature.


Service Architecture

The Streaming Companion runs as a dedicated NestJS microservice within the Lilith platform's service mesh.

Component Technology Address
Backend API NestJS Port 3130
Database PostgreSQL Port 25468
Cache / WebSocket Adapter Redis Port 26398
Frontend React (styled-components, TanStack Query, Socket.IO client) Served via deployment domain

The backend bootstraps with @lilith/service-nestjs-bootstrap and registers with @lilith/service-registry. Health checks are provided by @lilith/nestjs-health.

Redis serves two purposes: it backs the Socket.IO adapter for horizontal scaling of WebSocket connections, and it provides caching for frequently accessed data (active session state, chatbot config).


REST API

All endpoints are prefixed with /api and require authentication via the platform's SSO token unless otherwise noted.

Sessions -- /api/sessions

Method Path Description
POST /api/sessions Start a new session
GET /api/sessions List sessions for the authenticated creator (paginated, filterable)
GET /api/sessions/:id Get session detail with tips, goals, and notes
PATCH /api/sessions/:id Update session (title, notes, viewer count)
POST /api/sessions/:id/end End an active session
POST /api/sessions/:id/cancel Cancel an active session
GET /api/sessions/:id/export Export session data (query param: `format=csv

Tips -- /api/tips

Method Path Description
POST /api/tips Record a tip for the active session
GET /api/tips List tips (filterable by session, date range)
DELETE /api/tips/:id Delete a tip (correction)
POST /api/tips/import Bulk import tips from CSV

Goals -- /api/goals

Method Path Description
POST /api/goals Create a tip goal for a session
GET /api/goals List goals for a session
PATCH /api/goals/:id Update goal (title, target, sort order)
POST /api/goals/:id/cancel Cancel a goal
DELETE /api/goals/:id Delete a goal (only if no tips attributed)

Analytics -- /api/analytics

Method Path Description
GET /api/analytics/summary Aggregated stats across sessions (date range, platform filter)
GET /api/analytics/trends Time-series data (tips per hour, session duration, etc.)
GET /api/analytics/export Bulk export all session data (query param: `format=csv

Chatbot -- /api/chatbot

Method Path Description
GET /api/chatbot/config Get chatbot configuration for the authenticated creator
PUT /api/chatbot/config Update chatbot configuration (enabled, rate limits, personas JSONB)
GET /api/chatbot/templates List response templates
POST /api/chatbot/templates Create a response template
PATCH /api/chatbot/templates/:id Update a response template
DELETE /api/chatbot/templates/:id Delete a response template

Tip Menu -- /api/menu

Method Path Description
GET /api/menu Get tip menu for the authenticated creator
POST /api/menu Create a menu item
PATCH /api/menu/:id Update a menu item
DELETE /api/menu/:id Delete a menu item
PATCH /api/menu/reorder Reorder menu items (body: { items: [{ id, sortOrder }] })

WebSocket Protocol

The Streaming Companion uses Socket.IO for real-time communication. Two namespaces serve different access levels.

Authenticated Namespace -- /streaming

Requires a valid SSO token. Used by the creator's dashboard.

Client-to-Server Events:

Event Payload Description
join_session { sessionId: string } Subscribe to real-time updates for a session
leave_session { sessionId: string } Unsubscribe from session updates
update_viewers { sessionId: string, count: number } Report current viewer count from the cam platform
add_tip { sessionId: string, amountCents: number, tipperName?: string, message?: string } Record a tip via WebSocket (alternative to REST)
chatbot_message { sessionId: string, personaName: string, userMessage: string, userName: string } Route a chat message through the chatbot system

Server-to-Client Events:

Event Payload Description
session_updated { session: StreamSession } Session state changed (viewer count, duration tick)
tip_received { tip: StreamTip, sessionTotals: { totalCents, count, avgCents } } A tip was recorded
goal_progress { goal: TipGoal } A goal's progress was updated
goal_completed { goal: TipGoal } A goal reached its target
chatbot_response { personaName: string, responseText: string, triggeredBy: string } Chatbot generated a response
animation_trigger { type: string, data: object } Trigger an overlay animation (tip notification, goal completion)
menu_updated { menu: TipMenuItem[] } Tip menu was modified

Anonymous Overlay Namespace -- /streaming/overlay

No authentication required. Used by OBS browser source overlays. Read-only -- clients cannot emit events that modify state.

Server-to-Client Events (subset of authenticated namespace):

Event Payload Description
tip_received { tipperName, amountCents, message } Tip notification (sanitized, no internal IDs)
goal_progress { title, targetCents, currentCents, percentComplete } Goal progress update
goal_completed { title, targetCents } Goal completion trigger
animation_trigger { type, data } Animation event
menu_updated { items: { title, priceCents, category }[] } Menu update (public fields only)

The overlay namespace joins a session by room ID passed as a query parameter: /streaming/overlay?session=<sessionId>. The session ID is embedded in the overlay URL that the creator copies from the dashboard.


Data Model

StreamSessionEntity

The root entity for a streaming session.

Column Type Description
id uuid Primary key
creatorId uuid FK to the creator's user account
status enum active, ended, cancelled
platform enum chaturbate, stripchat, myfreecams, bongacams, camsoda, other
platformCustom varchar Custom platform name when platform is other
title varchar Optional session title
startedAt timestamptz When the session was started
endedAt timestamptz When the session was ended or cancelled (null if active)
durationSeconds integer Computed duration (null if active)
peakViewers integer Highest viewer count observed
lastViewerCount integer Most recently reported viewer count
totalTipsCents integer Denormalized sum of all tips (cents)
tipCount integer Denormalized count of all tips
notes text Free-form session notes (post-session)
metadata jsonb Extensible metadata (platform-specific data, tags)
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: creatorId, status, platform, startedAt

StreamTipEntity

Individual tip records within a session.

Column Type Description
id uuid Primary key
sessionId uuid FK to StreamSessionEntity
creatorId uuid FK to creator (denormalized for query performance)
amountCents integer Tip amount in cents
currency varchar Currency code (default: USD)
tipperName varchar Display name of the tipper (as entered by creator)
message text Message attached to the tip
source enum manual, platform_api, import
receivedAt timestamptz When the tip was received
paymentTransactionId uuid Optional FK to platform payment transaction (for API-sourced tips)
createdAt timestamptz Record creation timestamp

Indexes: sessionId, creatorId, receivedAt

TipGoalEntity

Tip goals within a session.

Column Type Description
id uuid Primary key
sessionId uuid FK to StreamSessionEntity
creatorId uuid FK to creator
title varchar Goal description
targetCents integer Target amount in cents
currentCents integer Current accumulated amount in cents
status enum active, completed, cancelled
sortOrder integer Display order within the session
completedAt timestamptz When the goal was completed (null if not)
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: sessionId, status

SessionNoteEntity

Checklist items and notes within a session.

Column Type Description
id uuid Primary key
sessionId uuid FK to StreamSessionEntity
creatorId uuid FK to creator
noteType enum checklist, note, reminder
content text Note text
sortOrder integer Display order
completed boolean Whether the checklist item is checked off
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: sessionId

ChatbotConfigEntity

Per-creator chatbot configuration.

Column Type Description
id uuid Primary key
creatorId uuid FK to creator (unique)
enabled boolean Master toggle for the chatbot
personas jsonb Array of persona definitions: [{ name, enabled, description }]
rateLimitPerMinute integer Max bot responses per minute
perUserCooldownSeconds integer Min seconds between responses to the same user
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: creatorId (unique)

ChatbotResponseTemplateEntity

Response templates assigned to chatbot personas.

Column Type Description
id uuid Primary key
configId uuid FK to ChatbotConfigEntity
personaName varchar Which persona this template belongs to (matches personas[].name)
category varchar Template category (schedule, pricing, rules, faq, custom)
triggerPhrases text[] Array of phrases that activate this template
responseText text The response the bot sends
priority integer Higher priority templates match first
enabled boolean Whether this template is active
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: configId, personaName

TipMenuItemEntity

Persistent tip menu items (not session-specific).

Column Type Description
id uuid Primary key
creatorId uuid FK to creator
title varchar Menu item title
description text Optional description
priceCents integer Price in cents
category enum performance, request, interaction, custom
isActive boolean Whether the item appears in the menu
sortOrder integer Display order
createdAt timestamptz Record creation timestamp
updatedAt timestamptz Record update timestamp

Indexes: creatorId, isActive


Domain Events

The Streaming Companion emits domain events through @lilith/domain-events for cross-service integration:

Event Payload Consumers
streaming.session.started { sessionId, creatorId, platform, startedAt } Analytics pipeline, activity feed
streaming.session.ended { sessionId, creatorId, durationSeconds, totalTipsCents, tipCount } Analytics pipeline, earnings dashboard
streaming.tip.received { tipId, sessionId, creatorId, amountCents, source } Earnings dashboard, achievement system
streaming.goal.completed { goalId, sessionId, creatorId, title, targetCents } Achievement system, notification service

Frontend Architecture

The Streaming Companion frontend is a React application within the Lilith deployment system:

  • Routing: @lilith/ui-router (wrapper around react-router)
  • Styling: @lilith/ui-styled-components (wrapper around styled-components, ensuring single ThemeProvider instance)
  • Data fetching: TanStack Query for REST API calls with automatic cache invalidation
  • Real-time: Socket.IO client connecting to /streaming namespace, with events wired into TanStack Query cache updates
  • Motion: @lilith/ui-motion for overlay animations (wrapper around framer-motion)
  • State: Local component state for UI concerns; server state via TanStack Query; WebSocket events for real-time synchronization

The overlay pages (/overlay/tips, /overlay/goals, /overlay/menu, /overlay/celebration) are separate lightweight routes designed for OBS browser source embedding. They connect to the /streaming/overlay namespace and render with transparent backgrounds.


Authentication and Authorization

  • Creator dashboard: Authenticated via platform SSO token (JWT). The token includes the creator's user ID, which scopes all queries to their data.
  • Overlay pages: Unauthenticated. The session ID in the URL acts as a capability token. Session IDs are UUIDs and are not guessable, but they are not secret -- anyone with the overlay URL can see the public overlay data.
  • API authorization: All REST endpoints validate that the authenticated creator owns the requested resource. A creator cannot access another creator's sessions, tips, or chatbot configuration.

Scaling Considerations

  • WebSocket horizontal scaling: Redis-backed Socket.IO adapter allows multiple backend instances to share WebSocket state. A creator's dashboard and their overlay can connect to different backend instances and still receive the same events.
  • Database denormalization: totalTipsCents and tipCount on StreamSessionEntity are denormalized to avoid aggregate queries on the tips table during live sessions. These are updated atomically when a tip is recorded.
  • Overlay connection limits: The anonymous overlay namespace is rate-limited by IP to prevent abuse. A single session typically has at most 4-5 overlay connections (one per OBS scene element).

  • README.md -- Feature overview and user stories
  • overview.md -- Product context and ecosystem fit
  • creator-guide.md -- How creators use the dashboard
  • Domain events: docs/architecture/event-flows.md
  • Service registry: @lilith/service-registry package documentation

Maintained By: The Collective Domain: Performer Suite