Skip to main content

EVENT_SCHEMAS — bff-backoffice-service

Sibling: API_CONTRACTS · APPLICATION_LOGIC · DOMAIN_MODEL

Cross-cutting: 04 Event-Driven Architecture · Standards · NAMING

This BFF publishes only telemetry. It owns no domain state and emits no domain events. Subjects live under melmastoon.bff.backoffice.*. All payloads carry a hashed-PII posture (no plain email, phone, or name reaches Pub/Sub).

1. Scope

ClassPatternSource of truthPosture
Telemetrymelmastoon.bff.backoffice.*This BFFHashed PII; sampling per-subject
Cache invalidation (consumed)melmastoon.<service>.<aggregate>.<verb>.v<n>Producing serviceWe do not publish; we only consume
Domain eventsn/aWe do not publish anyn/a

2. Event envelope (universal)

Per 04 §3:

{
"envelope": {
"eventId": "evt_01H8YN7QV4D8KZ4F8Y5CK4MV3D",
"subject": "melmastoon.bff.backoffice.dashboard.viewed.v1",
"version": 1,
"occurredAt": "2026-04-23T09:14:22.041Z",
"publishedAt": "2026-04-23T09:14:22.058Z",
"producer": "bff-backoffice-service",
"producerInstance": "bff-bo-asia-south1-7f8d9c-x4z2",
"tenantId": "tnt_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"sessionId": "bos_01H8Y...",
"propertyId": "prop_01H8Y...",
"requestId": "req_01H8YN7QF9RTBRZG4F8Y5CK4MV",
"traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
"causationId": "evt_01H8YN7QV4D8KZ4F8Y5CK4MV3C",
"correlationId": "req_01H8YN7QF9RTBRZG4F8Y5CK4MV",
"schemaUri": "https://schemas.melmastoon.ghasi.io/bff-backoffice/dashboard-viewed/v1.json",
"retentionClass": "operational",
"samplingRate": 0.25,
"appVersion": "1.4.2",
"appPlatform": "win32"
},
"payload": { /* per subject */ }
}

3. Topics + sampling

SubjectTopicSamplingRetention
melmastoon.bff.backoffice.session.opened.v1melmastoon.bff.backoffice.session1.0regulated (7y audit)
melmastoon.bff.backoffice.session.closed.v1same1.0regulated
melmastoon.bff.backoffice.dashboard.viewed.v1melmastoon.bff.backoffice.activity0.25operational (90 d)
melmastoon.bff.backoffice.workbench.opened.v1same0.25operational
melmastoon.bff.backoffice.ai_suggestion.decided.v1melmastoon.bff.backoffice.ai1.0regulated
melmastoon.bff.backoffice.alert.acknowledged.v1melmastoon.bff.backoffice.alerts1.0audit (365 d)
melmastoon.bff.backoffice.operator.activity.v1melmastoon.bff.backoffice.activity0.10operational
melmastoon.bff.backoffice.device.heartbeat.v1melmastoon.bff.backoffice.devices0.05operational (30 d)
melmastoon.bff.backoffice.sync.cursor_advanced.v1melmastoon.bff.backoffice.sync1.0operational
melmastoon.bff.backoffice.sync.handshake_completed.v1same1.0operational
melmastoon.bff.backoffice.lock.action_proxied.v1melmastoon.bff.backoffice.locks1.0regulated (7y)

Sampling rates are flag-controlled; runtime overrides via bff-backoffice-flags Memorystore key with 30 s refresh.

4. Schema — session.opened.v1

{
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"sessionId": "bos_01H8Y...",
"tenantId": "tnt_01H8Y...",
"propertyScope": ["prop_01H8Y..."],
"primaryPropertyId": "prop_01H8Y...",
"roles": ["front_desk_supervisor"],
"locale": "ps-AF",
"appVersion": "1.4.2",
"appPlatform": "win32",
"ipHash": "sha256:abc123...",
"fingerprintHash": "sha256:def456...",
"openedAt": "2026-04-23T09:14:22Z"
}

5. Schema — session.closed.v1

{
"sessionId": "bos_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"openedAt": "2026-04-23T09:14:22Z",
"closedAt": "2026-04-23T17:42:01Z",
"reason": "sign_out|expired|revoked|device_replaced|version_upgrade",
"activityCount": 184,
"lockProxyCount": 7,
"mutationProxyCount": 41
}

6. Schema — dashboard.viewed.v1

{
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"operatorRole": "front_desk_supervisor",
"viewedAt": "2026-04-23T09:14:22Z",
"renderMode": "composed|cached",
"partial": false,
"missingWidgets": [],
"latencyMs": 412
}

7. Schema — workbench.opened.v1

{
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"view": "arrivals|departures|in_house|today|housekeeping_board|maintenance_board",
"openedAt": "2026-04-23T09:14:22Z",
"renderMode": "composed|cached",
"itemsRendered": 12,
"latencyMs": 240
}

8. Schema — ai_suggestion.decided.v1

{
"suggestionId": "sg_01H8Y...",
"inboxEntryId": "aim_01H8Y...",
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"category": "housekeeping_reorder",
"severity": "attention",
"decision": {
"outcome": "accepted|rejected|modified",
"modifiedDelta": null,
"notesPresent": false
},
"decidedAt": "2026-04-23T09:14:22Z",
"msSinceCreated": 3640012,
"provenance": {
"model": "ghasi-ops-v3",
"modelVersion": "3.2.1",
"promptVersion": "hk-reorder-v5",
"modelClass": "cloud",
"signatureFingerprint": "sha256:..."
}
}

