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:
| Field | Type | Purpose |
|---|---|---|
handle | string | Cache key; identifies the deployment |
platform | MessagingBackends | Which channel adapter to register |
settings | object | Per-deployment settings (merged into tenant settings) |
tenant | Tenant | Nested tenant object (see below) |
tenant.contentCompiled | BotContentCompiled | Compiled conversation graph and responses |
tenant.componentTree.schema | ComponentTree | Flow topology used by FlowBuilder |
tenant.settings | TenantSettings | Session timeout, NLU config, live chat settings |
tenant.systemSettings | TenantSystemSettings | Internal platform settings |
tenant.nlpMap | { [id]: NLPPipeline } | NLU pipeline map (optional) |
tenant.nlpTreeMap | { [id]: NLPPipelineTreeHeader } | NLU tree pipeline map (optional) |
tenant.nluAgents | TenantNluAgent[] | NLU agent configs (optional) |
tenant.powerups | Powerup[] | Powerup list |
tenant.plugins | Plugin[] | Integration plugins (live chat, LLM, etc.) |
tenant.subscriptionPlan | SubscriptionPlan | Plan 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:
| Operation | API call | When |
|---|---|---|
| Load state | SubscribersClient.findByHandle(userId) | LoadStateStep at start of every request |
| Create subscriber | SubscribersClient.create(subscriber) | First message from a new user |
| Update subscriber | SubscribersClient.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:
| Field | Source |
|---|---|
tenantId | blackboard.tenantId |
status | ACTIVE on create; COMPLETE on close |
subscriberHandle | blackboard.userID |
language | blackboard.language.activeLanguageCode |
metadata.deploymentId/Name | blackboard.deploymentId/Name |
metadata.platformOrigin | blackboard.platformOrigin |
tags | Merged from sessionTags / sessionTagsRemoved in blackboard |
csatSurveys | Processed from CSATDataManager |
variables | From sessionVariableNames in blackboard |
messages | Batch-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:
| type | payload fields |
|---|---|
LLM | Record<string, unknown> (provider-specific) |
HTTP | Record<string, unknown> (request/response details) |
SEMANTIC_SEARCH | Record<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 variablessurvey.*-- survey variablespreviousSessionSurvey.*contact.*- Exact keys:
csatInput,csatSurveys
Redis Keys
| Key pattern | Value type | TTL | Written by | Read by |
|---|---|---|---|---|
<deployment-handle> | String (JSON) | REDIS_TENANT_TTL (default 7 days) | BotDeploymentCache.replaceCached | BotDeploymentCache.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:
| Key | Type | Set by |
|---|---|---|
variables.* | any | Conversation node variable actions |
publicInfo.* | object | Subscriber info (name, email, custom data) |
sessionTags | string[] | Tag operations |
sessionTagsRemoved | string[] | Tag remove operations |
csatSurveys | array | CSAT survey data |
contact.* | object | Contact resolver |
Session tier (_session.data)
Written with setSessionValue. Cleared on session expiry or resetSession. Also serialised and saved to the Subscriber document.
Common keys:
| Key | Type | Set by |
|---|---|---|
variables.* | any | Static variables initialised from BotDeployment.tenant.contentCompiled.metadata.variables at session start |
sessionMessages | ChatSessionMessageForm[] | SessionTrackerStep |
sessionVariableNames | string[] | Data collector |
survey.* | object | Survey components |
Ephemeral tier (ephemeral)
Written with setEphemeralValue. Never persisted. Cleared at loadState via resetEphemeral.
Key examples:
| Key | Set by | Purpose |
|---|---|---|
triggeredIntent | ConversationManager | NLU result for the current turn |
responses / responsesIds | NLG | Selected outbound responses |
newSubscriber | LoadStateStep | First-message flag |
skipInternalAnalytics | LoadStateStep | Analytics opt-out (uptime tester, skip flag) |
skipExternalAnalytics | LoadStateStep | External analytics opt-out |
isUserInteraction | LoadStateStep | Whether turn counts as user interaction |
liveChat | LoadStateStep | LiveChat state object |
liveChatEventType | Channel adapters | LCG event type |
flow_test_mode | ConversationStateManager | Flow test flag |
interactionMetadata.events | InteractionMetadata | Accumulated events for Kafka publish |
templateData | ConversationStateManager | Template variable data from event metadata |
previousSessionId | Session reset action | Ensures 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.
| Key | Type | Set by |
|---|---|---|
auth.isAuthenticated | boolean | AuthenticateUserAction (via strategy) |
auth.sub | string | Token claims |
auth.email | string | Token claims |
auth.name | string | Token claims |
auth.provider | string | Strategy identifier |
auth.authenticatedAt | string | ISO timestamp |
auth.rawToken | string | Original token (used for on-change-only comparison) |
auth.refreshToken | string | Refresh token (if available) |
auth.claims.* | any | All 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.