Standalone engine · Vanilla JavaScript · Firebase + Firestore

Your frontend renders.
Khaya keeps it correct.

Khaya Engine is the tested layer between a vanilla JavaScript interface and Firebase. It turns user actions into serializable commands, keeps every screen synchronized, and handles confirmation, conflicts, and rollback without owning a single HTML element.

Frontend
Always vanilla JavaScript
Production backend
Firebase + Cloud Firestore
Core dependencies
Zero

It separates interface decisions from data correctness.

A new frontend should replace its presentation without reimplementing real-time coordination. Khaya gives every vanilla frontend the same small contract: render state, execute commands, observe lifecycle.

Your frontend owns

Everything people see and touch.

  • HTML and semantic structure
  • CSS, branding, and responsive layout
  • Rendering and event delegation
  • Accessibility and interaction design
Khaya Engine owns

Everything that keeps state predictable.

  • Serializable state and commands
  • Optimistic presentation and rollback
  • Revisions, conflicts, and subscriptions
  • Lifecycle, errors, and structured events
Firebase adapter owns

Everything specific to the backend.

  • Firestore listeners and transactions
  • Firebase Authentication sessions
  • Offline and reconnect signals
  • SDK objects and transport errors

The boundary rule: DOM nodes never enter the engine. Firebase objects never enter the engine. Customer identity and project configuration never enter this repository.

Four layers. One direction of dependency.

The interface depends on the engine; the engine does not know the interface exists. Firebase stays replaceable at the code boundary even though it is Khaya's only supported production backend.

ApplicationVanilla frontendrender() · click handlers · forms
LifecycleVanilla bindingmount() · execute() · dispose()
BehaviorEngine corestate · commands · revisions · rollback
Production persistenceFirebase adapterFirestore · Auth · transactions · listeners

Down Serializable commands

Up State, status, events, errors

Change stock once. Follow every layer.

This safe documentation demo uses the memory adapter. The engine and binding are identical in production; the Firebase example changes only the adapter.

Frontend AOwner controls
Connecting
Frontend BCustomer view
Connecting

Both frontends are independent consumers of the same engine contract.

The smallest supported implementation.

There are only three integration decisions: create the adapter, provide a render callback, and translate interface events into commands.

Install

Add the engine and Firebase SDK.

Firebase is a peer dependency so the frontend controls its Firebase app, authentication, and project configuration.

npm install khaya-engine firebase
  1. 1
    Initialize Firebase

    Your application owns its dedicated project configuration.

  2. 2
    Create the Firestore adapter

    This is the only production persistence boundary.

  3. 3
    Mount the vanilla binding

    Pass state to your own renderer.

  4. 4
    Execute commands from UI events

    No Firebase calls belong in click handlers.

  5. 5
    Dispose on teardown

    The binding detaches subscriptions and listeners cleanly.

import { initializeApp } from "firebase/app"
import { getFirestore } from "firebase/firestore"
import { createRealtimeEngine } from "khaya-engine"
import { createFirebaseFirestoreAdapter }
  from "khaya-engine/firebase"
import { createVanillaBinding }
  from "khaya-engine/vanilla"

const app = initializeApp(firebaseConfig)
const adapter = createFirebaseFirestoreAdapter({
  firestore: getFirestore(app),
  initialState,
})
const engine = createRealtimeEngine({ adapter })
const binding = createVanillaBinding({
  engine,
  render(state, metadata) {
    renderCatalogue(state, metadata)
  },
})

await binding.mount()
From any click handler

Send intent, not storage instructions.

The same command drives optimistic state, the Firestore transaction, correlation events, and every subscribed frontend.

await binding.execute({
  type: "stock.adjust",
  categoryId: "catalogue",
  itemId: "coffee",
  delta: -1,
})

One repeatable setup for every new frontend.

Each implementation gets isolated Firebase infrastructure. The engine code stays unchanged; only application configuration, data shape, rendering, and branding vary.

01

Create a dedicated Firebase project

Register a Web app and enable Cloud Firestore plus Email/Password Authentication.

02

Copy the Firebase example

Replace the public Web configuration and choose the Firestore document path.

03

Provision an owner

Create the Auth user and add its UID to the protected khayaEngineOwners collection.

04

Test Security Rules

Use the Firebase emulator to verify public reads, owner writes, revision increments, and denied access.

05

Build the real frontend

Replace markup and rendering while keeping the binding, adapter, and command boundary.

Infrastructure stays isolated.

Never reuse another customer's Firebase project, owner UID, data, deployment target, or credentials. Firebase Web configuration belongs to the consuming frontend; service-account credentials never belong in browser code.

A deliberately small public vocabulary.

Lifecyclemount()unmount()dispose()
Statesubscribe()getState()getRevision()
Commandscategory.updateitem.updateitem.additem.removestock.adjust
Signalsstatestatuseventerror