diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
index 11061bc1b509a66e4136c9c568b88f2e60530bc5..bac82a01704e11e0dc1413739dff89084784528b 100644
--- a/src/ch/ch_conf.h
+++ b/src/ch/ch_conf.h
@@ -25,6 +25,7 @@
 #include "virthread.h"
 #include "ch_capabilities.h"
 #include "virebtables.h"
+#include "object_event.h"
 
 #define CH_DRIVER_NAME "CH"
 #define CH_CMD "cloud-hypervisor"
@@ -84,6 +85,9 @@ struct _virCHDriver
 
     /* Immutable pointer to host device manager */
     virHostdevManager *hostdevMgr;
+
+    /* Immutable pointer, self-locking APIs */
+    virObjectEventState *domainEventState;
 };
 
 #define CH_SAVE_MAGIC "libvirt-xml\n \0 \r"
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index 34f3a495fcef125d3d36811de2db437725ba2365..6a8da5f35678e2b877a52d7306e2b5319c12ec59 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -28,6 +28,7 @@
 #include "ch_monitor.h"
 #include "ch_process.h"
 #include "domain_cgroup.h"
+#include "domain_event.h"
 #include "datatypes.h"
 #include "driver.h"
 #include "viraccessapicheck.h"
@@ -305,6 +306,15 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
         ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
     }
 
+    if (ret == 0) {
+        virObjectEvent *event;
+
+        event = virDomainEventLifecycleNewFromObj(vm,
+                                                  VIR_DOMAIN_EVENT_STARTED,
+                                                  VIR_DOMAIN_EVENT_STARTED_BOOTED);
+        virObjectEventStateQueue(driver->domainEventState, event);
+    }
+
  endjob:
     virDomainObjEndJob(vm);
 
@@ -324,8 +334,10 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
 {
     virCHDriver *driver = conn->privateData;
     g_autoptr(virDomainDef) vmdef = NULL;
+    g_autoptr(virDomainDef) oldDef = NULL;
     virDomainObj *vm = NULL;
     virDomainPtr dom = NULL;
+    virObjectEvent *event = NULL;
     g_autofree char *managed_save_path = NULL;
     unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
 
@@ -346,7 +358,7 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
 
     if (!(vm = virDomainObjListAdd(driver->domains, &vmdef,
                                    driver->xmlopt,
-                                   0, NULL)))
+                                   0, &oldDef)))
         goto cleanup;
 
     /* cleanup if there's any stale managedsave dir */
@@ -359,11 +371,17 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
     }
 
     vm->persistent = 1;
-
+    event = virDomainEventLifecycleNewFromObj(vm,
+                                              VIR_DOMAIN_EVENT_DEFINED,
+                                              !oldDef ?
+                                              VIR_DOMAIN_EVENT_DEFINED_ADDED :
+                                              VIR_DOMAIN_EVENT_DEFINED_UPDATED);
     dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
 
  cleanup:
     virDomainObjEndAPI(&vm);
+    virObjectEventStateQueue(driver->domainEventState, event);
+
     return dom;
 }
 
