Skip to main content

Data Model: hbf-bot

Domain objects hbf-bot reads and writes, Kafka message schema, Redis keys, and blackboard tiers. hbf-bot does not own a database. All persistent state lives in hbf-core or Kafka.

Domain Objects via hbf-core-api

BotDeployment

Read by HBFCoreBotDeploymentStorage. Fetched on first use per deployment handle, then cached in Redis.

Key fields consumed at runtime:

FieldTypePurpose
handlestringCache key; identifies the deployment
platformMessagingBackendsWhich channel adapter to register
settingsobjectPer-deployment settings (merged into tenant settings)
tenantTenantNested tenant object (see below)
tenant.contentCompiledBotContentCompiledCompiled conversation graph and responses
tenant.componentTree.schemaComponentTreeFlow topology used by FlowBuilder
tenant.settingsTenantSettingsSession timeout, NLU config, live chat settings
tenant.systemSettingsTenantSystemSettingsInternal platform settings
tenant.nlpMap{ [id]: NLPPipeline }NLU pipeline map (optional)
tenant.nlpTreeMap{ [id]: NLPPipelineTreeHeader }NLU tree pipeline map (optional)
tenant.nluAgentsTenantNluAgent[]NLU agent configs (optional)
tenant.powerupsPowerup[]Powerup list
tenant.pluginsPlugin[]Integration plugins (live chat, LLM, etc.)
tenant.subscriptionPlanSubscriptionPlanPlan controlling feature gating

Fetched with options: includeTenant, includeTenantSettings, includeTenantSystemSettings, includeTenantPipelines, includeTenantPlugins, includeComponentTree, includeContentCompiled.

Subscribers

Read and written by CoreSubscribersStorage (app/system/storage/subscribers/CoreSubscriberStorage.ts).

Each Subscriber document in hbf-core stores the entire BlackboardData blob (see Blackboard Tiers below) in its blackboard field. hbf-bot serialises the event and stores a snapshot on every request.

Operations:

OperationAPI callWhen
Load stateSubscribersClient.findByHandle(userId)LoadStateStep at start of every request
Create subscriberSubscribersClient.create(subscriber)First message from a new user
Update subscriberSubscribersClient.save(subscriberId, data)ConversationStateManager.saveState at end of every request

ChatSessions

Read and written by ChatSessionsRepository (app/system/storage/sessions/ChatSessionsRepository.ts).

A ChatSession tracks one conversation session (bounded by inactivity timeout). hbf-bot creates or increments a session on every message via SessionTrackerStep.

Key fields written:

FieldSource
tenantIdblackboard.tenantId
statusACTIVE on create; COMPLETE on close
subscriberHandleblackboard.userID
languageblackboard.language.activeLanguageCode
metadata.deploymentId/Nameblackboard.deploymentId/Name
metadata.platformOriginblackboard.platformOrigin
tagsMerged from sessionTags / sessionTagsRemoved in blackboard
csatSurveysProcessed from CSATDataManager
variablesFrom sessionVariableNames in blackboard
messagesBatch-created from sessionMessages in blackboard

Groups

Read and written by HBFCoreGroupRepository (app/system/groups/HBFCoreGroupRepository.ts).

Used by flow components to manage subscriber group membership (e.g., segment targeting).

Operations: create, getByTenantAndHandle, update, delete, findAllByTenant.

NlpPipelines

Read via NlpPipelinesClient.process (hbf-core NLU mode) or directly against hbf-nlp (HBF_NLP mode). Config lives in BotDeployment.tenant.nlpMap / nlpTreeMap.

Kafka Messages

Topic: hbf.interaction.metadata (default; override via KAFKA_TOPIC).

Published by KafkaEventPublisher after each processed event (when KAFKA_ENABLED=true).

MetadataEvent schema

interface MetadataEvent<T extends MetadataType> {
hbfEventId: string; // ID of the inbound HBFEvent
tenantId: string; // Tenant object ID
organizationId: string;
sessionId: string; // Session UUID from ExpandedBlackboard
timestamp: Date;
type: MetadataType; // "LLM" | "HTTP" | "SEMANTIC_SEARCH" | "VARIABLE" | "TAG"
payload: PayloadByType[T];
hasError?: boolean;
duration?: number; // milliseconds
friendlyName?: string;
}

Payload shapes by type:

typepayload fields
LLMRecord<string, unknown> (provider-specific)
HTTPRecord<string, unknown> (request/response details)
SEMANTIC_SEARCHRecord<string, unknown> (query, results)
VARIABLE{ key, oldValue, newValue }
TAG{ current: string[], added: string[], removed: string[] }