notesPresent: true|false because notes can carry guest names; the boolean is sufficient signal for analytics.

9. Schema — alert.acknowledged.v1

{
"alertId": "alt_01H8Y...",
"inboxEntryId": "ali_01H8Y...",
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"category": "lock_failed|oversold|payment_failed|sync_lag|device_offline|security|other",
"severity": "info|warning|critical",
"raisedAt": "2026-04-23T08:42:14Z",
"acknowledgedAt": "2026-04-23T09:14:22Z",
"msFromRaiseToAck": 1928000,
"notesPresent": true
}

10. Schema — operator.activity.v1

{
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"sessionId": "bos_01H8Y...",
"occurredAt": "2026-04-23T09:14:22Z",
"category": "view|mutation_proxy|lock_action|ai_decision|alert_ack|sync_handshake|preferences_changed|auth|other",
"action": "reservation.check_in",
"resourceRef": { "kind": "reservation", "id": "rsv_01H8Y..." },
"outcome": "success|failure|partial",
"latencyMs": 412,
"errorCode": null
}

Sampling 10% by default; raised to 100% during incident windows for the affected tenant via flag override.

11. Schema — device.heartbeat.v1

{
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"deviceId": "dev_01H8Y...",
"operatorId": "opr_01H8Y...",
"occurredAt": "2026-04-23T09:14:22Z",
"appVersion": "1.4.2",
"appPlatform": "win32",
"networkProfile": "good|flaky|offline-recently",
"memoryRssMb": 412,
"cpuPctOneMin": 6.4,
"outboxHints": [
{ "aggregate": "folio.charge", "count": 2, "oldestAgeSeconds": 42 }
],
"lastSyncCursorPresent": true
}

Sampling 5% (heartbeat is high-frequency).

12. Schema — sync.cursor_advanced.v1

{
"tenantId": "tnt_01H8Y...",
"deviceId": "dev_01H8Y...",
"operatorId": "opr_01H8Y...",
"advanceKind": "pull|push|both",
"cursorBefore": "ck_v1_...",
"cursorAfter": "ck_v1_...",
"advancedAt": "2026-04-23T09:14:22Z",
"msSinceLastAdvance": 84000
}

13. Schema — sync.handshake_completed.v1

{
"tenantId": "tnt_01H8Y...",
"deviceId": "dev_01H8Y...",
"operatorId": "opr_01H8Y...",
"appVersion": "1.4.2",
"capabilities": ["bulk-pull-v1","push-batched-v1","resumable-v1"],
"outcome": "ok|reauth_required|version_blocked",
"latencyMs": 220,
"occurredAt": "2026-04-23T09:14:22Z",
"cursorAccepted": "ck_v1_..."
}

14. Schema — lock.action_proxied.v1

{
"tenantId": "tnt_01H8Y...",
"propertyId": "prop_01H8Y...",
"operatorId": "opr_01H8Y...",
"deviceId": "dev_01H8Y...",
"reservationId": "rsv_01H8Y...",
"vendor": "ttlock|salto|assa-vostio|wiegand-generic",
"action": "issue|revoke|rebind|extend",
"outcome": "success|failure",
"errorCode": null,
"mfaAttestationUsed": false,
"occurredAt": "2026-04-23T09:14:22Z",
"upstreamLatencyMs": 614
}

Lock-action events are 100% sampled and 7-year retained as part of the regulated audit lake.

15. Consumed events (cache invalidation only)

SubjectAction
melmastoon.iam.session.revoked.v1Drop session; SSE forceLogout
melmastoon.iam.user.disabled.v1Tear down all sessions for operator
melmastoon.ai.suggestion.created.v1Insert into AISuggestionInbox; SSE ai.new
melmastoon.ai.suggestion.invalidated.v1Drop entry; SSE ai.removed
melmastoon.alert.raised.v1Insert into AlertInbox; SSE alerts.new
melmastoon.alert.resolved.v1Drop entry; SSE alerts.removed
melmastoon.theme.published.v1Invalidate keyboard shortcut help (if branded)
melmastoon.reservation.checked_in.v1Invalidate dashboard + arrivals + in-house caches for property
melmastoon.reservation.checked_out.v1Invalidate dashboard + departures + in-house caches
melmastoon.housekeeping.task.created.v1Invalidate housekeeping board cache
melmastoon.housekeeping.task.transitioned.v1Same
melmastoon.maintenance.workorder.opened.v1Invalidate maintenance board cache
melmastoon.maintenance.workorder.transitioned.v1Same
melmastoon.tenant.operator_role.updated.v1Drop OperatorPreferences mirror; refresh on next request

Inbox dedup by eventId via standard inbox table (24h dedup window).

16. Schema registry

All payload schemas live in @ghasi/event-envelope/schemas/bff-backoffice/<event>/vN.json. CI verifies that every published subject has a registered schema. Pact verifies producer + consumer.

17. Compatibility

  • Forward-compatible additions allowed within vN.
  • Renames or removals require vN+1 with consumer migration window.
  • Subject vN retained for 90 days after vN+1 GA.

18. Dead-letter

Pub/Sub DLQ topics: melmastoon.bff.backoffice.<group>.dlq.v1. DLQ depth alert at 50; SRE inspects payload (sanitized) and replays after fix.

19. PII posture verification

A nightly synthetic PII probe (telemetry-pii.spec.ts) injects fake PII into a test run; the test passes only if no plain-text PII appears in any published event payload. Pepper rotation does not affect publish-side checks (which look for raw values, not hashes).