@@ -379,6 +397,7 @@ chDomainUndefineFlags(virDomainPtr dom,
 {
     virCHDriver *driver = dom->conn->privateData;
     virDomainObj *vm;
+    virObjectEvent *event = NULL;
     int ret = -1;
 
     virCheckFlags(0, -1);
@@ -394,6 +413,9 @@ chDomainUndefineFlags(virDomainPtr dom,
                        "%s", _("Cannot undefine transient domain"));
         goto cleanup;
     }
+    event = virDomainEventLifecycleNewFromObj(vm,
+                                              VIR_DOMAIN_EVENT_UNDEFINED,
+                                              VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
 
     vm->persistent = 0;
     if (!virDomainObjIsActive(vm)) {
@@ -404,6 +426,8 @@ chDomainUndefineFlags(virDomainPtr dom,
 
  cleanup:
     virDomainObjEndAPI(&vm);
+    virObjectEventStateQueue(driver->domainEventState, event);
+
     return ret;
 }
 
@@ -644,6 +668,7 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
 {
     virCHDriver *driver = dom->conn->privateData;
     virDomainObj *vm;
+    virObjectEvent *event = NULL;
     int ret = -1;
 
     virCheckFlags(0, -1);
@@ -663,6 +688,9 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
     if (virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED) < 0)
         goto endjob;
 
+    event = virDomainEventLifecycleNewFromObj(vm,
+                                              VIR_DOMAIN_EVENT_STOPPED,
+                                              VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
     virCHDomainRemoveInactive(driver, vm);
     ret = 0;
 
@@ -671,6 +699,8 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
 
  cleanup:
     virDomainObjEndAPI(&vm);
+    virObjectEventStateQueue(driver->domainEventState, event);
+
     return ret;
 }
 
@@ -1367,6 +1397,7 @@ static int chStateCleanup(void)
     virObjectUnref(ch_driver->caps);
     virObjectUnref(ch_driver->domains);
     virObjectUnref(ch_driver->hostdevMgr);
+    virObjectUnref(ch_driver->domainEventState);
     virMutexDestroy(&ch_driver->lock);
     g_clear_pointer(&ch_driver, g_free);
 
@@ -1419,6 +1450,9 @@ chStateInitialize(bool privileged,
     if (!(ch_driver->hostdevMgr = virHostdevManagerGetDefault()))
         goto cleanup;
 
+    if (!(ch_driver->domainEventState = virObjectEventStateNew()))
+        goto cleanup;
+
     if ((rv = chExtractVersion(ch_driver)) < 0) {
         if (rv == -2)
             ret = VIR_DRV_STATE_INIT_SKIPPED;
@@ -2223,6 +2257,48 @@ chNodeGetMemoryStats(virConnectPtr conn,
     return virHostMemGetStats(cellNum, params, nparams, flags);
 }
 
+static int
+chConnectDomainEventRegisterAny(virConnectPtr conn,
+                                virDomainPtr dom,
+                                int eventID,
+                                virConnectDomainEventGenericCallback callback,
+                                void *opaque,
+                                virFreeCallback freecb)
+{
+    virCHDriver *driver = conn->privateData;
+    int ret = -1;
+
+    if (virConnectDomainEventRegisterAnyEnsureACL(conn) < 0)
+        return -1;
+
+    if (virDomainEventStateRegisterID(conn,
+                                      driver->domainEventState,
+                                      dom, eventID,
+                                      callback, opaque, freecb, &ret) < 0)
+        ret = -1;
+
+    return ret;
+}
+
+
+static int
+chConnectDomainEventDeregisterAny(virConnectPtr conn,
+                                  int callbackID)
+{
+    virCHDriver *driver = conn->privateData;
+
+    if (virConnectDomainEventDeregisterAnyEnsureACL(conn) < 0)
+        return -1;
+
+    if (virObjectEventStateDeregisterID(conn,
+                                        driver->domainEventState,
+                                        callbackID, true) < 0)
+        return -1;
+
+    return 0;
+}
+
+
 /* Function Tables */
 static virHypervisorDriver chHypervisorDriver = {
     .name = "CH",
@@ -2281,6 +2357,8 @@ static virHypervisorDriver chHypervisorDriver = {
     .domainRestore = chDomainRestore,                       /* 10.2.0 */
     .domainRestoreFlags = chDomainRestoreFlags,             /* 10.2.0 */
     .nodeGetMemoryStats = chNodeGetMemoryStats,             /* 10.10.0 */
+    .connectDomainEventRegisterAny = chConnectDomainEventRegisterAny,       /* 10.10.0 */
+    .connectDomainEventDeregisterAny = chConnectDomainEventDeregisterAny,   /* 10.10.0 */
 };
 
 static virConnectDriver chConnectDriver = {