Kafka message envelope:

key:     sessionId (string)
value: JSON.stringify(MetadataEvent)
headers:
tenantId: Buffer
organizationId: Buffer
eventType: Buffer (MetadataEvent.type)

Events are accumulated in the ephemeral blackboard under interactionMetadata.events during the lifecycle, then flushed and published at the end of PowerupStep.

Captured variable prefixes (from InteractionMetadata.recordVariablesChangelog):

  • variables.* -- session variables
  • survey.* -- survey variables
  • previousSessionSurvey.*
  • contact.*
  • Exact keys: csatInput, csatSurveys

Redis Keys

Key patternValue typeTTLWritten byRead by
<deployment-handle>String (JSON)REDIS_TENANT_TTL (default 7 days)BotDeploymentCache.replaceCachedBotDeploymentCache.getCached

Value envelope:

interface CachedBotDeployment {
data: BotDeployment;
timestamp: Date;
}

Cache invalidation: DELETE /api/tenants/cache flushes one handle or the entire database (FLUSHDB). Cache is also bypassed when the stored timestamp is older than the tenant's updatedAt in hbf-core.

No pub/sub keys are used.

Blackboard Tiers

ExpandedBlackboard (app/system/blackboard/Blackboard.ts) manages three storage tiers for per-user state.

Persistent tier (data)

Written with setPersistentValue / setValue. Serialised into BlackboardData.data and saved to the Subscriber document in hbf-core.

Common keys:

KeyTypeSet by
variables.*anyConversation node variable actions
publicInfo.*objectSubscriber info (name, email, custom data)
sessionTagsstring[]Tag operations
sessionTagsRemovedstring[]Tag remove operations
csatSurveysarrayCSAT survey data
contact.*objectContact resolver

Session tier (_session.data)

Written with setSessionValue. Cleared on session expiry or resetSession. Also serialised and saved to the Subscriber document.

Common keys:

KeyTypeSet by
variables.*anyStatic variables initialised from BotDeployment.tenant.contentCompiled.metadata.variables at session start
sessionMessagesChatSessionMessageForm[]SessionTrackerStep
sessionVariableNamesstring[]Data collector
survey.*objectSurvey components

Ephemeral tier (ephemeral)

Written with setEphemeralValue. Never persisted. Cleared at loadState via resetEphemeral.

Key examples:

KeySet byPurpose
triggeredIntentConversationManagerNLU result for the current turn
responses / responsesIdsNLGSelected outbound responses
newSubscriberLoadStateStepFirst-message flag
skipInternalAnalyticsLoadStateStepAnalytics opt-out (uptime tester, skip flag)
skipExternalAnalyticsLoadStateStepExternal analytics opt-out
isUserInteractionLoadStateStepWhether turn counts as user interaction
liveChatLoadStateStepLiveChat state object
liveChatEventTypeChannel adaptersLCG event type
flow_test_modeConversationStateManagerFlow test flag
interactionMetadata.eventsInteractionMetadataAccumulated events for Kafka publish
templateDataConversationStateManagerTemplate variable data from event metadata
previousSessionIdSession reset actionEnsures ChatSession message integrity across resets

Auth store (_session.data.auth)

Managed exclusively by setAuthData(), getAuthData(), clearAuthData(). setSessionValue('auth.*') throws to enforce immutability. Stored within the session tier but accessed through dedicated methods.

KeyTypeSet by
auth.isAuthenticatedbooleanAuthenticateUserAction (via strategy)
auth.substringToken claims
auth.emailstringToken claims
auth.namestringToken claims
auth.providerstringStrategy identifier
auth.authenticatedAtstringISO timestamp
auth.rawTokenstringOriginal token (used for on-change-only comparison)
auth.refreshTokenstringRefresh token (if available)
auth.claims.*anyAll additional token claims spread

Template access: {{Auth.email}}, {{Auth.sub}}, etc. via AuthVariablesResolver. Auth.isAuthenticated, Auth.rawToken, and Auth.refreshToken are blocked from template resolution.

Key expiration

ExpandedBlackboard.setExpiration(key, seconds) marks a key for auto-deletion on next access. Expiration metadata is persisted alongside data and session in the Subscriber document (BlackboardData.keyExpirations).

Changelog

ExpandedBlackboard.changelog (BlackboardChangelogItem[]) tracks mutations within one request lifecycle. Used by InteractionMetadata to record variable and tag changes for Kafka. Cleared by clearChangelog() at the start of loadState.

Blacklist (never tracked): conversationData, conversationStats.