[gnome-software/wip/hughsie/libxmlb: 35/35] Use libxmlb to parse AppStream XML



commit d301d4968923177618a454e24490dda76bb379fb
Author: Richard Hughes <richard hughsie com>
Date:   Mon Sep 24 13:35:31 2018 +0100

    Use libxmlb to parse AppStream XML

 lib/gs-utils.c                                     |    6 +
 meson.build                                        |    3 +-
 plugins/core/gs-appstream.c                        | 1733 ++++++++++++--------
 plugins/core/gs-appstream.h                        |   38 +-
 plugins/core/gs-desktop-common.c                   |    2 +-
 plugins/core/gs-plugin-appstream.c                 |  955 ++++++-----
 plugins/core/gs-self-test.c                        |    6 +-
 plugins/core/meson.build                           |    6 +-
 plugins/dummy/gs-self-test.c                       |   10 +-
 plugins/external-appstream/gs-install-appstream.c  |   48 +-
 plugins/external-appstream/meson.build             |    5 +-
 plugins/flatpak/gs-flatpak.c                       |  551 ++++---
 plugins/flatpak/gs-plugin-flatpak.c                |    6 +
 plugins/flatpak/gs-self-test.c                     |   28 +-
 plugins/flatpak/meson.build                        |    6 +-
 plugins/modalias/gs-self-test.c                    |    7 +
 .../shell-extensions/gs-plugin-shell-extensions.c  |  164 +-
 plugins/shell-extensions/gs-self-test.c            |   17 +-
 plugins/shell-extensions/meson.build               |    6 +-
 19 files changed, 2134 insertions(+), 1463 deletions(-)
---
diff --git a/lib/gs-utils.c b/lib/gs-utils.c
index 23d24995..7805483b 100644
--- a/lib/gs-utils.c
+++ b/lib/gs-utils.c
@@ -165,11 +165,17 @@ gs_utils_get_cache_filename (const gchar *kind,
                             GsUtilsCacheFlags flags,
                             GError **error)
 {
+       const gchar *tmp;
        g_autofree gchar *basename = NULL;
        g_autofree gchar *cachedir = NULL;
        g_autoptr(GFile) cachedir_file = NULL;
        g_autoptr(GPtrArray) candidates = g_ptr_array_new_with_free_func (g_free);
 
+       /* in the self tests */
+       tmp = g_getenv ("GS_SELF_TEST_CORE_DATADIR");
+       if (tmp != NULL)
+               return g_build_filename (tmp, kind, resource, NULL);
+
        /* get basename */
        if (flags & GS_UTILS_CACHE_FLAG_USE_HASH) {
                g_autofree gchar *basename_tmp = g_path_get_basename (resource);
diff --git a/meson.build b/meson.build
index dbb7ca63..caef2171 100644
--- a/meson.build
+++ b/meson.build
@@ -96,8 +96,9 @@ add_project_arguments('-D_GNU_SOURCE', language : 'c')
 
 conf.set('HAVE_LINUX_UNISTD_H', cc.has_header('linux/unistd.h'))
 
-appstream_glib = dependency('appstream-glib', version : '>= 0.7.11')
+appstream_glib = dependency('appstream-glib', version : '>= 0.7.14')
 gdk_pixbuf = dependency('gdk-pixbuf-2.0', version : '>= 2.32.0')
+libxmlb = dependency('xmlb', version : '>= 0.1.1')
 gio_unix = dependency('gio-unix-2.0')
 gmodule = dependency('gmodule-2.0')
 gtk = dependency('gtk+-3.0', version : '>= 3.22.4')
diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c
index 030fad77..2199237c 100644
--- a/plugins/core/gs-appstream.c
+++ b/plugins/core/gs-appstream.c
@@ -28,95 +28,202 @@
 #define        GS_APPSTREAM_MAX_SCREENSHOTS    5
 
 GsApp *
-gs_appstream_create_app (GsPlugin *plugin, AsApp *item, GError **error)
+gs_appstream_create_app (GsPlugin *plugin, XbSilo *silo, XbNode *component, GError **error)
 {
-       const gchar *unique_id = as_app_get_unique_id (item);
-       GsApp *app = gs_plugin_cache_lookup (plugin, unique_id);
-
-       /* if the app we found has the "match-any-prefix" quirk and our item does
-        * not, then we create a new one because ours will be "complete", and
-        * using the mentioned quirk will lead to a different behavior (e.g. it'll
-        * be refined using refine_wildcard, it won't allow a management plugin to
-        * be set, etc.)  */
-       if (app != NULL && gs_app_has_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX) &&
-           !as_app_has_quirk (item, AS_APP_QUIRK_MATCH_ANY_PREFIX)) {
-               g_debug ("Looking for %s, got %s but has 'match-any-prefix' quirk "
-                        "so we create a new one instead.",
-                        unique_id, gs_app_get_unique_id (app));
-               g_clear_object (&app);
-       }
-
-       if (app == NULL) {
-               app = gs_app_new (NULL);
-               gs_app_set_from_unique_id (app, unique_id);
-               /* clear origin set from unique_id: appstream origin goes to
-                * GsApp's origin-appstream field instead */
-               gs_app_set_origin (app, NULL);
-               gs_app_set_metadata (app, "GnomeSoftware::Creator",
-                                    gs_plugin_get_name (plugin));
-               if (!gs_appstream_refine_app (plugin, app, item, error)) {
-                       g_object_unref (app);
-                       return NULL;
+       GsApp *app;
+       g_autoptr(GsApp) app_new = gs_app_new (NULL);
+
+       /* refine enough to get the unique ID */
+       if (!gs_appstream_refine_app (plugin, app_new, silo, component,
+                                     GS_PLUGIN_REFINE_FLAGS_DEFAULT,
+                                     error))
+               return NULL;
+
+       /* look for existing object */
+       g_debug ("looking for existing %s", gs_app_get_unique_id (app_new));
+       app = gs_plugin_cache_lookup (plugin, gs_app_get_unique_id (app_new));
+       if (app != NULL)
+               return app;
+
+       /* use the temp object we just created */
+       gs_app_set_metadata (app_new, "GnomeSoftware::Creator",
+                            gs_plugin_get_name (plugin));
+       gs_plugin_cache_add (plugin, NULL, app_new);
+       return g_steal_pointer (&app_new);
+}
+
+static gchar *
+gs_appstream_format_description (XbNode *root, GError **error)
+{
+       g_autoptr(GString) str = g_string_new (NULL);
+       g_autoptr(XbNode) n = xb_node_get_child (root);
+
+       while (n != NULL) {
+               g_autoptr(XbNode) n2 = NULL;
+
+               /* support <p>, <ul>, <ol> and <li>, ignore all else */
+               if (g_strcmp0 (xb_node_get_element (n), "p") == 0) {
+                       g_string_append_printf (str, "%s\n\n", xb_node_get_text (n));
+               } else if (g_strcmp0 (xb_node_get_element (n), "ul") == 0) {
+                       g_autoptr(GPtrArray) children = xb_node_get_children (n);
+                       for (guint i = 0; i < children->len; i++) {
+                               XbNode *nc = g_ptr_array_index (children, i);
+                               if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) {
+                                       g_string_append_printf (str, " • %s\n",
+                                                               xb_node_get_text (nc));
+                               }
+                       }
+                       g_string_append (str, "\n");
+               } else if (g_strcmp0 (xb_node_get_element (n), "ol") == 0) {
+                       g_autoptr(GPtrArray) children = xb_node_get_children (n);
+                       for (guint i = 0; i < children->len; i++) {
+                               XbNode *nc = g_ptr_array_index (children, i);
+                               if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) {
+                                       g_string_append_printf (str, " %u. %s\n",
+                                                               i + 1,
+                                                               xb_node_get_text (nc));
+                               }
+                       }
+                       g_string_append (str, "\n");
                }
-               gs_plugin_cache_add (plugin, unique_id, app);
+
+               n2 = xb_node_get_next (n);
+               g_set_object (&n, n2);
        }
-       return app;
+
+       /* remove extra newlines */
+       while (str->len > 0 && str->str[str->len - 1] == '\n')
+               g_string_truncate (str, str->len - 1);
+
+       /* success */
+       return g_string_free (g_steal_pointer (&str), FALSE);
+}
+
+static gchar *
+gs_appstream_build_icon_prefix (XbNode *component)
+{
+       const gchar *origin;
+       const gchar *tmp;
+       gint npath;
+       g_auto(GStrv) path = NULL;
+       g_autoptr(XbNode) components = NULL;
+
+       /* no parent, e.g. AppData */
+       components = xb_node_get_parent (component);
+       if (components == NULL)
+               return NULL;
+
+       /* set explicitly */
+       tmp = xb_node_query_text (components, "info/icon-prefix", NULL);
+       if (tmp != NULL)
+               return g_strdup (tmp);
+
+       /* fall back to origin */
+       origin = xb_node_get_attr (components, "origin");
+       if (origin == NULL)
+               return NULL;
+
+       /* no metadata */
+       tmp = xb_node_query_text (components, "info/filename", NULL);
+       if (tmp == NULL)
+               return NULL;
+
+       /* check format */
+       path = g_strsplit (tmp, "/", -1);
+       npath = g_strv_length (path);
+       if (npath < 3 || g_strcmp0 (path[npath-2], "xmls") != 0)
+               return NULL;
+
+       /* fix the new path */
+       g_free (path[npath-1]);
+       g_free (path[npath-2]);
+       path[npath-1] = g_strdup (origin);
+       path[npath-2] = g_strdup ("icons");
+       return g_strjoinv ("/", path);
 }
 
 static AsIcon *
-gs_appstream_get_icon_by_kind (AsApp *app, AsIconKind icon_kind)
+gs_appstream_new_icon (XbNode *component, XbNode *n, AsIconKind icon_kind, guint sz)
 {
-       GPtrArray *icons = as_app_get_icons (app);
-       for (guint i = 0; i < icons->len; i++) {
-               AsIcon *icon = g_ptr_array_index (icons, i);
-               if (as_icon_get_kind (icon) == icon_kind)
-                       return icon;
+       AsIcon *icon = as_icon_new ();
+       g_autofree gchar *icon_path = NULL;
+       as_icon_set_kind (icon, icon_kind);
+       as_icon_set_name (icon, xb_node_get_text (n));
+       if (sz == 0)
+               sz = xb_node_get_attr_as_uint (n, "width");
+       if (sz > 0) {
+               as_icon_set_width (icon, sz);
+               as_icon_set_height (icon, sz);
        }
-       return NULL;
+       icon_path = gs_appstream_build_icon_prefix (component);
+       if (icon_path != NULL)
+               as_icon_set_prefix (icon, icon_path);
+       return icon;
 }
 
 static AsIcon *
-gs_appstream_get_icon_by_kind_and_size (AsApp *app, AsIconKind icon_kind, guint sz)
+gs_appstream_get_icon_by_kind (XbNode *component, AsIconKind icon_kind)
 {
-       GPtrArray *icons = as_app_get_icons (app);
-       for (guint i = 0; i < icons->len; i++) {
-               AsIcon *icon = g_ptr_array_index (icons, i);
-               if (as_icon_get_kind (icon) == icon_kind &&
-                   as_icon_get_width (icon) == sz &&
-                   as_icon_get_height (icon) == sz)
-                       return icon;
-       }
-       return NULL;
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(XbNode) icon = NULL;
+
+       xpath = g_strdup_printf ("icon[@type='%s']",
+                                as_icon_kind_to_string (icon_kind));
+       icon = xb_node_query_first (component, xpath, NULL);
+       if (icon == NULL)
+               return NULL;
+       return gs_appstream_new_icon (component, icon, icon_kind, 0);
+}
+
+static AsIcon *
+gs_appstream_get_icon_by_kind_and_size (XbNode *component, AsIconKind icon_kind, guint sz)
+{
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(XbNode) icon = NULL;
+
+       xpath = g_strdup_printf ("icon[@type='%s'][@height='%u'][@width='%u']",
+                                as_icon_kind_to_string (icon_kind), sz, sz);
+       icon = xb_node_query_first (component, xpath, NULL);
+       if (icon == NULL)
+               return NULL;
+       return gs_appstream_new_icon (component, icon, icon_kind, sz);
 }
 
 static void
-gs_refine_item_icon (GsPlugin *plugin, GsApp *app, AsApp *item)
+gs_refine_item_icon (GsPlugin *plugin, GsApp *app, XbNode *component)
 {
-       AsIcon *icon;
+       g_autoptr(AsIcon) icon = NULL;
+       g_autoptr(XbNode) n = NULL;
 
        /* try a stock icon first */
-       icon = gs_appstream_get_icon_by_kind (item, AS_ICON_KIND_STOCK);
-       if (icon != NULL)
+       icon = gs_appstream_get_icon_by_kind (component, AS_ICON_KIND_STOCK);
+       if (icon != NULL) {
                gs_app_add_icon (app, icon);
+               return;
+       }
 
        /* if HiDPI get a 128px cached icon */
        if (gs_plugin_get_scale (plugin) == 2) {
-               icon = gs_appstream_get_icon_by_kind_and_size (item,
+               icon = gs_appstream_get_icon_by_kind_and_size (component,
                                                               AS_ICON_KIND_CACHED,
                                                               128);
-               if (icon != NULL)
+               if (icon != NULL) {
                        gs_app_add_icon (app, icon);
+                       return;
+               }
        }
 
        /* non-HiDPI cached icon */
-       icon = gs_appstream_get_icon_by_kind_and_size (item,
+       icon = gs_appstream_get_icon_by_kind_and_size (component,
                                                       AS_ICON_KIND_CACHED,
                                                       64);
-       if (icon != NULL)
+       if (icon != NULL) {
                gs_app_add_icon (app, icon);
+               return;
+       }
 
        /* prefer local */
-       icon = gs_appstream_get_icon_by_kind (item, AS_ICON_KIND_LOCAL);
+       icon = gs_appstream_get_icon_by_kind (component, AS_ICON_KIND_LOCAL);
        if (icon != NULL) {
                /* does not exist, so try to find using the icon theme */
                if (as_icon_get_kind (icon) == AS_ICON_KIND_LOCAL &&
@@ -126,191 +233,264 @@ gs_refine_item_icon (GsPlugin *plugin, GsApp *app, AsApp *item)
                        as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
                }
                gs_app_add_icon (app, icon);
+               return;
        }
 
-       /* remote as a last resort */
-       icon = gs_appstream_get_icon_by_kind (item, AS_ICON_KIND_REMOTE);
-       if (icon != NULL)
+       /* remote URL */
+       icon = gs_appstream_get_icon_by_kind (component, AS_ICON_KIND_REMOTE);
+       if (icon != NULL) {
                gs_app_add_icon (app, icon);
+               return;
+       }
+
+       /* assume a stock icon */
+       n = xb_node_query_first (component, "icon", NULL);
+       if (n != NULL) {
+               icon = gs_appstream_new_icon (component, n, AS_ICON_KIND_STOCK, 0);
+               gs_app_add_icon (app, icon);
+       }
 }
 
 static gboolean
 gs_appstream_refine_add_addons (GsPlugin *plugin,
                                GsApp *app,
-                               AsApp *item,
+                               XbSilo *silo,
                                GError **error)
 {
-       GPtrArray *addons;
-
-       /* we only care about addons to desktop apps */
-       if (gs_app_get_kind (app) != AS_APP_KIND_DESKTOP)
-               return TRUE;
-
-       addons = as_app_get_addons (item);
-       if (addons == NULL)
-               return TRUE;
-
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) addons = NULL;
+
+       /* get all components */
+       xpath = g_strdup_printf ("components/component/extends[text()='%s']/..",
+                                gs_app_get_id (app));
+       addons = xb_silo_query (silo, xpath, 0, &error_local);
+       if (addons == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < addons->len; i++) {
-               AsApp *as_addon = g_ptr_array_index (addons, i);
-               g_autoptr(GsApp) addon = NULL;
-
-               addon = gs_appstream_create_app (plugin, as_addon, error);
-               if (addon == NULL)
-                       return FALSE;
-
-               /* add all the data we can */
-               if (!gs_appstream_refine_app (plugin, addon, as_addon, error))
+               XbNode *addon = g_ptr_array_index (addons, i);
+               g_autoptr(GsApp) app2 = NULL;
+               app2 = gs_appstream_create_app (plugin, silo, addon, error);
+               if (app2 == NULL)
                        return FALSE;
-               gs_app_add_addon (app, addon);
+               gs_app_add_addon (app, app2);
        }
        return TRUE;
 }
 
-static void
-gs_appstream_refine_add_screenshots (GsApp *app, AsApp *item)
+static gboolean
+gs_appstream_refine_add_images (GsApp *app, AsScreenshot *ss, XbNode *screenshot, GError **error)
 {
-       GPtrArray *screenshots_as;
-
-       /* do we have any to add */
-       screenshots_as = as_app_get_screenshots (item);
-       if (screenshots_as->len == 0)
-               return;
-
-       /* does the app already have some */
-       gs_app_add_kudo (app, GS_APP_KUDO_HAS_SCREENSHOTS);
-       if (gs_app_get_screenshots(app)->len > 0)
-               return;
-
-       /* add any we know */
-       for (guint i = 0; i < screenshots_as->len &&
-                         i < GS_APPSTREAM_MAX_SCREENSHOTS; i++) {
-               AsScreenshot *ss = g_ptr_array_index (screenshots_as, i);
-               GPtrArray *images_as = as_screenshot_get_images (ss);
-               if (images_as->len == 0)
-                       continue;
-               if (as_screenshot_get_kind (ss) == AS_SCREENSHOT_KIND_UNKNOWN)
-                       continue;
-               gs_app_add_screenshot (app, ss);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) images = NULL;
+
+       /* get all components */
+       images = xb_node_query (screenshot, "image", 0, &error_local);
+       if (images == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < images->len; i++) {
+               XbNode *image = g_ptr_array_index (images, i);
+               g_autoptr(AsImage) im = as_image_new ();
+               as_image_set_height (im, xb_node_get_attr_as_uint (image, "height"));
+               as_image_set_width (im, xb_node_get_attr_as_uint (image, "width"));
+               as_image_set_kind (im, as_image_kind_from_string (xb_node_get_attr (image, "type")));
+               as_image_set_url (im, xb_node_get_text (image));
+               as_screenshot_add_image (ss, g_steal_pointer (&im));
        }
+
+       /* success */
+       return TRUE;
 }
 
-static void
-gs_appstream_refine_add_reviews (GsApp *app, AsApp *item)
+static gboolean
+gs_appstream_refine_add_screenshots (GsApp *app, XbNode *component, GError **error)
 {
-       GPtrArray *reviews;
-
-       /* do we have any to add */
-       if (gs_app_get_reviews(app)->len > 0)
-               return;
-       reviews = as_app_get_reviews (item);
-       for (guint i = 0; i < reviews->len; i++) {
-               AsReview *review = g_ptr_array_index (reviews, i);
-               gs_app_add_review (app, review);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) screenshots = NULL;
+
+       /* get all components */
+       screenshots = xb_node_query (component, "screenshots/screenshot", 0, &error_local);
+       if (screenshots == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < screenshots->len; i++) {
+               XbNode *screenshot = g_ptr_array_index (screenshots, i);
+               g_autoptr(AsScreenshot) ss = as_screenshot_new ();
+               if (!gs_appstream_refine_add_images (app, ss, screenshot, error))
+                       return FALSE;
+               gs_app_add_screenshot (app, g_steal_pointer (&ss));
        }
+
+       /* FIXME: move into no refine flags section? */
+       if (screenshots ->len > 0)
+               gs_app_add_kudo (app, GS_APP_KUDO_HAS_SCREENSHOTS);
+
+       /* success */
+       return TRUE;
 }
 
-static void
-gs_appstream_refine_add_provides (GsApp *app, AsApp *item)
+static gboolean
+gs_appstream_refine_add_provides (GsApp *app, XbNode *component, GError **error)
 {
-       GPtrArray *provides;
-
-       /* do we have any to add */
-       if (gs_app_get_provides(app)->len > 0)
-               return;
-       provides = as_app_get_provides (item);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) provides = NULL;
+
+       /* get all components */
+       provides = xb_node_query (component, "provides/*", 0, &error_local);
+       if (provides == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < provides->len; i++) {
-               AsProvide *provide = g_ptr_array_index (provides, i);
-               gs_app_add_provide (app, provide);
+               XbNode *provide = g_ptr_array_index (provides, i);
+               g_autoptr(AsProvide) pr = as_provide_new ();
+               as_provide_set_kind (pr, as_provide_kind_from_string (xb_node_get_element (provide)));
+               as_provide_set_value (pr, xb_node_get_text (provide));
+               gs_app_add_provide (app, g_steal_pointer (&pr));
        }
+
+       /* success */
+       return TRUE;
 }
 
 static gboolean
-gs_appstream_is_recent_release (AsApp *app)
+gs_appstream_is_recent_release (XbNode *component)
 {
-       AsRelease *release;
-       GPtrArray *releases;
+       guint64 ts;
        guint64 secs;
 
        /* get newest release */
-       releases = as_app_get_releases (app);
-       if (releases->len == 0)
+       ts = xb_node_query_attr_as_uint (component, "releases/release", "timestamp", NULL);
+       if (ts == G_MAXUINT64)
                return FALSE;
-       release = g_ptr_array_index (releases, 0);
 
        /* is last build less than one year ago? */
-       secs = ((guint64) g_get_real_time () / G_USEC_PER_SEC) -
-               as_release_get_timestamp (release);
+       secs = ((guint64) g_get_real_time () / G_USEC_PER_SEC) - ts;
        return secs / (60 * 60 * 24) < 365;
 }
 
-static void
-gs_appstream_copy_metadata (GsApp *app, AsApp *item)
+static gboolean
+gs_appstream_copy_metadata (GsApp *app, XbNode *component, GError **error)
 {
-       GHashTable *hash = as_app_get_metadata (item);
-       g_autoptr(GList) keys = g_hash_table_get_keys (hash);
-       for (GList *l = keys; l != NULL; l = l->next) {
-               const gchar *key = l->data;
-               const gchar *value = g_hash_table_lookup (hash, key);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) values = NULL;
+
+       /* get all components */
+       values = xb_node_query (component, "custom/value", 0, &error_local);
+       if (values == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < values->len; i++) {
+               XbNode *value = g_ptr_array_index (values, i);
+               const gchar *key = xb_node_get_attr (value, "key");
+               if (key == NULL)
+                       continue;
                if (gs_app_get_metadata_item (app, key) != NULL)
                        continue;
-               gs_app_set_metadata (app, key, value);
-       }
-}
-
-static void
-gs_refine_item_management_plugin (GsPlugin *plugin, GsApp *app, AsApp *item)
-{
-       GPtrArray *bundles;
-       const gchar *management_plugin = NULL;
-
-       /* allow override */
-       management_plugin = as_app_get_metadata_item (item, "GnomeSoftware::Plugin");
-       if (management_plugin != NULL)
-               gs_app_set_management_plugin (app, management_plugin);
-
-       /* find the default bundle kind */
-       bundles = as_app_get_bundles (item);
-       for (guint i = 0; i < bundles->len; i++) {
-               AsBundle *bundle = g_ptr_array_index (bundles, i);
-               gs_app_add_source (app, as_bundle_get_id (bundle));
+               gs_app_set_metadata (app, key, xb_node_get_text (value));
        }
+       return TRUE;
 }
 
 static gboolean
 gs_appstream_refine_app_updates (GsPlugin *plugin,
                                 GsApp *app,
-                                AsApp *item,
+                                XbSilo *silo,
+                                XbNode *component,
                                 GError **error)
 {
        AsUrgencyKind urgency_best = AS_URGENCY_KIND_UNKNOWN;
-       GPtrArray *releases;
-       g_autoptr(GPtrArray) updates_list = NULL;
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GHashTable) installed = g_hash_table_new (g_str_hash, g_str_equal);
+       g_autoptr(GPtrArray) releases_inst = NULL;
+       g_autoptr(GPtrArray) releases = NULL;
+       g_autoptr(GPtrArray) updates_list = g_ptr_array_new ();
 
        /* only for UPDATABLE apps */
        if (!gs_app_is_updatable (app))
                return TRUE;
 
-       /* make a list of valid updates */
-       updates_list = g_ptr_array_new ();
-       releases = as_app_get_releases (item);
+       /* find out which releases are already installed */
+       xpath = g_strdup_printf ("component/id[text()='%s']/../releases/*[@version]",
+                                gs_app_get_id (app));
+       releases_inst = xb_silo_query (silo, xpath, 0, &error_local);
+       if (releases_inst == NULL) {
+               if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
+                   !g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
+                       g_propagate_error (error, g_steal_pointer (&error_local));
+                       return FALSE;
+               }
+       } else {
+               for (guint i = 0; i < releases_inst->len; i++) {
+                       XbNode *release = g_ptr_array_index (releases_inst, i);
+                       g_hash_table_insert (installed,
+                                            (gpointer) xb_node_get_attr (release, "version"),
+                                            (gpointer) release);
+               }
+       }
+       g_clear_error (&error_local);
+
+       /* get all components */
+       releases = xb_node_query (component, "releases/*", 0, &error_local);
+       if (releases == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < releases->len; i++) {
-               AsRelease *rel = g_ptr_array_index (releases, i);
+               XbNode *release = g_ptr_array_index (releases, i);
+               const gchar *version = xb_node_get_attr (release, "version");
+               g_autoptr(XbNode) description = NULL;
+               AsUrgencyKind urgency_tmp;
+
+               /* ignore releases with no version */
+               if (version == NULL)
+                       continue;
 
                /* already installed */
-               g_debug ("installable update %s [%u]",
-                        as_release_get_version (rel),
-                        as_release_get_state (rel));
-               if (as_release_get_state (rel) == AS_RELEASE_STATE_INSTALLED)
+               if (g_hash_table_lookup (installed, version) != NULL)
                        continue;
 
                /* use the 'worst' urgency, e.g. critical over enhancement */
-               if (as_release_get_urgency (rel) > urgency_best)
-                       urgency_best = as_release_get_urgency (rel);
+               urgency_tmp = as_urgency_kind_from_string (xb_node_get_attr (release, "urgency"));
+               if (urgency_tmp > urgency_best)
+                       urgency_best = urgency_tmp;
 
                /* add updates with a description */
-               if (as_release_get_description (rel, NULL) == NULL)
+               description = xb_node_query_first (release, "description", NULL);
+               if (description == NULL)
                        continue;
-               g_ptr_array_add (updates_list, rel);
+               g_ptr_array_add (updates_list, release);
        }
 
        /* only set if known */
@@ -319,33 +499,26 @@ gs_appstream_refine_app_updates (GsPlugin *plugin,
 
        /* no prefix on each release */
        if (updates_list->len == 1) {
+               XbNode *release = g_ptr_array_index (updates_list, 0);
+               g_autoptr(XbNode) n = NULL;
                g_autofree gchar *desc = NULL;
-               AsRelease *rel = g_ptr_array_index (updates_list, 0);
-               desc = as_markup_convert (as_release_get_description (rel, NULL),
-                                         AS_MARKUP_CONVERT_FORMAT_SIMPLE,
-                                         error);
-               if (desc == NULL) {
-                       gs_utils_error_convert_appstream (error);
-                       return FALSE;
-               }
+               n = xb_node_query_first (release, "description", NULL);
+               desc = gs_appstream_format_description (n, NULL);
                gs_app_set_update_details (app, desc);
 
        /* get the descriptions with a version prefix */
        } else if (updates_list->len > 1) {
                g_autoptr(GString) update_desc = g_string_new ("");
                for (guint i = 0; i < updates_list->len; i++) {
+                       XbNode *release = g_ptr_array_index (updates_list, i);
                        g_autofree gchar *desc = NULL;
-                       AsRelease *rel = g_ptr_array_index (updates_list, i);
-                       desc = as_markup_convert (as_release_get_description (rel, NULL),
-                                                 AS_MARKUP_CONVERT_FORMAT_SIMPLE,
-                                                 error);
-                       if (desc == NULL) {
-                               gs_utils_error_convert_appstream (error);
-                               return FALSE;
-                       }
+                       g_autoptr(XbNode) n = NULL;
+
+                       n = xb_node_query_first (release, "description", NULL);
+                       desc = gs_appstream_format_description (n, NULL);
                        g_string_append_printf (update_desc,
                                                "Version %s:\n%s\n\n",
-                                               as_release_get_version (rel),
+                                               xb_node_get_attr (release, "version"),
                                                desc);
                }
 
@@ -356,10 +529,10 @@ gs_appstream_refine_app_updates (GsPlugin *plugin,
        }
 
        /* if there is no already set update version use the newest */
-       if (gs_app_get_update_version (app) == NULL) {
-               AsRelease *rel = as_app_get_release_default (item);
-               if (rel != NULL)
-                       gs_app_set_update_version (app, as_release_get_version (rel));
+       if (gs_app_get_update_version (app) == NULL &&
+           updates_list->len > 0) {
+               XbNode *release = g_ptr_array_index (updates_list, 0);
+               gs_app_set_update_version (app, xb_node_get_attr (release, "version"));
        }
 
        /* success */
@@ -386,29 +559,6 @@ _gs_utils_locale_has_translations (const gchar *locale)
        return TRUE;
 }
 
-static AsBundleKind
-gs_appstream_get_bundle_kind (AsApp *item)
-{
-       GPtrArray *bundles;
-       GPtrArray *pkgnames;
-
-       /* prefer bundle */
-       bundles = as_app_get_bundles (item);
-       if (bundles->len > 0) {
-               AsBundle *bundle = g_ptr_array_index (bundles, 0);
-               if (as_bundle_get_kind (bundle) != AS_BUNDLE_KIND_UNKNOWN)
-                       return as_bundle_get_kind (bundle);
-       }
-
-       /* fallback to packages */
-       pkgnames = as_app_get_pkgnames (item);
-       if (pkgnames->len > 0)
-               return AS_BUNDLE_KIND_PACKAGE;
-
-       /* nothing */
-       return AS_BUNDLE_KIND_UNKNOWN;
-}
-
 static gboolean
 gs_appstream_origin_valid (const gchar *origin)
 {
@@ -420,46 +570,101 @@ gs_appstream_origin_valid (const gchar *origin)
 }
 
 static gboolean
-gs_appstream_is_valid_project_group (AsApp *item)
+gs_appstream_is_valid_project_group (const gchar *project_group)
 {
-       const gchar *project_group = as_app_get_project_group (item);
        if (project_group == NULL)
                return FALSE;
        return as_utils_is_environment_id (project_group);
 }
 
+static gboolean
+gs_appstream_refine_app_content_rating (GsPlugin *plugin,
+                                       GsApp *app,
+                                       XbNode *content_rating,
+                                       GError **error)
+{
+       g_autoptr(AsContentRating) cr = as_content_rating_new ();
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) content_attributes = NULL;
+
+       /* get kind */
+       as_content_rating_set_kind (cr, xb_node_get_attr (content_rating, "type"));
+
+       /* get attributes */
+       content_attributes = xb_node_query (content_rating, "content_attribute", 0, &error_local);
+       if (content_attributes == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < content_attributes->len; i++) {
+               XbNode *content_attribute = g_ptr_array_index (content_attributes, i);
+               as_content_rating_add_attribute (cr,
+                                                xb_node_get_attr (content_attribute, "id"),
+                                                as_content_rating_value_from_string (xb_node_get_text 
(content_attribute)));
+       }
+
+       /* we only really expect OARS 1.0 and 1.1 */
+       if (g_str_has_prefix (as_content_rating_get_kind (cr), "oars-1."))
+               gs_app_set_content_rating (app, cr);
+       return TRUE;
+}
+
+static gboolean
+gs_appstream_refine_app_content_ratings (GsPlugin *plugin,
+                                        GsApp *app,
+                                        XbNode *component,
+                                        GError **error)
+{
+       g_autoptr(GPtrArray) content_ratings = NULL;
+       g_autoptr(GError) error_local = NULL;
+
+       /* find any content ratings */
+       content_ratings = xb_node_query (component, "content_rating", 0, &error_local);
+       if (content_ratings == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < content_ratings->len; i++) {
+               XbNode *content_rating = g_ptr_array_index (content_ratings, i);
+               if (!gs_appstream_refine_app_content_rating (plugin, app, content_rating, error))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
 gboolean
 gs_appstream_refine_app (GsPlugin *plugin,
                         GsApp *app,
-                        AsApp *item,
+                        XbSilo *silo,
+                        XbNode *component,
+                        GsPluginRefineFlags refine_flags,
                         GError **error)
 {
-       AsRequire *req;
-       g_autoptr(GError) error_local = NULL;
-       GHashTable *urls;
-       GPtrArray *launchables;
-       GPtrArray *array;
-       GPtrArray *pkgnames;
-       GPtrArray *kudos;
        const gchar *tmp;
-
-       /* set the kind to be more precise */
-       if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN ||
-           gs_app_get_kind (app) == AS_APP_KIND_GENERIC) {
-               gs_app_set_kind (app, as_app_get_kind (item));
-       }
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) bundles = NULL;
+       g_autoptr(GPtrArray) launchables = NULL;
+       g_autoptr(GPtrArray) pkgnames = NULL;
+       g_autoptr(XbNode) req = NULL;
 
        /* is compatible */
-       req = as_app_get_require_by_value (item,
-                                          AS_REQUIRE_KIND_ID,
-                                          "org.gnome.Software.desktop");
+       req = xb_node_query_first (component,
+                                  "requires/id[@type='id']"
+                                  "[text()='org.gnome.Software.desktop']", NULL);
        if (req != NULL) {
-               if (!as_require_version_compare (req, PACKAGE_VERSION, &error_local)) {
+               if (as_utils_vercmp (xb_node_get_attr (req, "version"), PACKAGE_VERSION) > 0) {
                        g_set_error (error,
                                     GS_PLUGIN_ERROR,
                                     GS_PLUGIN_ERROR_NOT_SUPPORTED,
-                                    "not for this gnome-software: %s",
-                                    error_local->message);
+                                    "not for this gnome-software");
                        return FALSE;
                }
        }
@@ -493,365 +698,386 @@ gs_appstream_refine_app (GsPlugin *plugin,
                        gs_app_remove_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
        }
 
-       /* set management plugin automatically */
-       gs_refine_item_management_plugin (plugin, app, item);
-
        /* set id */
-       if (as_app_get_id (item) != NULL && gs_app_get_id (app) == NULL)
-               gs_app_set_id (app, as_app_get_id (item));
-
-       /* set source */
-       if (gs_app_get_metadata_item (app, "appstream::source-file") == NULL) {
-               AsFormat *format = as_app_get_format_by_kind (item, AS_FORMAT_KIND_DESKTOP);
-               if (format != NULL) {
-                       gs_app_set_metadata (app, "appstream::source-file",
-                                            as_format_get_filename (format));
-               }
-       }
-
-       /* scope */
-       if (gs_app_get_scope (app) == AS_APP_SCOPE_UNKNOWN &&
-           as_app_get_scope (item) != AS_APP_SCOPE_UNKNOWN)
-               gs_app_set_scope (app, as_app_get_scope (item));
-
-       /* set branch */
-       if (as_app_get_branch (item) != NULL &&
-           gs_app_get_branch (app) == NULL)
-               gs_app_set_branch (app, as_app_get_branch (item));
+       tmp = xb_node_query_text (component, "id", NULL);
+       if (tmp != NULL && gs_app_get_id (app) == NULL)
+               gs_app_set_id (app, tmp);
 
        /* set content rating */
-       array = as_app_get_content_ratings (item);
-       for (guint i = 0; i < array->len; i++) {
-               AsContentRating *cr = g_ptr_array_index (array, i);
-               if (g_str_has_prefix (as_content_rating_get_kind (cr), "oars-1.")) {
-                       gs_app_set_content_rating (app, cr);
-                       break;
-               }
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_CONTENT_RATING) {
+               if (!gs_appstream_refine_app_content_ratings (plugin, app, component, error))
+                       return FALSE;
        }
 
-       /* bundle-kind */
-       if (gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_UNKNOWN)
-               gs_app_set_bundle_kind (app, gs_appstream_get_bundle_kind (item));
-
        /* set name */
-       tmp = as_app_get_name (item, NULL);
+       tmp = xb_node_query_text (component, "name", NULL);
        if (tmp != NULL)
                gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, tmp);
 
        /* set summary */
-       tmp = as_app_get_comment (item, NULL);
-       if (tmp != NULL) {
+       tmp = xb_node_query_text (component, "summary", NULL);
+       if (tmp != NULL)
                gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST, tmp);
-       }
 
        /* add urls */
-       urls = as_app_get_urls (item);
-       if (g_hash_table_size (urls) > 0 &&
-           gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) {
-               g_autoptr(GList) keys = NULL;
-               keys = g_hash_table_get_keys (urls);
-               for (GList *l = keys; l != NULL; l = l->next) {
-                       gs_app_set_url (app,
-                                       as_url_kind_from_string (l->data),
-                                       g_hash_table_lookup (urls, l->data));
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL) {
+               g_autoptr(GPtrArray) urls = NULL;
+               urls = xb_node_query (component, "url", 0, NULL);
+               if (urls != NULL) {
+                       for (guint i = 0; i < urls->len; i++) {
+                               XbNode *url = g_ptr_array_index (urls, i);
+                               const gchar *kind = xb_node_get_attr (url, "type");
+                               if (kind == NULL)
+                                       continue;
+                               gs_app_set_url (app,
+                                               as_url_kind_from_string (kind),
+                                               xb_node_get_text (url));
+                       }
                }
        }
 
        /* add launchables */
-       launchables = as_app_get_launchables (item);
-       for (guint i = 0; i < launchables->len; i++) {
-               AsLaunchable *launchable = g_ptr_array_index (launchables, i);
-               switch (as_launchable_get_kind (launchable)) {
-               case AS_LAUNCHABLE_KIND_DESKTOP_ID:
-                       gs_app_set_launchable (app,
-                                              AS_LAUNCHABLE_KIND_DESKTOP_ID,
-                                              as_launchable_get_value (launchable));
-                       break;
-               case AS_LAUNCHABLE_KIND_SERVICE:
-                       gs_app_set_launchable (app,
-                                              AS_LAUNCHABLE_KIND_SERVICE,
-                                              as_launchable_get_value (launchable));
-                       break;
-               case AS_LAUNCHABLE_KIND_COCKPIT_MANIFEST:
-                       gs_app_set_launchable (app,
-                                              AS_LAUNCHABLE_KIND_COCKPIT_MANIFEST,
-                                              as_launchable_get_value (launchable));
-                       break;
-               case AS_LAUNCHABLE_KIND_URL:
-                       gs_app_set_launchable (app,
-                                              AS_LAUNCHABLE_KIND_URL,
-                                              as_launchable_get_value (launchable));
-                       break;
-               default:
-                       break;
+       launchables = xb_node_query (component, "launchable", 0, NULL);
+       if (launchables != NULL) {
+               for (guint i = 0; i < launchables->len; i++) {
+                       XbNode *launchable = g_ptr_array_index (launchables, i);
+                       const gchar *kind = xb_node_get_attr (launchable, "type");
+                       if (g_strcmp0 (kind, "desktop-id") == 0) {
+                               gs_app_set_launchable (app,
+                                                      AS_LAUNCHABLE_KIND_DESKTOP_ID,
+                                                      xb_node_get_text (launchable));
+                               break;
+                       } else if (g_strcmp0 (kind, "url") == 0) {
+                               gs_app_set_launchable (app,
+                                                      AS_LAUNCHABLE_KIND_URL,
+                                                      xb_node_get_text (launchable));
+                       }
                }
        }
 
        /* set license */
-       if (as_app_get_project_license (item) != NULL && gs_app_get_license (app) == NULL)
-               gs_app_set_license (app,
-                                   GS_APP_QUALITY_HIGHEST,
-                                   as_app_get_project_license (item));
-
-       /* set keywords */
-       if (as_app_get_keywords (item, NULL) != NULL)
-               gs_app_add_kudo (app, GS_APP_KUDO_HAS_KEYWORDS);
-
-       /* set origin */
-       if (as_app_get_origin (item) != NULL &&
-           gs_app_get_origin (app) == NULL ) {
-               tmp = as_app_get_unique_id (item);
-               if (tmp != NULL) {
-                       if (g_str_has_prefix (tmp, "user/flatpak/") ||
-                           g_str_has_prefix (tmp, "system/flatpak/"))
-                               gs_app_set_origin (app, as_app_get_origin (item));
-               }
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE) > 0 &&
+           gs_app_get_license (app) == NULL) {
+               tmp = xb_node_query_text (component, "project_license", NULL);
+               if (tmp != NULL)
+                       gs_app_set_license (app, GS_APP_QUALITY_HIGHEST, tmp);
        }
 
        /* set description */
-       tmp = as_app_get_description (item, NULL);
-       if (tmp != NULL) {
-               g_autofree gchar *from_xml = NULL;
-               from_xml = as_markup_convert_simple (tmp, error);
-               if (from_xml == NULL) {
-                       gs_utils_error_convert_appstream (error);
-                       g_prefix_error (error, "trying to parse '%s': ", tmp);
-                       return FALSE;
-               }
-               gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, from_xml);
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION) {
+               g_autofree gchar *description = NULL;
+               g_autoptr(XbNode) n = xb_node_query_first (component, "description", NULL);
+               if (n != NULL)
+                       description = gs_appstream_format_description (n, NULL);
+               if (description != NULL)
+                       gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, description);
        }
 
        /* set icon */
-       if (as_app_get_icon_default (item) != NULL &&
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON) > 0 &&
            gs_app_get_icons(app)->len == 0)
-               gs_refine_item_icon (plugin, app, item);
+               gs_refine_item_icon (plugin, app, component);
 
        /* set categories */
-       array = as_app_get_categories (item);
-       if (array != NULL && gs_app_get_categories (app)->len == 0) {
-               for (guint i = 0; i < array->len; i++) {
-                       tmp = g_ptr_array_index (array, i);
-                       gs_app_add_category (app, tmp);
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES) {
+               g_autoptr(GPtrArray) categories = NULL;
+               categories = xb_node_query (component, "categories/category", 0, NULL);
+               if (categories != NULL) {
+                       for (guint i = 0; i < categories->len; i++) {
+                               XbNode *category = g_ptr_array_index (categories, i);
+                               gs_app_add_category (app, xb_node_get_text (category));
+                       }
                }
        }
 
        /* set project group */
-       if (gs_app_get_project_group (app) == NULL &&
-           gs_appstream_is_valid_project_group (item))
-               gs_app_set_project_group (app, as_app_get_project_group (item));
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROJECT_GROUP) > 0 &&
+           gs_app_get_project_group (app) == NULL) {
+               tmp = xb_node_query_text (component, "project_group", NULL);
+               if (tmp != NULL && gs_appstream_is_valid_project_group (tmp))
+                       gs_app_set_project_group (app, tmp);
+       }
 
        /* set developer name */
-       if (gs_app_get_developer_name (app) == NULL &&
-           as_app_get_developer_name (item, NULL) != NULL)
-               gs_app_set_developer_name (app, as_app_get_developer_name (item, NULL));
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_DEVELOPER_NAME) > 0 &&
+           gs_app_get_developer_name (app) == NULL) {
+               tmp = xb_node_query_text (component, "developer_name", NULL);
+               if (tmp != NULL)
+                       gs_app_set_developer_name (app, tmp);
+       }
 
        /* set id kind */
-       if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN)
-               gs_app_set_kind (app, as_app_get_kind (item));
+       if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN ||
+           gs_app_get_kind (app) == AS_APP_KIND_GENERIC) {
+               tmp = xb_node_get_attr (component, "type");
+               gs_app_set_kind (app, as_app_kind_from_string (tmp));
+       }
 
        /* copy all the metadata */
-       gs_appstream_copy_metadata (app, item);
+       if (!gs_appstream_copy_metadata (app, component, error))
+               return FALSE;
 
-       /* set package names */
-       pkgnames = as_app_get_pkgnames (item);
-       if (pkgnames->len > 0 && gs_app_get_sources(app)->len == 0)
-               gs_app_set_sources (app, pkgnames);
+       /* add package names */
+       pkgnames = xb_node_query (component, "pkgname", 0, NULL);
+       if (pkgnames != NULL && gs_app_get_sources(app)->len == 0) {
+               for (guint i = 0; i < pkgnames->len; i++) {
+                       XbNode *pkgname = g_ptr_array_index (pkgnames, i);
+                       gs_app_add_source (app, xb_node_get_text (pkgname));
+               }
+               gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
+       }
+
+       /* add bundles */
+       bundles = xb_node_query (component, "bundle", 0, NULL);
+       if (bundles != NULL && gs_app_get_sources(app)->len == 0) {
+               for (guint i = 0; i < bundles->len; i++) {
+                       XbNode *bundle = g_ptr_array_index (bundles, i);
+                       const gchar *kind = xb_node_get_attr (bundle, "type");
+                       gs_app_add_source (app, xb_node_get_text (bundle));
+                       gs_app_set_bundle_kind (app, as_bundle_kind_from_string (kind));
+               }
+       }
 
        /* set addons */
-       if (!gs_appstream_refine_add_addons (plugin, app, item, error))
-               return FALSE;
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS) {
+               if (!gs_appstream_refine_add_addons (plugin, app, silo, error))
+                       return FALSE;
+       }
 
        /* set screenshots */
-       gs_appstream_refine_add_screenshots (app, item);
-
-       /* set reviews */
-       gs_appstream_refine_add_reviews (app, item);
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SCREENSHOTS) > 0 &&
+           gs_app_get_screenshots(app)->len == 0) {
+               if (!gs_appstream_refine_add_screenshots (app, component, error))
+                       return FALSE;
+       }
 
        /* set provides */
-       gs_appstream_refine_add_provides (app, item);
-
-       /* was this application released recently */
-       if (gs_appstream_is_recent_release (item))
-               gs_app_add_kudo (app, GS_APP_KUDO_RECENT_RELEASE);
+       if (!gs_appstream_refine_add_provides (app, component, error))
+               return FALSE;
 
        /* add kudos */
-       tmp = gs_plugin_get_locale (plugin);
-       if (!_gs_utils_locale_has_translations (tmp) ||
-           as_app_get_language (item, tmp) > 50)
-               gs_app_add_kudo (app, GS_APP_KUDO_MY_LANGUAGE);
-
-       /* add a kudo to featured and popular apps */
-       if (as_app_has_kudo (item, "GnomeSoftware::popular"))
-               gs_app_add_kudo (app, GS_APP_KUDO_FEATURED_RECOMMENDED);
-       if (as_app_has_category (item, "featured"))
-               gs_app_add_kudo (app, GS_APP_KUDO_FEATURED_RECOMMENDED);
-
-       /* add new-style kudos */
-       kudos = as_app_get_kudos (item);
-       for (guint i = 0; i < kudos->len; i++) {
-               tmp = g_ptr_array_index (kudos, i);
-               switch (as_kudo_kind_from_string (tmp)) {
-               case AS_KUDO_KIND_SEARCH_PROVIDER:
-                       gs_app_add_kudo (app, GS_APP_KUDO_SEARCH_PROVIDER);
-                       break;
-               case AS_KUDO_KIND_USER_DOCS:
-                       gs_app_add_kudo (app, GS_APP_KUDO_INSTALLS_USER_DOCS);
-                       break;
-               case AS_KUDO_KIND_APP_MENU:
-                       gs_app_add_kudo (app, GS_APP_KUDO_USES_APP_MENU);
-                       break;
-               case AS_KUDO_KIND_MODERN_TOOLKIT:
-                       gs_app_add_kudo (app, GS_APP_KUDO_MODERN_TOOLKIT);
-                       break;
-               case AS_KUDO_KIND_NOTIFICATIONS:
-                       gs_app_add_kudo (app, GS_APP_KUDO_USES_NOTIFICATIONS);
-                       break;
-               case AS_KUDO_KIND_HIGH_CONTRAST:
-                       gs_app_add_kudo (app, GS_APP_KUDO_HIGH_CONTRAST);
-                       break;
-               case AS_KUDO_KIND_HI_DPI_ICON:
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_KUDOS) {
+               g_autoptr(GPtrArray) kudos = NULL;
+               tmp = gs_plugin_get_locale (plugin);
+               if (!_gs_utils_locale_has_translations (tmp)) {
+                       gs_app_add_kudo (app, GS_APP_KUDO_MY_LANGUAGE);
+               } else {
+                       g_autofree gchar *xpath = NULL;
+                       xpath = g_strdup_printf ("languages/lang[text()='%s'][@percentage>50]", tmp);
+                       if (xb_node_query_text (component, xpath, NULL) != NULL)
+                               gs_app_add_kudo (app, GS_APP_KUDO_MY_LANGUAGE);
+               }
+
+               /* any keywords */
+               if (xb_node_query_text (component, "keywords/keyword", NULL) != NULL)
+                       gs_app_add_kudo (app, GS_APP_KUDO_HAS_KEYWORDS);
+
+               /* HiDPI icon */
+               if (xb_node_query_text (component, "icon[@width='128']", NULL) != NULL)
                        gs_app_add_kudo (app, GS_APP_KUDO_HI_DPI_ICON);
-                       break;
-               default:
-                       break;
+
+               /* was this application released recently */
+               if (gs_appstream_is_recent_release (component))
+                       gs_app_add_kudo (app, GS_APP_KUDO_RECENT_RELEASE);
+
+               /* add a kudo to featured and popular apps */
+               if (xb_node_query_text (component, "kudos/kudo[text()='GnomeSoftware::popular']", NULL) != 
NULL)
+                       gs_app_add_kudo (app, GS_APP_KUDO_FEATURED_RECOMMENDED);
+               if (xb_node_query_text (component, "categories/category[text()='featured']", NULL) != NULL)
+                       gs_app_add_kudo (app, GS_APP_KUDO_FEATURED_RECOMMENDED);
+
+               /* add new-style kudos */
+               kudos = xb_node_query (component, "kudos/kudo", 0, NULL);
+               for (guint i = 0; kudos != NULL && i < kudos->len; i++) {
+                       XbNode *kudo = g_ptr_array_index (kudos, i);
+                       switch (as_kudo_kind_from_string (xb_node_get_text (kudo))) {
+                       case AS_KUDO_KIND_SEARCH_PROVIDER:
+                               gs_app_add_kudo (app, GS_APP_KUDO_SEARCH_PROVIDER);
+                               break;
+                       case AS_KUDO_KIND_USER_DOCS:
+                               gs_app_add_kudo (app, GS_APP_KUDO_INSTALLS_USER_DOCS);
+                               break;
+                       case AS_KUDO_KIND_APP_MENU:
+                               gs_app_add_kudo (app, GS_APP_KUDO_USES_APP_MENU);
+                               break;
+                       case AS_KUDO_KIND_MODERN_TOOLKIT:
+                               gs_app_add_kudo (app, GS_APP_KUDO_MODERN_TOOLKIT);
+                               break;
+                       case AS_KUDO_KIND_NOTIFICATIONS:
+                               gs_app_add_kudo (app, GS_APP_KUDO_USES_NOTIFICATIONS);
+                               break;
+                       case AS_KUDO_KIND_HIGH_CONTRAST:
+                               gs_app_add_kudo (app, GS_APP_KUDO_HIGH_CONTRAST);
+                               break;
+                       case AS_KUDO_KIND_HI_DPI_ICON:
+                               gs_app_add_kudo (app, GS_APP_KUDO_HI_DPI_ICON);
+                               break;
+                       default:
+                               break;
+                       }
                }
        }
 
-       /* we saved the origin hostname in the metadata */
-       tmp = as_app_get_metadata_item (item, "GnomeSoftware::OriginHostnameUrl");
-       if (tmp != NULL && gs_app_get_origin_hostname (app) == NULL)
-               gs_app_set_origin_hostname (app, tmp);
-
        /* we have an origin in the XML */
-       if (gs_app_get_origin (app) == NULL &&
-           gs_appstream_origin_valid (as_app_get_origin (item)))
-               gs_app_set_origin_appstream (app, as_app_get_origin (item));
+       if ((refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN) > 0 &&
+           gs_app_get_origin_appstream (app) == NULL) {
+               g_autoptr(XbNode) parent = xb_node_get_parent (component);
+               if (parent != NULL) {
+                       tmp = xb_node_get_attr (parent, "origin");
+                       if (gs_appstream_origin_valid (tmp))
+                               gs_app_set_origin_appstream (app, tmp);
+               }
+       }
 
        /* is there any update information */
-       if (!gs_appstream_refine_app_updates (plugin, app, item, error))
-               return FALSE;
+       if (refine_flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS) {
+               if (!gs_appstream_refine_app_updates (plugin,
+                                                     app,
+                                                     silo,
+                                                     component,
+                                                     error))
+                       return FALSE;
+       }
 
        return TRUE;
 }
 
-static gboolean
-gs_appstream_store_search_item (GsPlugin *plugin,
-                               AsApp *item,
-                               gchar **values,
-                               GsAppList *list,
-                               GCancellable *cancellable,
-                               GError **error)
-{
-       GPtrArray *addons;
-       guint match_value;
-       g_autoptr(GsApp) app = NULL;
-
-       /* match against the app or any of the addons */
-       match_value = as_app_search_matches_all (item, values);
-       addons = as_app_get_addons (item);
-       for (guint i = 0; i < addons->len; i++) {
-               AsApp *item_tmp = g_ptr_array_index (addons, i);
-               match_value |= as_app_search_matches_all (item_tmp, values);
-       }
-
-       /* no match */
-       if (match_value == 0)
-               return TRUE;
+typedef struct {
+       AsAppSearchMatch         match_value;
+       gchar                   *xpath;
+} GsAppstreamSearchHelper;
 
-       /* create app */
-       app = gs_appstream_create_app (plugin, item, error);
-       if (app == NULL)
-               return FALSE;
-       gs_app_set_match_value (app, match_value);
-       gs_app_list_add (list, app);
-       return TRUE;
+static void
+gs_appstream_search_helper_free (GsAppstreamSearchHelper *helper)
+{
+       g_free (helper->xpath);
+       g_free (helper);
 }
 
-gboolean
-gs_appstream_store_search (GsPlugin *plugin,
-                          AsStore *store,
-                          gchar **values,
-                          GsAppList *list,
-                          GCancellable *cancellable,
-                          GError **error)
+static guint16
+gs_appstream_silo_search_component2 (XbNode *component, const gchar *search)
 {
-       GPtrArray *array;
-       gboolean ret = TRUE;
-
-       array = as_store_get_apps (store);
+       GsAppstreamSearchHelper *helper;
+       guint16 match_value = 0;
+       g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func ((GDestroyNotify) 
gs_appstream_search_helper_free);
+
+       /* mimetype */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_MIMETYPE;
+       helper->xpath = g_strdup_printf ("mimetypes/mimetype[type()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* package name */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_PKGNAME;
+       helper->xpath = g_strdup_printf ("pkgname[text()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* summary */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_COMMENT;
+       helper->xpath = g_strdup_printf ("summary[text()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* name */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_NAME;
+       helper->xpath = g_strdup_printf ("name[text()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* keywords */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_KEYWORD;
+       helper->xpath = g_strdup_printf ("keywords/keyword[text()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* AppStream ID */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_ID;
+       helper->xpath = g_strdup_printf ("id[text()~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* origin */
+       helper = g_new0 (GsAppstreamSearchHelper, 1);
+       helper->match_value = AS_APP_SEARCH_MATCH_ORIGIN;
+       helper->xpath = g_strdup_printf ("../components[@origin~='%s']", search);
+       g_ptr_array_add (array, helper);
+
+       /* do searches */
        for (guint i = 0; i < array->len; i++) {
-               AsApp *item = g_ptr_array_index (array, i);
-               if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-                       gs_utils_error_convert_gio (error);
-                       return FALSE;
-               }
-               ret = gs_appstream_store_search_item (plugin, item,
-                                                     values, list,
-                                                     cancellable, error);
-               if (!ret)
-                       return FALSE;
+               g_autoptr(XbNode) n = NULL;
+               helper = g_ptr_array_index (array, i);
+               n = xb_node_query_first (component, helper->xpath, NULL);
+               if (n != NULL)
+                       match_value |= helper->match_value;
        }
-       return TRUE;
+       return match_value;
 }
 
-static gboolean
-_as_app_matches_desktop_group_set (AsApp *app, gchar **desktop_groups)
+static guint16
+gs_appstream_silo_search_component (XbNode *component, gchar **search)
 {
-       for (guint i = 0; desktop_groups[i] != NULL; i++) {
-               if (!as_app_has_category (app, desktop_groups[i]))
-                       return FALSE;
+       guint16 matches_sum = 0;
+
+       /* do *all* search keywords match */
+       for (guint i = 0; search[i] != NULL; i++) {
+               guint tmp = gs_appstream_silo_search_component2 (component, search[i]);
+               if (tmp == 0)
+                       return 0;
+               matches_sum |= tmp;
        }
-       return TRUE;
-}
-
-static gboolean
-_as_app_matches_desktop_group (AsApp *app, const gchar *desktop_group)
-{
-       g_auto(GStrv) split = g_strsplit (desktop_group, "::", -1);
-       return _as_app_matches_desktop_group_set (app, split);
+       return matches_sum;
 }
 
-static void
-gs_appstream_store_add_categories_for_app (GsCategory *parent, AsApp *app)
+gboolean
+gs_appstream_search (GsPlugin *plugin,
+                    XbSilo *silo,
+                    gchar **values,
+                    GsAppList *list,
+                    GCancellable *cancellable,
+                    GError **error)
 {
-       GPtrArray *children;
-       GPtrArray *desktop_groups;
-
-       /* find all the sub-categories */
-       children = gs_category_get_children (parent);
-       for (guint j = 0; j < children->len; j++) {
-               gboolean matched = FALSE;
-               GsCategory *category = GS_CATEGORY (g_ptr_array_index (children, j));
-
-               /* do any desktop_groups match this application */
-               desktop_groups = gs_category_get_desktop_groups (category);
-               for (guint i = 0; i < desktop_groups->len; i++) {
-                       const gchar *desktop_group = g_ptr_array_index (desktop_groups, i);
-                       if (_as_app_matches_desktop_group (app, desktop_group)) {
-                               matched = TRUE;
-                               break;
-                       }
-               }
-               if (matched) {
-                       gs_category_increment_size (category);
-                       gs_category_increment_size (parent);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) components = NULL;
+       g_autoptr(GTimer) timer = g_timer_new ();
+
+       /* get all components */
+       components = xb_silo_query (silo, "components/component", 0, &error_local);
+       if (components == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < components->len; i++) {
+               XbNode *component = g_ptr_array_index (components, i);
+               guint16 match_value = gs_appstream_silo_search_component (component, values);
+               if (match_value != 0) {
+                       g_autoptr(GsApp) app = gs_appstream_create_app (plugin, silo, component, error);
+                       if (app == NULL)
+                               return FALSE;
+                       g_debug ("add %s", gs_app_get_id (app));
+                       gs_app_set_match_value (app, match_value);
+                       gs_app_list_add (list, app);
                }
        }
+       g_debug ("search took %fms", g_timer_elapsed (timer, NULL) * 1000);
+       return TRUE;
 }
 
 gboolean
-gs_appstream_store_add_category_apps (GsPlugin *plugin,
-                                     AsStore *store,
-                                     GsCategory *category,
-                                     GsAppList *list,
-                                     GCancellable *cancellable,
-                                     GError **error)
+gs_appstream_add_category_apps (GsPlugin *plugin,
+                               XbSilo *silo,
+                               GsCategory *category,
+                               GsAppList *list,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       GPtrArray *array;
        GPtrArray *desktop_groups;
+       g_autoptr(GError) error_local = NULL;
 
-       /* just look at each app in turn */
-       array = as_store_get_apps (store);
        desktop_groups = gs_category_get_desktop_groups (category);
        if (desktop_groups->len == 0) {
                g_warning ("no desktop_groups for %s", gs_category_get_id (category));
@@ -859,113 +1085,174 @@ gs_appstream_store_add_category_apps (GsPlugin *plugin,
        }
        for (guint j = 0; j < desktop_groups->len; j++) {
                const gchar *desktop_group = g_ptr_array_index (desktop_groups, j);
+               g_autofree gchar *xpath = NULL;
                g_auto(GStrv) split = g_strsplit (desktop_group, "::", -1);
+               g_autoptr(GPtrArray) components = NULL;
+
+               /* generate query */
+               if (g_strv_length (split) == 1) {
+                       xpath = g_strdup_printf ("components/component/categories/"
+                                                "category[text()='%s']/../..",
+                                                split[0]);
+               } else if (g_strv_length (split) == 2) {
+                       xpath = g_strdup_printf ("components/component/categories/"
+                                                "category[text()='%s']/../"
+                                                "category[text()='%s']/../..",
+                                                split[0], split[1]);
+               }
+               components = xb_silo_query (silo, xpath, 0, &error_local);
+               if (components == NULL) {
+                       if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                               return TRUE;
+                       if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                               return TRUE;
+                       g_propagate_error (error, g_steal_pointer (&error_local));
+                       return FALSE;
+               }
 
-               /* match the app */
-               for (guint i = 0; i < array->len; i++) {
-                       AsApp *item;
+               /* create app */
+               for (guint i = 0; i < components->len; i++) {
+                       XbNode *component = g_ptr_array_index (components, i);
                        g_autoptr(GsApp) app = NULL;
 
-                       /* no ID is invalid */
-                       item = g_ptr_array_index (array, i);
-                       if (as_app_get_id (item) == NULL)
-                               continue;
-
-                       /* match all the desktop groups */
-                       if (!_as_app_matches_desktop_group_set (item, split))
-                               continue;
-
                        /* add all the data we can */
-                       app = gs_appstream_create_app (plugin, item, error);
+                       app = gs_appstream_create_app (plugin, silo, component, error);
                        if (app == NULL)
                                return FALSE;
                        gs_app_list_add (list, app);
                }
+
        }
        return TRUE;
 }
 
-gboolean
-gs_appstream_store_add_categories (GsPlugin *plugin,
-                                  AsStore *store,
-                                  GPtrArray *list,
-                                  GCancellable *cancellable,
-                                  GError **error)
+static guint
+gs_appstream_count_component_for_groups (GsPlugin *plugin, XbSilo *silo, const gchar *desktop_group)
 {
-       GPtrArray *array;
+       guint limit = 10;
+       g_autofree gchar *xpath = NULL;
+       g_auto(GStrv) split = g_strsplit (desktop_group, "::", -1);
+       g_autoptr(GPtrArray) array = NULL;
+       g_autoptr(GError) error_local = NULL;
 
-       /* find out how many packages are in each category */
-       array = as_store_get_apps (store);
-       for (guint i = 0; i < array->len; i++) {
-               AsApp *app = g_ptr_array_index (array, i);
-               if (as_app_get_id (app) == NULL)
-                       continue;
-               if (as_app_get_priority (app) < 0)
-                       continue;
-               for (guint j = 0; j < list->len; j++) {
-                       GsCategory *parent = GS_CATEGORY (g_ptr_array_index (list, j));
-                       gs_appstream_store_add_categories_for_app (parent, app);
+       if (g_strv_length (split) != 2)
+               return 0;
+
+       xpath = g_strdup_printf ("components/component/categories/"
+                                "category[text()='%s']/../"
+                                "category[text()='%s']/../..",
+                                split[0], split[1]);
+       array = xb_silo_query (silo, xpath, limit, &error_local);
+       if (array == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return 0;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return 0;
+               g_error ("%s", error_local->message);
+       }
+       return array->len;
+}
+
+/* we're not actually adding categories here, we're just setting the number of
+ * applications available in each category */
+gboolean
+gs_appstream_add_categories (GsPlugin *plugin,
+                                 XbSilo *silo,
+                                 GPtrArray *list,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       g_autoptr(GTimer) timer = g_timer_new ();
+       for (guint j = 0; j < list->len; j++) {
+               GsCategory *parent = GS_CATEGORY (g_ptr_array_index (list, j));
+               GPtrArray *children = gs_category_get_children (parent);
+               g_autofree gchar *xpath = NULL;
+               g_autoptr(GError) error_local = NULL;
+               g_autoptr(GPtrArray) array = NULL;
+
+               for (guint i = 1; i < children->len; i++) { /* 1 to ignore all */
+                       GsCategory *cat = g_ptr_array_index (children, i);
+                       GPtrArray *groups = gs_category_get_desktop_groups (cat);
+                       for (guint k = 0; k < groups->len; k++) {
+                               const gchar *group = g_ptr_array_index (groups, k);
+                               guint cnt = gs_appstream_count_component_for_groups (plugin, silo, group);
+                               for (guint l = 0; l < cnt; l++) {
+                                       gs_category_increment_size (parent);
+                                       gs_category_increment_size (cat);
+                               }
+                       }
                }
+               continue;
        }
+       g_warning ("counted apps in category groups: %f.1ms", g_timer_elapsed (timer, NULL) * 1000);
        return TRUE;
 }
 
 gboolean
 gs_appstream_add_popular (GsPlugin *plugin,
-                         AsStore *store,
+                         XbSilo *silo,
                          GsAppList *list,
                          GCancellable *cancellable,
                          GError **error)
 {
-       GPtrArray *array = as_store_get_apps (store);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) array = NULL;
+
+       /* find out how many packages are in each category */
+       array = xb_silo_query (silo,
+                              "components/component/kudos/"
+                              "kudo[text()='GnomeSoftware::popular']/../..",
+                              0, &error_local);
+       if (array == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < array->len; i++) {
                g_autoptr(GsApp) app = NULL;
-               AsApp *item = g_ptr_array_index (array, i);
-               if (as_app_get_id (item) == NULL)
+               XbNode *component = g_ptr_array_index (array, i);
+               const gchar *component_id = xb_node_query_text (component, "id", NULL);
+               if (component_id == NULL)
                        continue;
-               if (!as_app_has_kudo (item, "GnomeSoftware::popular"))
-                       continue;
-               app = gs_app_new (as_app_get_id (item));
+               app = gs_app_new (component_id);
                gs_app_add_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX);
                gs_app_list_add (list, app);
        }
        return TRUE;
 }
 
-static gboolean
-_as_app_is_recent (AsApp *app, guint64 age)
-{
-       AsRelease *rel;
-       guint64 ts;
-       guint64 now;
-
-       rel = as_app_get_release_default (app);
-       if (rel == NULL)
-               return FALSE;
-       ts = as_release_get_timestamp (rel);
-       if (ts == 0)
-               return FALSE;
-       now = (guint64) g_get_real_time () / G_USEC_PER_SEC;
-       return (now - ts) < age;
-}
-
 gboolean
 gs_appstream_add_recent (GsPlugin *plugin,
-                        AsStore *store,
+                        XbSilo *silo,
                         GsAppList *list,
                         guint64 age,
                         GCancellable *cancellable,
                         GError **error)
 {
-       GPtrArray *array = as_store_get_apps (store);
+       guint64 now = (guint64) g_get_real_time () / G_USEC_PER_SEC;
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) array = NULL;
+
+       /* use predicate conditions to the max */
+       xpath = g_strdup_printf ("components/component/releases/"
+                                "release[@timestamp>%" G_GUINT64_FORMAT "]/../..",
+                                now - (30 * 24 * 60 * 60));
+       array = xb_silo_query (silo, xpath, 0, &error_local);
+       if (array == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < array->len; i++) {
-               g_autoptr(GsApp) app = NULL;
-               AsApp *item = g_ptr_array_index (array, i);
-               if (as_app_get_id (item) == NULL)
-                       continue;
-               if (!_as_app_is_recent (item, age))
-                       continue;
-               app = gs_appstream_create_app (plugin, item, error);
+               XbNode *component = g_ptr_array_index (array, i);
+               g_autoptr(GsApp) app = gs_appstream_create_app (plugin, silo, component, error);
                if (app == NULL)
                        return FALSE;
                gs_app_list_add (list, app);
@@ -973,101 +1260,54 @@ gs_appstream_add_recent (GsPlugin *plugin,
        return TRUE;
 }
 
-static void
-_g_ptr_array_add_str_uniq (GPtrArray *array, const gchar *str)
-{
-       if (str == NULL)
-               return;
-       for (guint i = 0; i < array->len; i++) {
-               const gchar *str_tmp = g_ptr_array_index (array, i);
-               if (g_strcmp0 (str, str_tmp) == 0)
-                       return;
-       }
-       g_ptr_array_add (array, g_strdup (str));
-}
-
-/* add the component ID for any matching <provide><id/></provide> value */
-static void
-gs_appstream_add_alternates_new_id (GPtrArray *array, AsApp *item, const gchar *id)
-{
-       GPtrArray *provides = as_app_get_provides (item);
-       for (guint i = 0; i < provides->len; i++) {
-               AsProvide *provide = g_ptr_array_index (provides, i);
-               if (as_provide_get_kind (provide) == AS_PROVIDE_KIND_ID &&
-                   g_strcmp0 (as_provide_get_value (provide), id) == 0) {
-                       _g_ptr_array_add_str_uniq (array, as_app_get_id (item));
-                       break;
-               }
-       }
-
-}
-
-/* add all <provide><id/></provide> values for a matching component ID */
-static void
-gs_appstream_add_alternates_old_id (GPtrArray *array, AsApp *item, const gchar *id)
-{
-       GPtrArray *provides;
-       if (g_strcmp0 (as_app_get_id (item), id) != 0)
-               return;
-       provides = as_app_get_provides (item);
-       for (guint i = 0; i < provides->len; i++) {
-               AsProvide *provide = g_ptr_array_index (provides, i);
-               if (as_provide_get_kind (provide) == AS_PROVIDE_KIND_ID)
-                       _g_ptr_array_add_str_uniq (array, as_provide_get_value (provide));
-       }
-}
-
-/* find any matching package names */
-static void
-gs_appstream_add_alternates_source (GPtrArray *array, AsApp *item, const gchar *source)
-{
-       GPtrArray *item_pkgnames = as_app_get_pkgnames (item);
-       for (guint i = 0; i < item_pkgnames->len; i++) {
-               const gchar *pkgname = g_ptr_array_index (item_pkgnames, i);
-               if (g_strcmp0 (pkgname, source) == 0) {
-                       _g_ptr_array_add_str_uniq (array, as_app_get_id (item));
-                       break;
-               }
-       }
-}
-
 gboolean
 gs_appstream_add_alternates (GsPlugin *plugin,
-                            AsStore *store,
+                            XbSilo *silo,
                             GsApp *app,
                             GsAppList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
-       GPtrArray *apps = as_store_get_apps (store);
-       GPtrArray *ids = g_ptr_array_new_with_free_func (g_free);
-
-       /* find apps that provide the new name */
-       for (guint i = 0; i < apps->len; i++) {
-               AsApp *item = g_ptr_array_index (apps, i);
-               GPtrArray *sources = gs_app_get_sources (app);
-
-               /* actual ID */
-               if (g_strcmp0 (as_app_get_id (item), gs_app_get_id (app)) == 0)
-                       _g_ptr_array_add_str_uniq (ids, as_app_get_id (item));
-
-               /* new ID -> old ID */
-               gs_appstream_add_alternates_old_id (ids, item, gs_app_get_id (app));
-
-               /* old ID -> new ID */
-               gs_appstream_add_alternates_new_id (ids, item, gs_app_get_id (app));
-
-               /* find apps that use the same pkgname */
-               for (guint j = 0; j < sources->len; j++) {
-                       const gchar *source = g_ptr_array_index (sources, j);
-                       gs_appstream_add_alternates_source (ids, item, source);
-               }
+       GPtrArray *sources = gs_app_get_sources (app);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) ids = NULL;
+       g_autoptr(GString) xpath = g_string_new (NULL);
+
+       /* actual ID */
+       g_string_append_printf (xpath, "components/component/id[text()='%s']",
+                               gs_app_get_id (app));
+
+       /* new ID -> old ID */
+       g_string_append (xpath, "|");
+       g_string_append_printf (xpath, "components/component/id[text()='%s']/../provides/id",
+                               gs_app_get_id (app));
+
+       /* old ID -> new ID */
+       g_string_append (xpath, "|");
+       g_string_append_printf (xpath, "components/component/provides/id[text()='%s']/../../id",
+                               gs_app_get_id (app));
+
+       /* find apps that use the same pkgname */
+       for (guint j = 0; j < sources->len; j++) {
+               const gchar *source = g_ptr_array_index (sources, j);
+               g_string_append (xpath, "|");
+               g_string_append_printf (xpath, "components/component/pkgname[text()='%s']/../id", source);
        }
 
-       /* add all results */
+       /* do a big query, and return all the unique results */
+       ids = xb_silo_query (silo, xpath->str, 0, &error_local);
+       if (ids == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < ids->len; i++) {
-               const gchar *id = g_ptr_array_index (ids, i);
-               g_autoptr(GsApp) app2 = gs_app_new (id);
+               XbNode *n = g_ptr_array_index (ids, i);
+               g_autoptr(GsApp) app2 = NULL;
+               app2 = gs_app_new (xb_node_get_text (n));
                gs_app_add_quirk (app2, AS_APP_QUIRK_MATCH_ANY_PREFIX);
                gs_app_list_add (list, app2);
        }
@@ -1076,20 +1316,34 @@ gs_appstream_add_alternates (GsPlugin *plugin,
 
 gboolean
 gs_appstream_add_featured (GsPlugin *plugin,
-                          AsStore *store,
+                          XbSilo *silo,
                           GsAppList *list,
                           GCancellable *cancellable,
                           GError **error)
 {
-       GPtrArray *array = as_store_get_apps (store);
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) array = NULL;
+
+       /* find out how many packages are in each category */
+       array = xb_silo_query (silo,
+                              "components/component/custom/"
+                              "value[@key='GnomeSoftware::FeatureTile-css']/../..",
+                              0, &error_local);
+       if (array == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
        for (guint i = 0; i < array->len; i++) {
                g_autoptr(GsApp) app = NULL;
-               AsApp *item = g_ptr_array_index (array, i);
-               if (as_app_get_id (item) == NULL)
-                       continue;
-               if (as_app_get_metadata_item (item, "GnomeSoftware::FeatureTile-css") == NULL)
+               XbNode *component = g_ptr_array_index (array, i);
+               const gchar *component_id = xb_node_query_text (component, "id", NULL);
+               if (component_id == NULL)
                        continue;
-               app = gs_app_new (as_app_get_id (item));
+               app = gs_app_new (component_id);
                gs_app_add_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX);
                gs_app_list_add (list, app);
        }
@@ -1097,78 +1351,101 @@ gs_appstream_add_featured (GsPlugin *plugin,
 }
 
 void
-gs_appstream_add_extra_info (GsPlugin *plugin, AsApp *app)
+gs_appstream_component_add_keyword (XbBuilderNode *component, const gchar *str)
 {
-       const gchar *tmp;
-       g_autoptr(AsIcon) icon = NULL;
+       g_autoptr(XbBuilderNode) keyword = NULL;
+       g_autoptr(XbBuilderNode) keywords = NULL;
+
+       /* create <keywords> if it does not already exist */
+       keywords = xb_builder_node_get_child (component, "keywords", NULL);
+       if (keywords == NULL)
+               keywords = xb_builder_node_insert (component, "keywords", NULL);
+
+       /* create <keyword>str</keyword> if it does not already exist */
+       keyword = xb_builder_node_get_child (keywords, "keyword", str);
+       if (keyword == NULL) {
+               keyword = xb_builder_node_insert (keywords, "keyword", NULL);
+               xb_builder_node_set_text (keyword, str, -1);
+       }
+}
 
-       /* add more search terms */
-       switch (as_app_get_kind (app)) {
-       case AS_APP_KIND_WEB_APP:
-       case AS_APP_KIND_INPUT_METHOD:
-               tmp = as_app_kind_to_string (as_app_get_kind (app));
-               g_debug ("adding keyword '%s' to %s",
-                        tmp, as_app_get_unique_id (app));
-               as_app_add_keyword (app, NULL, tmp);
-               break;
-       default:
-               break;
+void
+gs_appstream_component_add_category (XbBuilderNode *component, const gchar *str)
+{
+       g_autoptr(XbBuilderNode) category = NULL;
+       g_autoptr(XbBuilderNode) categories = NULL;
+
+       /* create <categories> if it does not already exist */
+       categories = xb_builder_node_get_child (component, "categories", NULL);
+       if (categories == NULL)
+               categories = xb_builder_node_insert (component, "categories", NULL);
+
+       /* create <category>str</category> if it does not already exist */
+       category = xb_builder_node_get_child (categories, "category", str);
+       if (category == NULL) {
+               category = xb_builder_node_insert (categories, "category", NULL);
+               xb_builder_node_set_text (category, str, -1);
        }
+}
+
+void
+gs_appstream_component_add_icon (XbBuilderNode *component, const gchar *str)
+{
+       g_autoptr(XbBuilderNode) icon = NULL;
+
+       /* create <icon>str</icon> if it does not already exist */
+       icon = xb_builder_node_get_child (component, "icon", NULL);
+       if (icon == NULL) {
+               icon = xb_builder_node_insert (component, "icon",
+                                              "type", "stock",
+                                              NULL);
+               xb_builder_node_set_text (icon, str, -1);
+       }
+}
+
+void
+gs_appstream_component_add_extra_info (GsPlugin *plugin, XbBuilderNode *component)
+{
+       const gchar *kind = xb_builder_node_get_attr (component, "type");
 
        /* add the gnome-software-specific 'Addon' group and ensure they
         * all have an icon set */
-       switch (as_app_get_kind (app)) {
+       switch (as_app_kind_from_string (kind)) {
+       case AS_APP_KIND_WEB_APP:
+               gs_appstream_component_add_keyword (component, kind);
+               break;
        case AS_APP_KIND_FONT:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "Font");
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "Font");
                break;
        case AS_APP_KIND_SHELL_EXTENSION:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "ShellExtension");
-               if (g_hash_table_size (as_app_get_comments (app)) == 0)
-                       as_app_set_comment (app, NULL, "GNOME Shell Extension");
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "application-x-addon-symbolic");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "ShellExtension");
+               gs_appstream_component_add_icon (component, "application-x-addon-symbolic");
                break;
        case AS_APP_KIND_DRIVER:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "Driver");
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "application-x-firmware-symbolic");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "Driver");
+               gs_appstream_component_add_icon (component, "application-x-firmware-symbolic");
                break;
        case AS_APP_KIND_LOCALIZATION:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "Localization");
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "accessories-dictionary-symbolic");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "Localization");
+               gs_appstream_component_add_icon (component, "accessories-dictionary-symbolic");
                break;
        case AS_APP_KIND_CODEC:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "Codec");
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "application-x-addon");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "Codec");
+               gs_appstream_component_add_icon (component, "application-x-addon");
                break;
        case AS_APP_KIND_INPUT_METHOD:
-               as_app_add_category (app, "Addon");
-               as_app_add_category (app, "InputSource");
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "system-run-symbolic");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_keyword (component, kind);
+               gs_appstream_component_add_category (component, "Addon");
+               gs_appstream_component_add_category (component, "InputSource");
+               gs_appstream_component_add_icon (component, "system-run-symbolic");
                break;
        case AS_APP_KIND_FIRMWARE:
-               icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "system-run-symbolic");
-               as_app_add_icon (app, icon);
+               gs_appstream_component_add_icon (component, "system-run-symbolic");
                break;
        default:
                break;
diff --git a/plugins/core/gs-appstream.h b/plugins/core/gs-appstream.h
index 29ccc11a..44ba1132 100644
--- a/plugins/core/gs-appstream.h
+++ b/plugins/core/gs-appstream.h
@@ -23,57 +23,67 @@
 #define __APPSTREAM_COMMON_H
 
 #include <gnome-software.h>
+#include <xmlb.h>
 
 G_BEGIN_DECLS
 
 GsApp          *gs_appstream_create_app                (GsPlugin       *plugin,
-                                                        AsApp          *item,
+                                                        XbSilo         *silo,
+                                                        XbNode         *component,
                                                         GError         **error);
 gboolean        gs_appstream_refine_app                (GsPlugin       *plugin,
                                                         GsApp          *app,
-                                                        AsApp          *item,
+                                                        XbSilo         *silo,
+                                                        XbNode         *component,
+                                                        GsPluginRefineFlags flags,
                                                         GError         **error);
-gboolean        gs_appstream_store_search              (GsPlugin       *plugin,
-                                                        AsStore        *store,
+gboolean        gs_appstream_search                    (GsPlugin       *plugin,
+                                                        XbSilo         *silo,
                                                         gchar          **values,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-gboolean        gs_appstream_store_add_categories      (GsPlugin       *plugin,
-                                                        AsStore        *store,
+gboolean        gs_appstream_add_categories            (GsPlugin       *plugin,
+                                                        XbSilo         *silo,
                                                         GPtrArray      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-gboolean        gs_appstream_store_add_category_apps   (GsPlugin       *plugin,
-                                                        AsStore        *store,
+gboolean        gs_appstream_add_category_apps         (GsPlugin       *plugin,
+                                                        XbSilo         *silo,
                                                         GsCategory     *category,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_appstream_add_popular               (GsPlugin       *plugin,
-                                                        AsStore        *store,
+                                                        XbSilo         *silo,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_appstream_add_featured              (GsPlugin       *plugin,
-                                                        AsStore        *store,
+                                                        XbSilo         *silo,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_appstream_add_alternates            (GsPlugin       *plugin,
-                                                        AsStore        *store,
+                                                        XbSilo         *silo,
                                                         GsApp          *app,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_appstream_add_recent                (GsPlugin       *plugin,
-                                                        AsStore        *store,
+                                                        XbSilo         *silo,
                                                         GsAppList      *list,
                                                         guint64         age,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-void            gs_appstream_add_extra_info            (GsPlugin       *plugin,
-                                                        AsApp          *app);
+void            gs_appstream_component_add_extra_info  (GsPlugin       *plugin,
+                                                        XbBuilderNode  *component);
+void            gs_appstream_component_add_keyword     (XbBuilderNode  *component,
+                                                        const gchar    *str);
+void            gs_appstream_component_add_category    (XbBuilderNode  *component,
+                                                        const gchar    *str);
+void            gs_appstream_component_add_icon        (XbBuilderNode  *component,
+                                                        const gchar    *str);
 
 G_END_DECLS
 
diff --git a/plugins/core/gs-desktop-common.c b/plugins/core/gs-desktop-common.c
index fad749e5..bfb24a45 100644
--- a/plugins/core/gs-desktop-common.c
+++ b/plugins/core/gs-desktop-common.c
@@ -55,7 +55,7 @@ static const GsDesktopMap map_developertools[] = {
                                        { "Development::Featured",
                                          NULL} },
        { "debuggers",          NC_("Menu of Developer Tools", "Debuggers"),
-                                       { "Development:Debugger",
+                                       { "Development::Debugger",
                                          NULL} },
        { "ide",                NC_("Menu of Developer Tools", "IDEs"),
                                        { "Development::IDE",
diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c
index 88c55c55..2320e9bd 100644
--- a/plugins/core/gs-plugin-appstream.c
+++ b/plugins/core/gs-plugin-appstream.c
@@ -24,6 +24,7 @@
 
 #include <glib/gi18n.h>
 #include <gnome-software.h>
+#include <xmlb.h>
 
 #include "gs-appstream.h"
 
@@ -39,255 +40,455 @@
  */
 
 struct GsPluginData {
-       AsStore                 *store;
-       GHashTable              *app_hash_old;
-       guint                    store_changed_id;
+       XbSilo                  *silo;
        GSettings               *settings;
 };
 
-#define GS_PLUGIN_NUMBER_CHANGED_RELOAD        10
-
-static GHashTable *
-gs_plugin_appstream_create_app_hash (AsStore *store)
+void
+gs_plugin_initialize (GsPlugin *plugin)
 {
-       GHashTable *hash;
-       GPtrArray *apps;
-       guint i;
-
-       hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                     g_free, (GDestroyNotify) g_object_unref);
-       apps = as_store_get_apps (store);
-       for (i = 0; i < apps->len; i++) {
-               AsApp *app = g_ptr_array_index (apps, i);
-               gchar *key = g_strdup (as_app_get_id (app));
-               g_hash_table_insert (hash, key, g_object_ref (app));
-       }
-       return hash;
+       GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
+
+       /* need package name */
+       gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dpkg");
+
+       /* require settings */
+       priv->settings = g_settings_new ("org.gnome.software");
 }
 
-static void
-gs_plugin_detect_reload_apps (GsPlugin *plugin)
+void
+gs_plugin_destroy (GsPlugin *plugin)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       AsApp *item;
-       GsApp *app;
-       guint cnt = 0;
-       g_autoptr(GHashTable) app_hash = NULL;
-       g_autoptr(GList) keys = NULL;
-       g_autoptr(GList) keys_old = NULL;
-
-       /* find packages that have been added */
-       app_hash = gs_plugin_appstream_create_app_hash (priv->store);
-       keys = g_hash_table_get_keys (app_hash);
-       for (GList *l = keys; l != NULL; l = l->next) {
-               const gchar *key = l->data;
-               item = g_hash_table_lookup (priv->app_hash_old, key);
-               if (item == NULL) {
-                       item = g_hash_table_lookup (app_hash, key);
-                       app = gs_plugin_cache_lookup (plugin,
-                                                     as_app_get_unique_id (item));
-                       if (app != NULL)
-                               g_debug ("added GsApp %s", gs_app_get_id (app));
-                       cnt++;
-               }
-       }
+       g_object_unref (priv->silo);
+       g_object_unref (priv->settings);
+}
 
-       /* find packages that have been removed */
-       keys_old = g_hash_table_get_keys (priv->app_hash_old);
-       for (GList *l = keys_old; l != NULL; l = l->next) {
-               const gchar *key = l->data;
-               item = g_hash_table_lookup (app_hash, key);
-               if (item == NULL) {
-                       item = g_hash_table_lookup (priv->app_hash_old, key);
-                       app = gs_plugin_cache_lookup (plugin,
-                                                     as_app_get_unique_id (item));
-                       if (app != NULL)
-                               g_debug ("removed GsApp %s", gs_app_get_id (app));
-                       cnt++;
+static gboolean
+gs_plugin_appstream_upgrade_cb (XbBuilderSource *self,
+                               XbBuilderNode *bn,
+                               gpointer user_data,
+                               GError **error)
+{
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "application") == 0) {
+               g_autoptr(XbBuilderNode) id = xb_builder_node_get_child (bn, "id", NULL);
+               g_autofree gchar *kind = NULL;
+               if (id != NULL) {
+                       kind = g_strdup (xb_builder_node_get_attr (id, "type"));
+                       xb_builder_node_remove_attr (id, "type");
                }
+               if (kind != NULL)
+                       xb_builder_node_set_attr (bn, "type", kind);
+               xb_builder_node_set_element (bn, "component");
+       } else if (g_strcmp0 (xb_builder_node_get_element (bn), "metadata") == 0) {
+               xb_builder_node_set_element (bn, "custom");
        }
+       return TRUE;
+}
 
-       /* replace if any changes */
-       if (cnt > 0) {
-               if (priv->app_hash_old != NULL)
-                       g_hash_table_unref (priv->app_hash_old);
-               priv->app_hash_old = g_hash_table_ref (app_hash);
-       }
+static gboolean
+gs_plugin_appstream_add_icons_cb (XbBuilderSource *self,
+                                 XbBuilderNode *bn,
+                                 gpointer user_data,
+                                 GError **error)
+{
+       GsPlugin *plugin = GS_PLUGIN (user_data);
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "component") != 0)
+               return TRUE;
+       gs_appstream_component_add_extra_info (plugin, bn);
+       return TRUE;
+}
 
-       /* invalidate all if a large number of apps changed */
-       if (cnt > GS_PLUGIN_NUMBER_CHANGED_RELOAD) {
-               g_debug ("%u is more than %i AsApps changed",
-                        cnt, GS_PLUGIN_NUMBER_CHANGED_RELOAD);
-               gs_plugin_reload (plugin);
+static gboolean
+gs_plugin_appstream_add_origin_keyword_cb (XbBuilderSource *self,
+                                          XbBuilderNode *bn,
+                                          gpointer user_data,
+                                          GError **error)
+{
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "components") == 0) {
+               const gchar *origin = xb_builder_node_get_attr (bn, "origin");
+               GPtrArray *components = xb_builder_node_get_children (bn);
+               if (origin == NULL || origin[0] == '\0')
+                       return TRUE;
+               g_debug ("origin %s has %u components", origin, components->len);
+               if (components->len < 200) {
+                       for (guint i = 0; i < components->len; i++) {
+                               XbBuilderNode *component = g_ptr_array_index (components, i);
+                               gs_appstream_component_add_keyword (component, origin);
+                       }
+               }
        }
+       return TRUE;
 }
 
-static void
-gs_plugin_appstream_store_changed_cb (AsStore *store, GsPlugin *plugin)
+static gboolean
+gs_plugin_appstream_load_appdata_fn (GsPlugin *plugin,
+                                    XbBuilder *builder,
+                                    const gchar *filename,
+                                    GCancellable *cancellable,
+                                    GError **error)
 {
-       g_debug ("AppStream metadata changed");
+       g_autoptr(GFile) file = g_file_new_for_path (filename);
+       g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
+
+       /* add source */
+       if (!xb_builder_source_load_file (source, file,
+                                         XB_BUILDER_SOURCE_FLAG_WATCH_FILE,
+                                         cancellable,
+                                         error)) {
+               return FALSE;
+       }
 
-       /* send ::reload-apps */
-       gs_plugin_detect_reload_apps (plugin);
+       /* fix up any legacy installed files */
+       xb_builder_source_add_node_func (source, "AppStreamUpgrade",
+                                        gs_plugin_appstream_upgrade_cb,
+                                        plugin, NULL);
 
-       /* all the UI is reloaded as something external has happened */
-       if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_RUNNING_OTHER))
-               gs_plugin_reload (plugin);
+       /* success */
+       xb_builder_import_source (builder, source);
+       return TRUE;
 }
 
-static void
-gs_plugin_appstream_store_app_added_cb (AsStore *store,
-                                       AsApp *app,
-                                       GsPlugin *plugin)
+static gboolean
+gs_plugin_appstream_load_appdata (GsPlugin *plugin,
+                                 XbBuilder *builder,
+                                 const gchar *path,
+                                 GCancellable *cancellable,
+                                 GError **error)
 {
-       gs_appstream_add_extra_info (plugin, app);
+       const gchar *fn;
+       g_autoptr(GDir) dir = g_dir_open (path, 0, error);
+       g_autoptr(GFile) parent = g_file_new_for_path (path);
+       if (!g_file_query_exists (parent, cancellable))
+               return TRUE;
+       if (dir == NULL)
+               return FALSE;
+       while ((fn = g_dir_read_name (dir)) != NULL) {
+               if (g_str_has_suffix (fn, ".appdata.xml") ||
+                   g_str_has_suffix (fn, ".metainfo.xml")) {
+                       g_autofree gchar *filename = g_build_filename (path, fn, NULL);
+                       g_autoptr(GError) error_local = NULL;
+                       if (!gs_plugin_appstream_load_appdata_fn (plugin,
+                                                                 builder,
+                                                                 filename,
+                                                                 cancellable,
+                                                                 &error_local)) {
+                               g_debug ("ignoring %s: %s", filename, error_local->message);
+                               continue;
+                       }
+               }
+       }
+
+       /* success */
+       return TRUE;
 }
 
-static void
-gs_plugin_appstream_store_app_removed_cb (AsStore *store,
-                                         AsApp *app,
-                                         GsPlugin *plugin)
+static GInputStream *
+gs_plugin_appstream_load_desktop_cb (XbBuilderSource *self,
+                                    GFile *file,
+                                    gpointer user_data,
+                                    GCancellable *cancellable,
+                                    GError **error)
 {
-       g_debug ("AppStream app was removed, doing delete from global cache");
-       gs_plugin_cache_remove (plugin, as_app_get_unique_id (app));
+       g_autofree gchar *fn = g_file_get_path (file);
+       g_autoptr(AsApp) app = as_app_new ();
+       GString *xml;
+       if (!as_app_parse_file (app, fn, AS_APP_PARSE_FLAG_USE_FALLBACKS, error))
+               return NULL;
+       xml = as_app_to_xml (app, error);
+       if (xml == NULL)
+               return NULL;
+       return g_memory_input_stream_new_from_data (g_string_free (xml, FALSE), -1, g_free);
 }
 
-void
-gs_plugin_initialize (GsPlugin *plugin)
+static gboolean
+gs_plugin_appstream_load_desktop_fn (GsPlugin *plugin,
+                                    XbBuilder *builder,
+                                    const gchar *filename,
+                                    GCancellable *cancellable,
+                                    GError **error)
 {
-       GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
-       priv->store = as_store_new ();
-       g_signal_connect (priv->store, "app-added",
-                         G_CALLBACK (gs_plugin_appstream_store_app_added_cb),
-                         plugin);
-       g_signal_connect (priv->store, "app-removed",
-                         G_CALLBACK (gs_plugin_appstream_store_app_removed_cb),
-                         plugin);
-       as_store_set_add_flags (priv->store,
-                               AS_STORE_ADD_FLAG_USE_UNIQUE_ID |
-                               AS_STORE_ADD_FLAG_ONLY_NATIVE_LANGS |
-                               AS_STORE_ADD_FLAG_USE_MERGE_HEURISTIC);
-       as_store_set_watch_flags (priv->store,
-                                 AS_STORE_WATCH_FLAG_ADDED |
-                                 AS_STORE_WATCH_FLAG_REMOVED);
-       as_store_set_search_match (priv->store,
-                                  AS_APP_SEARCH_MATCH_MIMETYPE |
-                                  AS_APP_SEARCH_MATCH_PKGNAME |
-                                  AS_APP_SEARCH_MATCH_COMMENT |
-                                  AS_APP_SEARCH_MATCH_NAME |
-                                  AS_APP_SEARCH_MATCH_KEYWORD |
-                                  AS_APP_SEARCH_MATCH_ID);
+       g_autoptr(GFile) file = g_file_new_for_path (filename);
+       g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
+
+       /* add support for desktop files */
+       xb_builder_source_add_converter (source,
+                                        "application/x-desktop",
+                                        gs_plugin_appstream_load_desktop_cb,
+                                        NULL, NULL);
+
+       /* add source */
+       if (!xb_builder_source_load_file (source, file,
+                                         XB_BUILDER_SOURCE_FLAG_WATCH_FILE,
+                                         cancellable,
+                                         error)) {
+               return FALSE;
+       }
 
-       /* need package name */
-       gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dpkg");
+       /* success */
+       xb_builder_import_source (builder, source);
+       return TRUE;
+}
 
-       /* require settings */
-       priv->settings = g_settings_new ("org.gnome.software");
+static gboolean
+gs_plugin_appstream_load_desktop (GsPlugin *plugin,
+                                 XbBuilder *builder,
+                                 const gchar *path,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       const gchar *fn;
+       g_autoptr(GDir) dir = g_dir_open (path, 0, error);
+       g_autoptr(GFile) parent = g_file_new_for_path (path);
+       if (!g_file_query_exists (parent, cancellable))
+               return TRUE;
+       if (dir == NULL)
+               return FALSE;
+       while ((fn = g_dir_read_name (dir)) != NULL) {
+               if (g_str_has_suffix (fn, ".desktop")) {
+                       g_autofree gchar *filename = g_build_filename (path, fn, NULL);
+                       g_autoptr(GError) error_local = NULL;
+                       if (!gs_plugin_appstream_load_desktop_fn (plugin,
+                                                                 builder,
+                                                                 filename,
+                                                                 cancellable,
+                                                                 &error_local)) {
+                               g_debug ("ignoring %s: %s", filename, error_local->message);
+                               continue;
+                       }
+               }
+       }
+
+       /* success */
+       return TRUE;
 }
 
-void
-gs_plugin_destroy (GsPlugin *plugin)
+static GInputStream *
+gs_plugin_appstream_load_dep11_cb (XbBuilderSource *self,
+                                  GFile *file,
+                                  gpointer user_data,
+                                  GCancellable *cancellable,
+                                  GError **error)
 {
-       GsPluginData *priv = gs_plugin_get_data (plugin);
-       if (priv->store_changed_id != 0)
-               g_signal_handler_disconnect (priv->store, priv->store_changed_id);
-       if (priv->app_hash_old != NULL)
-               g_hash_table_unref (priv->app_hash_old);
-       g_object_unref (priv->store);
-       g_object_unref (priv->settings);
+       GString *xml;
+       g_autoptr(AsStore) store = as_store_new ();
+       if (!as_store_from_file (store, file, NULL, cancellable, error))
+               return FALSE;
+       xml = as_store_to_xml (store, AS_NODE_INSERT_FLAG_NONE);
+       if (xml == NULL)
+               return NULL;
+       return g_memory_input_stream_new_from_data (g_string_free (xml, FALSE), -1, g_free);
 }
 
-/*
- * Returns: A hash table with a string key of the application origin and a
- * value of the guint percentage of the store is made up by that origin.
- */
-static GHashTable *
-gs_plugin_appstream_get_origins_hash (GPtrArray *array)
+static gboolean
+gs_plugin_appstream_load_appstream_fn (GsPlugin *plugin,
+                                      XbBuilder *builder,
+                                      const gchar *filename,
+                                      GCancellable *cancellable,
+                                      GError **error)
 {
-       AsApp *app;
-       GHashTable *origins = NULL;
-       const gchar *tmp;
-       gdouble perc;
-       guint *cnt;
-       guint i;
-       g_autoptr(GList) keys = NULL;
-
-       /* create a hash table with origin:cnt */
-       origins = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                        g_free, g_free);
-       for (i = 0; i < array->len; i++) {
-               app = g_ptr_array_index (array, i);
-               tmp = as_app_get_origin (app);
-               if (tmp == NULL)
-                       continue;
-               cnt = g_hash_table_lookup (origins, tmp);
-               if (cnt == NULL) {
-                       cnt = g_new0 (guint, 1);
-                       g_hash_table_insert (origins, g_strdup (tmp), cnt);
-               }
-               (*cnt)++;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GFile) file = g_file_new_for_path (filename);
+       g_autoptr(XbBuilderNode) info = NULL;
+       g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
+
+       /* add support for DEP-11 files */
+       xb_builder_source_add_converter (source,
+                                        "application/x-yaml",
+                                        gs_plugin_appstream_load_dep11_cb,
+                                        NULL, NULL);
+
+       /* add source */
+       if (!xb_builder_source_load_file (source, file,
+                                         XB_BUILDER_SOURCE_FLAG_WATCH_FILE |
+                                         XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT,
+                                         cancellable,
+                                         error)) {
+               return FALSE;
        }
 
-       /* convert the cnt to a percentage */
-       keys = g_hash_table_get_keys (origins);
-       for (GList *l = keys; l != NULL; l = l->next) {
-               tmp = l->data;
-               if (tmp == NULL || tmp[0] == '\0')
-                       continue;
-               cnt = g_hash_table_lookup (origins, tmp);
-               perc = (100.f / (gdouble) array->len) * (gdouble) (*cnt);
-               g_debug ("origin %s provides %u apps (%.0f%%)", tmp, *cnt, perc);
-               *cnt = (guint) perc;
+       /* add metadata */
+       info = xb_builder_node_insert (NULL, "info", NULL);
+       xb_builder_node_insert_text (info, "scope", "system", NULL);
+       xb_builder_node_insert_text (info, "filename", filename, NULL);
+       xb_builder_source_set_info (source, info);
+
+       /* add missing icons as required */
+       xb_builder_source_add_node_func (source, "AddIcons",
+                                        gs_plugin_appstream_add_icons_cb,
+                                        plugin, NULL);
+
+       /* fix up any legacy installed files */
+       xb_builder_source_add_node_func (source, "AppStreamUpgrade",
+                                        gs_plugin_appstream_upgrade_cb,
+                                        plugin, NULL);
+
+       /* add the origin as a search keyword for small repos */
+       xb_builder_source_add_node_func (source, "AddOriginKeyword",
+                                        gs_plugin_appstream_add_origin_keyword_cb,
+                                        plugin, NULL);
+
+       /* success */
+       xb_builder_import_source (builder, source);
+       return TRUE;
+}
+
+static gboolean
+gs_plugin_appstream_load_appstream (GsPlugin *plugin,
+                                   XbBuilder *builder,
+                                   const gchar *path,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+       const gchar *fn;
+       g_autoptr(GDir) dir = NULL;
+       g_autoptr(GFile) parent = g_file_new_for_path (path);
+
+       /* parent patch does not exist */
+       if (!g_file_query_exists (parent, cancellable))
+               return TRUE;
+       dir = g_dir_open (path, 0, error);
+       if (dir == NULL)
+               return FALSE;
+       while ((fn = g_dir_read_name (dir)) != NULL) {
+               if (g_str_has_suffix (fn, ".xml") ||
+                   g_str_has_suffix (fn, ".yml") ||
+                   g_str_has_suffix (fn, ".xml.gz")) {
+                       g_autofree gchar *filename = g_build_filename (path, fn, NULL);
+                       g_autoptr(GError) error_local = NULL;
+                       if (!gs_plugin_appstream_load_appstream_fn (plugin,
+                                                                   builder,
+                                                                   filename,
+                                                                   cancellable,
+                                                                   &error_local)) {
+                               g_debug ("ignoring %s: %s", filename, error_local->message);
+                               continue;
+                       }
+               }
        }
 
-       return origins;
+       /* success */
+       return TRUE;
 }
 
-gboolean
-gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
+static gboolean
+gs_plugin_appstream_check_silo (GsPlugin *plugin,
+                               GCancellable *cancellable,
+                               GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GPtrArray *items;
-       gboolean ret;
-       const gchar *tmp;
+       const gchar *locale;
        const gchar *test_xml;
-       const gchar *test_icon_root;
-       gboolean all_origin_keywords = g_getenv ("GS_SELF_TEST_ALL_ORIGIN_KEYWORDS") != NULL;
-       guint *perc;
-       guint i;
-       g_autoptr(GHashTable) origins = NULL;
-
-       /* Parse the XML */
-       if (g_getenv ("GNOME_SOFTWARE_PREFER_LOCAL") != NULL) {
-               as_store_set_add_flags (priv->store,
-                                       AS_STORE_ADD_FLAG_PREFER_LOCAL);
+       g_autofree gchar *blobfn = NULL;
+       g_autoptr(XbBuilder) builder = xb_builder_new ();
+       g_autoptr(XbNode) n = NULL;
+       g_autoptr(GFile) file = NULL;
+       g_autoptr(GPtrArray) parent_appdata = g_ptr_array_new_with_free_func (g_free);
+       g_autoptr(GPtrArray) parent_appstream = g_ptr_array_new_with_free_func (g_free);
+
+       /* everything is okay */
+       if (priv->silo != NULL && xb_silo_is_valid (priv->silo))
+               return TRUE;
+
+       /* drat! silo needs regenerating */
+       g_clear_object (&priv->silo);
+
+       /* verbose profiling */
+       if (g_getenv ("GS_XMLB_VERBOSE") != NULL) {
+               xb_builder_set_profile_flags (builder,
+                                             XB_SILO_PROFILE_FLAG_XPATH |
+                                             XB_SILO_PROFILE_FLAG_DEBUG);
+       }
+
+       /* add current locales */
+       locale = g_getenv ("GS_SELF_TEST_LOCALE");
+       if (locale == NULL) {
+               const gchar *const *locales = g_get_language_names ();
+               for (guint i = 0; locales[i] != NULL; i++)
+                       xb_builder_add_locale (builder, locales[i]);
+       } else {
+               xb_builder_add_locale (builder, locale);
        }
 
        /* only when in self test */
        test_xml = g_getenv ("GS_SELF_TEST_APPSTREAM_XML");
        if (test_xml != NULL) {
-               test_icon_root = g_getenv ("GS_SELF_TEST_APPSTREAM_ICON_ROOT");
-               g_debug ("using self test data of %s... with icon root %s",
-                        test_xml, test_icon_root);
-               if (!as_store_from_xml (priv->store, test_xml, test_icon_root, error))
+               g_autofree gchar *icon_prefix = NULL;
+               g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
+               if (!xb_builder_source_load_xml (source, test_xml,
+                                                XB_BUILDER_SOURCE_FLAG_NONE,
+                                                error))
                        return FALSE;
+               xb_builder_source_add_node_func (source, "AddOriginKeywords",
+                                                gs_plugin_appstream_add_origin_keyword_cb,
+                                                plugin, NULL);
+               xb_builder_source_add_node_func (source, "AddIcons",
+                                                gs_plugin_appstream_add_icons_cb,
+                                                plugin, NULL);
+               xb_builder_import_source (builder, source);
        } else {
-               ret = as_store_load (priv->store,
-                                    AS_STORE_LOAD_FLAG_IGNORE_INVALID |
-                                    AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM |
-                                    AS_STORE_LOAD_FLAG_APP_INFO_USER |
-                                    AS_STORE_LOAD_FLAG_APPDATA |
-                                    AS_STORE_LOAD_FLAG_DESKTOP |
-                                    AS_STORE_LOAD_FLAG_APP_INSTALL,
-                                    cancellable,
-                                    error);
-               if (!ret) {
-                       gs_utils_error_convert_appstream (error);
+               /* add search paths */
+               g_ptr_array_add (parent_appstream,
+                                g_build_filename ("/usr/share", "app-info", "xmls", NULL));
+               g_ptr_array_add (parent_appstream,
+                                g_build_filename ("/usr/share", "app-info", "yaml", NULL));
+               g_ptr_array_add (parent_appdata,
+                                g_build_filename ("/usr/share", "appdata", NULL));
+               g_ptr_array_add (parent_appdata,
+                                g_build_filename ("/usr/share", "metainfo", NULL));
+
+               /* import all files */
+               for (guint i = 0; i < parent_appstream->len; i++) {
+                       const gchar *fn = g_ptr_array_index (parent_appstream, i);
+                       if (!gs_plugin_appstream_load_appstream (plugin, builder, fn,
+                                                                cancellable, error))
+                               return FALSE;
+               }
+               for (guint i = 0; i < parent_appdata->len; i++) {
+                       const gchar *fn = g_ptr_array_index (parent_appdata, i);
+                       if (!gs_plugin_appstream_load_appdata (plugin, builder, fn,
+                                                              cancellable, error))
+                               return FALSE;
+               }
+               if (!gs_plugin_appstream_load_desktop (plugin, builder,
+                                                      "/usr/share/applications",
+                                                      cancellable, error)) {
                        return FALSE;
                }
        }
-       items = as_store_get_apps (priv->store);
-       if (items->len == 0) {
+
+       /* create per-user cache */
+       blobfn = gs_utils_get_cache_filename ("appstream", "components.xmlb",
+                                             GS_UTILS_CACHE_FLAG_WRITEABLE,
+                                             error);
+       if (blobfn == NULL)
+               return FALSE;
+       file = g_file_new_for_path (blobfn);
+       g_debug ("ensuring %s", blobfn);
+       priv->silo = xb_builder_ensure (builder, file,
+                                       XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID |
+                                       XB_BUILDER_COMPILE_FLAG_SINGLE_LANG,
+                                       NULL, error);
+       if (priv->silo == NULL)
+               return FALSE;
+
+       /* watch all directories too */
+       for (guint i = 0; i < parent_appstream->len; i++) {
+               const gchar *fn = g_ptr_array_index (parent_appstream, i);
+               g_autoptr(GFile) file_tmp = g_file_new_for_path (fn);
+               if (!xb_silo_watch_file (priv->silo, file_tmp, cancellable, error))
+                       return FALSE;
+       }
+       for (guint i = 0; i < parent_appdata->len; i++) {
+               const gchar *fn = g_ptr_array_index (parent_appdata, i);
+               g_autoptr(GFile) file_tmp = g_file_new_for_path (fn);
+               if (!xb_silo_watch_file (priv->silo, file_tmp, cancellable, error))
+                       return FALSE;
+       }
+
+       /* test we found something */
+       n = xb_silo_query_first (priv->silo, "components/component", NULL);
+       if (n == NULL) {
                g_warning ("No AppStream data, try 'make install-sample-data' in data/");
                g_set_error (error,
                             GS_PLUGIN_ERROR,
@@ -296,36 +497,17 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
                return FALSE;
        }
 
-       /* prime the cache */
-       priv->app_hash_old = gs_plugin_appstream_create_app_hash (priv->store);
-
-       /* watch for changes */
-       priv->store_changed_id =
-               g_signal_connect (priv->store, "changed",
-                                 G_CALLBACK (gs_plugin_appstream_store_changed_cb),
-                                 plugin);
-
-       /* add search terms for apps not in the main source */
-       origins = gs_plugin_appstream_get_origins_hash (items);
-       for (i = 0; i < items->len; i++) {
-               AsApp *app = g_ptr_array_index (items, i);
-               tmp = as_app_get_origin (app);
-               if (tmp == NULL || tmp[0] == '\0')
-                       continue;
-               perc = g_hash_table_lookup (origins, tmp);
-               if (*perc < 10 || all_origin_keywords) {
-                       g_debug ("adding keyword '%s' to %s",
-                                tmp, as_app_get_id (app));
-                       as_app_set_search_match (app,
-                                                as_store_get_search_match (priv->store) |
-                                                AS_APP_SEARCH_MATCH_ORIGIN);
-               }
-       }
-
-       /* rely on the store keeping itself updated */
+       /* success */
        return TRUE;
 }
 
+gboolean
+gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
+{
+       /* set up silo, compiling if required */
+       return gs_plugin_appstream_check_silo (plugin, cancellable, error);
+}
+
 gboolean
 gs_plugin_url_to_app (GsPlugin *plugin,
                      GsAppList *list,
@@ -334,10 +516,15 @@ gs_plugin_url_to_app (GsPlugin *plugin,
                      GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       AsApp *item;
        g_autofree gchar *path = NULL;
        g_autofree gchar *scheme = NULL;
+       g_autofree gchar *xpath = NULL;
        g_autoptr(GsApp) app = NULL;
+       g_autoptr(XbNode) component = NULL;
+
+       /* check silo is valid */
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
 
        /* not us */
        scheme = gs_utils_get_url_scheme (url);
@@ -346,10 +533,11 @@ gs_plugin_url_to_app (GsPlugin *plugin,
 
        /* create app */
        path = gs_utils_get_url_path (url);
-       item = as_store_get_app_by_id (priv->store, path);
-       if (item == NULL)
+       xpath = g_strdup_printf ("components/component/id[text()='%s']", path);
+       component = xb_silo_query_first (priv->silo, xpath, NULL);
+       if (component == NULL)
                return TRUE;
-       app = gs_appstream_create_app (plugin, item, error);
+       app = gs_appstream_create_app (plugin, priv->silo, component, error);
        if (app == NULL)
                return FALSE;
        gs_app_list_add (list, app);
@@ -357,9 +545,9 @@ gs_plugin_url_to_app (GsPlugin *plugin,
 }
 
 static void
-gs_plugin_appstream_set_compulsory_quirk (GsApp *app, AsApp *item)
+gs_plugin_appstream_set_compulsory_quirk (GsApp *app, XbNode *component)
 {
-       GPtrArray *array;
+       g_autoptr(GPtrArray) array = NULL;
        const gchar *current_desktop;
 
        /*
@@ -377,12 +565,15 @@ gs_plugin_appstream_set_compulsory_quirk (GsApp *app, AsApp *item)
         * compulsory apps for such compound desktops if they want.
         *
         */
-       array = as_app_get_compulsory_for_desktops (item);
+       array = xb_node_query (component, "compulsory_for_desktop", 0, NULL);
+       if (array == NULL)
+               return;
        current_desktop = g_getenv ("XDG_CURRENT_DESKTOP");
        if (current_desktop != NULL) {
                g_auto(GStrv) xdg_current_desktops = g_strsplit (current_desktop, ":", 0);
                for (guint i = 0; i < array->len; i++) {
-                       const gchar *tmp = g_ptr_array_index (array, i);
+                       XbNode *n = g_ptr_array_index (array, i);
+                       const gchar *tmp = xb_node_get_text (n);
                        /* if the value has a :, check the whole string */
                        if (g_strstr_len (tmp, -1, ":")) {
                                if (g_strcmp0 (current_desktop, tmp) == 0) {
@@ -401,53 +592,58 @@ gs_plugin_appstream_set_compulsory_quirk (GsApp *app, AsApp *item)
 static gboolean
 gs_plugin_refine_from_id (GsPlugin *plugin,
                          GsApp *app,
+                         GsPluginRefineFlags flags,
                          gboolean *found,
                          GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       const gchar *unique_id;
-       AsApp *item;
-
-       /* unfound */
-       *found = FALSE;
+       const gchar *id;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GString) xpath = g_string_new (NULL);
+       g_autoptr(GPtrArray) components = NULL;
 
-       /* find anything that matches the ID */
-       unique_id = gs_app_get_unique_id (app);
-       if (unique_id == NULL)
+       /* not enough info to find */
+       id = gs_app_get_id (app);
+       if (id == NULL)
                return TRUE;
 
        /* nothing found */
-       g_debug ("searching appstream for %s", unique_id);
-       item = as_store_get_app_by_unique_id (priv->store, unique_id,
-                                             AS_STORE_SEARCH_FLAG_USE_WILDCARDS);
-       if (item == NULL) {
-               GPtrArray *apps = as_store_get_apps (priv->store);
-               g_debug ("no app with ID %s found in system appstream", unique_id);
-               for (guint i = 0; i < apps->len; i++) {
-                       item = g_ptr_array_index (apps, i);
-                       if (g_strcmp0 (as_app_get_id (item), gs_app_get_id (app)) != 0)
-                               continue;
-                       g_debug ("possible match: %s",
-                                as_app_get_unique_id (item));
-               }
-
-               /* fall back to trying to get a merge app */
-               apps = as_store_get_apps_by_id_merge (priv->store, gs_app_get_id (app));
-               if (apps != NULL) {
-                       for (guint i = 0; i < apps->len; i++) {
-                               item = g_ptr_array_index (apps, i);
-                               if (!gs_appstream_refine_app (plugin, app, item, error))
-                                       return FALSE;
-                               gs_plugin_appstream_set_compulsory_quirk (app, item);
-                       }
-               }
-               return TRUE;
+       g_debug ("searching appstream for %s", id);
+
+       /* look in AppStream then fall back to AppData */
+       g_string_append_printf (xpath, "components/component/id[text()='%s']/..", id);
+       g_string_append (xpath, "|");
+       g_string_append_printf (xpath, "component/id[text()='%s']/..", id);
+       components = xb_silo_query (priv->silo, xpath->str, 0, &error_local);
+       if (components == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < components->len; i++) {
+               XbNode *component = g_ptr_array_index (components, i);
+               if (!gs_appstream_refine_app (plugin, app, priv->silo,
+                                             component, flags, error))
+                       return FALSE;
+               gs_plugin_appstream_set_compulsory_quirk (app, component);
        }
 
-       /* set new properties */
-       if (!gs_appstream_refine_app (plugin, app, item, error))
-               return FALSE;
+       /* maybe no if */
+{
+       g_autoptr(XbNode) component_installed = NULL;
+       g_autofree gchar *xpath2 = NULL;
+//     if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) {
+               xpath2 = g_strdup_printf ("component/id[text()='%s']", id);
+               component_installed = xb_silo_query_first (priv->silo, xpath2, NULL);
+               if (component_installed != NULL)
+                       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+//     }
+}
 
+       /* success */
        *found = TRUE;
        return TRUE;
 }
@@ -455,56 +651,45 @@ gs_plugin_refine_from_id (GsPlugin *plugin,
 static gboolean
 gs_plugin_refine_from_pkgname (GsPlugin *plugin,
                               GsApp *app,
+                              GsPluginRefineFlags flags,
                               GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       AsApp *item = NULL;
-       GPtrArray *sources;
-
-       /* find anything that matches the ID */
-       sources = gs_app_get_sources (app);
-       for (guint i = 0; i < sources->len && item == NULL; i++) {
-               const gchar *pkgname = g_ptr_array_index (sources, i);
-               item = as_store_get_app_by_pkgname (priv->store, pkgname);
-               if (item == NULL)
-                       g_debug ("no AppStream match for {pkgname} %s", pkgname);
-       }
+       GPtrArray *sources = gs_app_get_sources (app);
+       g_autoptr(GError) error_local = NULL;
 
-       /* nothing found */
-       if (item == NULL)
+       /* not enough info to find */
+       if (sources->len == 0)
                return TRUE;
 
-       /* set new properties */
-       return gs_appstream_refine_app (plugin, app, item, error);
-}
-
-gboolean
-gs_plugin_add_distro_upgrades (GsPlugin *plugin,
-                              GsAppList *list,
-                              GCancellable *cancellable,
-                              GError **error)
-{
-       GsPluginData *priv = gs_plugin_get_data (plugin);
-       AsApp *item;
-       GPtrArray *array;
-       guint i;
-
-       /* find any upgrades */
-       array = as_store_get_apps (priv->store);
-       for (i = 0; i < array->len; i++) {
-               g_autoptr(GsApp) app = NULL;
-               item = g_ptr_array_index (array, i);
-               if (as_app_get_kind (item) != AS_APP_KIND_OS_UPDATE)
-                       continue;
-
-               /* create */
-               app = gs_appstream_create_app (plugin, item, error);
-               if (app == NULL)
+       /* find all apps when matching any prefixes */
+       for (guint j = 0; j < sources->len; j++) {
+               const gchar *pkgname = g_ptr_array_index (sources, j);
+               g_autofree gchar *xpath = NULL;
+               g_autoptr(GPtrArray) components = NULL;
+
+               g_debug ("searching appstream for pkg %s", pkgname);
+               xpath = g_strdup_printf ("components/component/pkgname[text()='%s']/..",
+                                        pkgname);
+               components = xb_silo_query (priv->silo, xpath, 0, &error_local);
+               if (components == NULL) {
+                       if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                               continue;
+                       if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                               continue;
+                       g_propagate_error (error, g_steal_pointer (&error_local));
                        return FALSE;
-               gs_app_set_kind (app, AS_APP_KIND_OS_UPGRADE);
-               gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-               gs_app_list_add (list, app);
+               }
+               for (guint i = 0; i < components->len; i++) {
+                       XbNode *component = g_ptr_array_index (components, i);
+                       if (!gs_appstream_refine_app (plugin, app, priv->silo,
+                                                     component, flags, error))
+                               return FALSE;
+                       gs_plugin_appstream_set_compulsory_quirk (app, component);
+               }
        }
+
+       /* success */
        return TRUE;
 }
 
@@ -520,15 +705,19 @@ gs_plugin_refine_app (GsPlugin *plugin,
        /* not us */
        if (gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_PACKAGE &&
            gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_UNKNOWN) {
-               g_debug ("not a package, ignoring");
+               g_debug ("not a package, ignoring %s", gs_app_get_unique_id (app));
                return TRUE;
        }
 
-       /* find by ID then package name */
-       if (!gs_plugin_refine_from_id (plugin, app, &found, error))
+       /* check silo is valid */
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+
+       /* find by ID then fall back to package name */
+       if (!gs_plugin_refine_from_id (plugin, app, flags, &found, error))
                return FALSE;
        if (!found) {
-               if (!gs_plugin_refine_from_pkgname (plugin, app, error))
+               if (!gs_plugin_refine_from_pkgname (plugin, app, flags, error))
                        return FALSE;
        }
 
@@ -546,8 +735,13 @@ gs_plugin_refine_wildcard (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        const gchar *id;
-       guint i;
-       g_autoptr(GPtrArray) items = NULL;
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) components = NULL;
+
+       /* check silo is valid */
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
 
        /* not enough info to find */
        id = gs_app_get_id (app);
@@ -555,40 +749,30 @@ gs_plugin_refine_wildcard (GsPlugin *plugin,
                return TRUE;
 
        /* find all apps when matching any prefixes */
-       items = as_store_get_apps_by_id (priv->store, id);
-       for (i = 0; i < items->len; i++) {
-               AsApp *item = g_ptr_array_index (items, i);
+       xpath = g_strdup_printf ("components/component/id[text()='%s']/..", id);
+       components = xb_silo_query (priv->silo, xpath, 0, &error_local);
+       if (components == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < components->len; i++) {
+               XbNode *component = g_ptr_array_index (components, i);
                g_autoptr(GsApp) new = NULL;
 
-               /* is compatible */
-               if (!as_utils_unique_id_match (gs_app_get_unique_id (app),
-                                              as_app_get_unique_id (item),
-                                              AS_UNIQUE_ID_MATCH_FLAG_SCOPE |
-                                              AS_UNIQUE_ID_MATCH_FLAG_BUNDLE_KIND |
-                                              /* don't match origin as AsApp appstream
-                                               * origin can differ from package origin */
-                                              AS_UNIQUE_ID_MATCH_FLAG_KIND |
-                                              AS_UNIQUE_ID_MATCH_FLAG_ID |
-                                              AS_UNIQUE_ID_MATCH_FLAG_BRANCH)) {
-                       g_debug ("does not match unique ID constraints: %s, %s",
-                                gs_app_get_unique_id (app),
-                                as_app_get_unique_id (item));
-                       continue;
-               }
-
                /* does the app have an installation method */
-               if (as_app_get_pkgname_default (item) == NULL &&
-                   as_app_get_bundle_default (item) == NULL) {
-                       g_debug ("not using %s for wildcard as "
-                                "no bundle or pkgname",
-                                as_app_get_id (item));
+               if (xb_node_query_text (component, "pkgname", NULL) == NULL) {
+                       g_debug ("not using %s for wildcard as no pkgname",
+                                xb_node_query_text (component, "id", NULL));
                        continue;
                }
 
                /* new app */
-               g_debug ("found %s for wildcard %s",
-                        as_app_get_id (item), id);
-               new = gs_appstream_create_app (plugin, item, error);
+               g_debug ("found component for wildcard %s", id);
+               new = gs_appstream_create_app (plugin, priv->silo, component, error);
                if (new == NULL)
                        return FALSE;
                gs_app_list_add (list, new);
@@ -606,12 +790,14 @@ gs_plugin_add_category_apps (GsPlugin *plugin,
                             GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_store_add_category_apps (plugin,
-                                                    priv->store,
-                                                    category,
-                                                    list,
-                                                    cancellable,
-                                                    error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_category_apps (plugin,
+                                              priv->silo,
+                                              category,
+                                              list,
+                                              cancellable,
+                                              error);
 }
 
 gboolean
@@ -622,12 +808,14 @@ gs_plugin_add_search (GsPlugin *plugin,
                      GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_store_search (plugin,
-                                         priv->store,
-                                         values,
-                                         list,
-                                         cancellable,
-                                         error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_search (plugin,
+                                   priv->silo,
+                                   values,
+                                   list,
+                                   cancellable,
+                                   error);
 }
 
 gboolean
@@ -637,19 +825,23 @@ gs_plugin_add_installed (GsPlugin *plugin,
                         GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GPtrArray *array;
-
-       /* search categories for the search term */
-       array = as_store_get_apps (priv->store);
-       for (guint i = 0; i < array->len; i++) {
-               AsApp *item = g_ptr_array_index (array, i);
-               if (as_app_get_state (item) == AS_APP_STATE_INSTALLED) {
-                       g_autoptr(GsApp) app = NULL;
-                       app = gs_appstream_create_app (plugin, item, error);
-                       if (app == NULL)
-                               return FALSE;
-                       gs_app_list_add (list, app);
-               }
+       g_autoptr(GPtrArray) components = NULL;
+
+       /* check silo is valid */
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+
+       /* get all installed appdata files (notice no 'components/' prefix...) */
+       components = xb_silo_query (priv->silo, "component", 0, NULL);
+       if (components == NULL)
+               return TRUE;
+       for (guint i = 0; i < components->len; i++) {
+               XbNode *component = g_ptr_array_index (components, i);
+               g_autoptr(GsApp) app = gs_appstream_create_app (plugin, priv->silo, component, error);
+               if (app == NULL)
+                       return FALSE;
+               gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+               gs_app_list_add (list, app);
        }
        return TRUE;
 }
@@ -661,8 +853,10 @@ gs_plugin_add_categories (GsPlugin *plugin,
                          GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_store_add_categories (plugin, priv->store, list,
-                                                 cancellable, error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_categories (plugin, priv->silo, list,
+                                           cancellable, error);
 }
 
 gboolean
@@ -672,8 +866,9 @@ gs_plugin_add_popular (GsPlugin *plugin,
                       GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_add_popular (plugin, priv->store, list, cancellable,
-                                        error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_popular (plugin, priv->silo, list, cancellable, error);
 }
 
 gboolean
@@ -683,8 +878,9 @@ gs_plugin_add_featured (GsPlugin *plugin,
                        GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_add_featured (plugin, priv->store, list, cancellable,
-                                         error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_featured (plugin, priv->silo, list, cancellable, error);
 }
 
 gboolean
@@ -695,7 +891,9 @@ gs_plugin_add_recent (GsPlugin *plugin,
                      GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_add_recent (plugin, priv->store, list, age,
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_recent (plugin, priv->silo, list, age,
                                        cancellable, error);
 }
 
@@ -707,7 +905,10 @@ gs_plugin_add_alternates (GsPlugin *plugin,
                          GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       return gs_appstream_add_alternates (plugin, priv->store, app, list, cancellable, error);
+       if (!gs_plugin_appstream_check_silo (plugin, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_alternates (plugin, priv->silo, app, list,
+                                           cancellable, error);
 }
 
 gboolean
@@ -716,11 +917,5 @@ gs_plugin_refresh (GsPlugin *plugin,
                   GCancellable *cancellable,
                   GError **error)
 {
-       GsPluginData *priv = gs_plugin_get_data (plugin);
-
-       /* ensure the token cache */
-       if (cache_age == G_MAXUINT)
-               as_store_load_search_cache (priv->store);
-
-       return TRUE;
+       return gs_plugin_appstream_check_silo (plugin, cancellable, error);
 }
diff --git a/plugins/core/gs-self-test.c b/plugins/core/gs-self-test.c
index c7724a54..3d8fdcac 100644
--- a/plugins/core/gs-self-test.c
+++ b/plugins/core/gs-self-test.c
@@ -21,6 +21,8 @@
 
 #include "config.h"
 
+#include <glib/gstdio.h>
+
 #include "gnome-software-private.h"
 
 #include "gs-appstream.h"
@@ -37,6 +39,7 @@ gs_plugins_core_search_repo_name_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* force this app to be installed */
@@ -70,6 +73,7 @@ gs_plugins_core_os_release_func (GsPluginLoader *plugin_loader)
        g_autoptr(GError) error = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* refine system application */
@@ -120,6 +124,7 @@ gs_plugins_core_generic_updates_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsAppList) list_wildcard = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* create a list with generic apps */
@@ -238,7 +243,6 @@ main (int argc, char **argv)
                "  </component>\n"
                "</components>\n";
        g_setenv ("GS_SELF_TEST_APPSTREAM_XML", xml, TRUE);
-       g_setenv ("GS_SELF_TEST_ALL_ORIGIN_KEYWORDS", "1", TRUE);
 
        /* only critical and error are fatal */
        g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
diff --git a/plugins/core/meson.build b/plugins/core/meson.build
index 92ec67a5..599d2bb2 100644
--- a/plugins/core/meson.build
+++ b/plugins/core/meson.build
@@ -92,7 +92,10 @@ shared_module(
   install : true,
   install_dir: plugin_dir,
   c_args : cargs,
-  dependencies : plugin_libs
+  dependencies : [
+    plugin_libs,
+    libxmlb,
+  ],
 )
 
 shared_module(
@@ -208,6 +211,7 @@ if get_option('tests')
     ],
     dependencies : [
       plugin_libs,
+      libxmlb,
     ],
     link_with : [
       libgnomesoftware
diff --git a/plugins/dummy/gs-self-test.c b/plugins/dummy/gs-self-test.c
index ddc3e868..4087c905 100644
--- a/plugins/dummy/gs-self-test.c
+++ b/plugins/dummy/gs-self-test.c
@@ -21,6 +21,8 @@
 
 #include "config.h"
 
+#include <glib/gstdio.h>
+
 #include "gnome-software-private.h"
 
 #include "gs-test.h"
@@ -104,6 +106,7 @@ gs_plugins_dummy_error_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* update, which should cause an error to be emitted */
@@ -424,7 +427,7 @@ gs_plugins_dummy_search_func (GsPluginLoader *plugin_loader)
 
        /* get search result based on addon keyword */
        plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_SEARCH,
-                                        "search", "spell",
+                                        "search", "zeus",
                                         "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
                                         NULL);
        list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
@@ -478,6 +481,7 @@ gs_plugins_dummy_hang_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* get search result based on addon keyword */
@@ -734,6 +738,7 @@ gs_plugins_dummy_limit_parallel_ops_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsDummyTestHelper) helper3 = gs_dummy_test_helper_new ();
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* get the updates list */
@@ -825,6 +830,7 @@ gs_plugins_dummy_limit_parallel_ops_func (GsPluginLoader *plugin_loader)
 int
 main (int argc, char **argv)
 {
+       const gchar *tmp_root = "/var/tmp/self-test";
        gboolean ret;
        g_autofree gchar *xml = NULL;
        g_autoptr(GError) error = NULL;
@@ -845,6 +851,7 @@ main (int argc, char **argv)
 
        g_test_init (&argc, &argv, NULL);
        g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+       g_setenv ("GS_XMLB_VERBOSE", "1", TRUE);
 
        /* set all the things required as a dummy test harness */
        g_setenv ("GS_SELF_TEST_LOCALE", "en_GB", TRUE);
@@ -853,6 +860,7 @@ main (int argc, char **argv)
        g_setenv ("GS_SELF_TEST_PROVENANCE_LICENSE_SOURCES", "london*,boston", TRUE);
        g_setenv ("GS_SELF_TEST_PROVENANCE_LICENSE_URL", "https://www.debian.org/";, TRUE);
        g_setenv ("GNOME_SOFTWARE_POPULAR", "", TRUE);
+       g_setenv ("GS_SELF_TEST_CORE_DATADIR", tmp_root, TRUE);
 
        xml = g_strdup ("<?xml version=\"1.0\"?>\n"
                "<components version=\"0.9\">\n"
diff --git a/plugins/external-appstream/gs-install-appstream.c 
b/plugins/external-appstream/gs-install-appstream.c
index d4a430c9..7f5eb92d 100644
--- a/plugins/external-appstream/gs-install-appstream.c
+++ b/plugins/external-appstream/gs-install-appstream.c
@@ -24,10 +24,8 @@
 #include <locale.h>
 #include <stdlib.h>
 
-#include <appstream-glib.h>
-#include <gio/gio.h>
+#include <xmlb.h>
 #include <glib/gi18n.h>
-#include <glib-object.h>
 
 #include "gs-external-appstream-utils.h"
 
@@ -59,8 +57,12 @@ static gboolean
 gs_install_appstream_check_content_type (GFile *file, GError **error)
 {
        const gchar *type;
-       g_autoptr(AsStore) store = NULL;
+       g_autoptr(GError) error_local = NULL;
        g_autoptr(GFileInfo) info = NULL;
+       g_autoptr(GPtrArray) components = NULL;
+       g_autoptr(XbBuilder) builder = xb_builder_new ();
+       g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
+       g_autoptr(XbSilo) silo = NULL;
 
        /* check is correct type */
        info = g_file_query_info (file,
@@ -80,14 +82,38 @@ gs_install_appstream_check_content_type (GFile *file, GError **error)
        }
 
        /* check is an AppStream file */
-       store = as_store_new ();
-       if (!as_store_from_file (store, file, NULL, NULL, error))
+       if (!xb_builder_source_load_file (source, file,
+                                         XB_BUILDER_SOURCE_FLAG_NONE,
+                                         NULL, &error_local)) {
+               g_set_error (error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_INVALID_DATA,
+                            "Failed to import XML: %s", error_local->message);
                return FALSE;
-       if (as_store_get_size (store) == 0) {
-               g_set_error_literal (error,
-                                    G_IO_ERROR,
-                                    G_IO_ERROR_INVALID_DATA,
-                                    "No applications found in the AppStream XML");
+       }
+       xb_builder_import_source (builder, source);
+       silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE,
+                                  NULL, &error_local);
+       if (silo == NULL) {
+               g_set_error (error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_INVALID_DATA,
+                            "Failed to parse XML: %s", error_local->message);
+               return FALSE;
+       }
+       components = xb_silo_query (silo, "components/component", 0, &error_local);
+       if (components == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+                       g_set_error_literal (error,
+                                            G_IO_ERROR,
+                                            G_IO_ERROR_INVALID_DATA,
+                                            "No applications found in the AppStream XML");
+                       return FALSE;
+               }
+               g_set_error (error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_INVALID_DATA,
+                            "Failed to query XML: %s", error_local->message);
                return FALSE;
        }
 
diff --git a/plugins/external-appstream/meson.build b/plugins/external-appstream/meson.build
index 6e2f3dfa..95897252 100644
--- a/plugins/external-appstream/meson.build
+++ b/plugins/external-appstream/meson.build
@@ -9,7 +9,10 @@ executable(
   include_directories : [
     include_directories('../..'),
   ],
-  dependencies : [gio_unix, appstream_glib],
+  dependencies : [
+    gio_unix,
+    libxmlb,
+  ],
   c_args : cargs,
   install : true,
   install_dir : get_option('libexecdir')
diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c
index ecbe1ed5..8e1d53e1 100644
--- a/plugins/flatpak/gs-flatpak.c
+++ b/plugins/flatpak/gs-flatpak.c
@@ -30,6 +30,7 @@
 #include <config.h>
 
 #include <glib/gi18n.h>
+#include <xmlb.h>
 
 #include "gs-appstream.h"
 #include "gs-flatpak-app.h"
@@ -44,7 +45,7 @@ struct _GsFlatpak {
        GFileMonitor            *monitor;
        AsAppScope               scope;
        GsPlugin                *plugin;
-       AsStore                 *store;
+       XbSilo                  *silo;
        gchar                   *id;
        guint                    changed_id;
 };
@@ -196,11 +197,6 @@ gs_plugin_flatpak_changed_cb (GFileMonitor *monitor,
                              GsFlatpak *self)
 {
        g_autoptr(GError) error = NULL;
-       g_autoptr(GError) error_md = NULL;
-
-       /* don't refresh when it's us ourselves doing the change */
-       if (gs_plugin_has_flags (self->plugin, GS_PLUGIN_FLAGS_RUNNING_SELF))
-               return;
 
        /* manually drop the cache */
        if (!flatpak_installation_drop_caches (self->installation,
@@ -208,49 +204,96 @@ gs_plugin_flatpak_changed_cb (GFileMonitor *monitor,
                g_warning ("failed to drop cache: %s", error->message);
                return;
        }
+}
+
+static gboolean
+gs_flatpak_add_flatpak_keyword_cb (XbBuilderSource *self,
+                                  XbBuilderNode *bn,
+                                  gpointer user_data,
+                                  GError **error)
+{
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "component") == 0)
+               gs_appstream_component_add_keyword (bn, "flatpak");
+       return TRUE;
+}
 
-       /* if this is a new remote, get the AppStream data */
-       if (!gs_flatpak_refresh_appstream (self, G_MAXUINT, NULL, &error_md)) {
-               g_warning ("failed to get initial available data: %s",
-                          error_md->message);
+static gboolean
+gs_flatpak_set_origin_cb (XbBuilderSource *self,
+                         XbBuilderNode *bn,
+                         gpointer user_data,
+                         GError **error)
+{
+       FlatpakRemote *xremote = FLATPAK_REMOTE (user_data);
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "components") == 0) {
+               xb_builder_node_set_attr (bn, "origin",
+                                         flatpak_remote_get_name (xremote));
        }
+       return TRUE;
 }
 
-static void
-gs_flatpak_remove_prefixed_names (AsApp *app)
+static gboolean
+gs_flatpak_filter_default_branch_cb (XbBuilderSource *self,
+                                    XbBuilderNode *bn,
+                                    gpointer user_data,
+                                    GError **error)
 {
-       GHashTable *names;
-       g_autoptr(GList) keys = NULL;
+       const gchar *default_branch = (const gchar *) user_data;
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "component") == 0) {
+               g_autoptr(XbBuilderNode) bc = xb_builder_node_get_child (bn, "bundle", NULL);
+               g_auto(GStrv) split = NULL;
+               if (bc == NULL) {
+                       g_debug ("no bundle for component");
+                       return TRUE;
+               }
+               split = g_strsplit (xb_builder_node_get_text (bc), "/", -1);
+               if (split == NULL || g_strv_length (split) != 4)
+                       return TRUE;
+               if (g_strcmp0 (split[3], default_branch) != 0) {
+                       g_debug ("not adding app with branch %s as filtering to %s",
+                                split[3], default_branch);
+                       xb_builder_node_unlink (bn);
+               }
+       }
+       return TRUE;
+}
 
-       names = as_app_get_names (app);
-       keys = g_hash_table_get_keys (names);
-       for (GList *l = keys; l != NULL; l = l->next) {
-               const gchar *locale = l->data;
-               const gchar *value = g_hash_table_lookup (names, locale);
-               if (value == NULL)
-                       continue;
-               if (!g_str_has_prefix (value, "(Nightly) "))
-                       continue;
-               as_app_set_name (app, locale, value + 10);
+static gboolean
+gs_flatpak_filter_noenumerate_cb (XbBuilderSource *self,
+                                 XbBuilderNode *bn,
+                                 gpointer user_data,
+                                 GError **error)
+{
+       const gchar *only_app_id = (const gchar *) user_data;
+       if (g_strcmp0 (xb_builder_node_get_element (bn), "component") == 0) {
+               g_autoptr(XbBuilderNode) bc = xb_builder_node_get_child (bn, "id", NULL);
+               if (bc == NULL) {
+                       g_debug ("no ID for component");
+                       return TRUE;
+               }
+               if (g_strcmp0 (xb_builder_node_get_text (bc), only_app_id) != 0) {
+                       g_debug ("not adding app %s as filtering to %s",
+                                xb_builder_node_get_text (bc), only_app_id);
+                       xb_builder_node_unlink (bn);
+               }
        }
+       return TRUE;
 }
 
 static gboolean
 gs_flatpak_add_apps_from_xremote (GsFlatpak *self,
+                                 XbBuilder *builder,
                                  FlatpakRemote *xremote,
                                  GCancellable *cancellable,
                                  GError **error)
 {
-       GPtrArray *apps;
        g_autofree gchar *appstream_dir_fn = NULL;
        g_autofree gchar *appstream_fn = NULL;
-       g_autofree gchar *default_branch = NULL;
-       g_autofree gchar *only_app_id = NULL;
-       g_autoptr(AsStore) store = NULL;
+       g_autofree gchar *icon_prefix = NULL;
        g_autoptr(GFile) appstream_dir = NULL;
-       g_autoptr(GFile) file = NULL;
+       g_autoptr(GFile) file_xml = NULL;
        g_autoptr(GSettings) settings = NULL;
-       g_autoptr(GPtrArray) app_filtered = NULL;
+       g_autoptr(XbBuilderNode) info = NULL;
+       g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
 
        /* get the AppStream data location */
        appstream_dir = flatpak_remote_get_appstream_dir (xremote, NULL);
@@ -260,94 +303,68 @@ gs_flatpak_add_apps_from_xremote (GsFlatpak *self,
                return TRUE;
        }
 
-       /* load the file into a temp store */
+       /* load the file into a temp silo */
        appstream_dir_fn = g_file_get_path (appstream_dir);
-       appstream_fn = g_build_filename (appstream_dir_fn,
-                                        "appstream.xml.gz", NULL);
+       appstream_fn = g_build_filename (appstream_dir_fn, "appstream.xml.gz", NULL);
        if (!g_file_test (appstream_fn, G_FILE_TEST_EXISTS)) {
                g_debug ("no %s appstream metadata found: %s",
                         flatpak_remote_get_name (xremote),
                         appstream_fn);
                return TRUE;
        }
-       file = g_file_new_for_path (appstream_fn);
-       store = as_store_new ();
-       as_store_set_add_flags (store,
-                               AS_STORE_ADD_FLAG_USE_UNIQUE_ID |
-                               AS_STORE_ADD_FLAG_ONLY_NATIVE_LANGS);
-       as_store_set_search_match (store,
-                                  AS_APP_SEARCH_MATCH_MIMETYPE |
-                                  AS_APP_SEARCH_MATCH_PKGNAME |
-                                  AS_APP_SEARCH_MATCH_COMMENT |
-                                  AS_APP_SEARCH_MATCH_NAME |
-                                  AS_APP_SEARCH_MATCH_KEYWORD |
-                                  AS_APP_SEARCH_MATCH_ORIGIN |
-                                  AS_APP_SEARCH_MATCH_ID);
-       if (!as_store_from_file (store, file, NULL, cancellable, error)) {
-               gs_utils_error_convert_appstream (error);
+
+       /* add source */
+       file_xml = g_file_new_for_path (appstream_fn);
+       if (!xb_builder_source_load_file (source, file_xml,
+                                         XB_BUILDER_SOURCE_FLAG_WATCH_FILE |
+                                         XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT,
+                                         cancellable,
+                                         error))
                return FALSE;
-       }
+
+       /* add the origin as a search keyword for small repos */
+       xb_builder_source_add_node_func (source, "AddKeywordFlatpak",
+                                        gs_flatpak_add_flatpak_keyword_cb,
+                                        self, NULL);
 
        /* override the *AppStream* origin */
-       apps = as_store_get_apps (store);
-       for (guint i = 0; i < apps->len; i++) {
-               AsApp *app = g_ptr_array_index (apps, i);
-               as_app_set_origin (app, flatpak_remote_get_name (xremote));
-       }
+       xb_builder_source_add_node_func (source, "SetOrigin",
+                                        gs_flatpak_set_origin_cb,
+                                        xremote, NULL);
+
+       /* add metadata */
+       icon_prefix = g_build_filename (appstream_dir_fn, "icons", NULL);
+       info = xb_builder_node_insert (NULL, "info", NULL);
+       xb_builder_node_insert_text (info, "scope", as_app_scope_to_string (self->scope), NULL);
+       xb_builder_node_insert_text (info, "icon-prefix", icon_prefix, NULL);
+       xb_builder_source_set_info (source, info);
 
        /* only add the specific app for noenumerate=true */
        if (flatpak_remote_get_noenumerate (xremote)) {
                g_autofree gchar *tmp = NULL;
                tmp = g_strdup (flatpak_remote_get_name (xremote));
                g_strdelimit (tmp, "-", '\0');
-               only_app_id = g_strdup_printf ("%s.desktop", tmp);
+               xb_builder_source_add_node_func (source, "FilterNoEnumerate",
+                                                gs_flatpak_filter_noenumerate_cb,
+                                                g_strdup_printf ("%s.desktop", tmp),
+                                                g_free);
        }
 
        /* do we want to filter to the default branch */
        settings = g_settings_new ("org.gnome.software");
-       if (g_settings_get_boolean (settings, "filter-default-branch"))
-               default_branch = flatpak_remote_get_default_branch (xremote);
-
-       /* get all the apps and fix them up */
-       app_filtered = g_ptr_array_new ();
-       for (guint i = 0; i < apps->len; i++) {
-               AsApp *app = g_ptr_array_index (apps, i);
-
-               /* filter to app */
-               if (only_app_id != NULL &&
-                   g_strcmp0 (as_app_get_id (app), only_app_id) != 0) {
-                       as_app_set_kind (app, AS_APP_KIND_UNKNOWN);
-                       continue;
-               }
-
-               /* filter by branch */
-               if (default_branch != NULL &&
-                   g_strcmp0 (as_app_get_branch (app), default_branch) != 0) {
-                       g_debug ("not adding app with branch %s as filtering to %s",
-                                as_app_get_branch (app), default_branch);
-                       continue;
-               }
-
-               /* fix the names when using old versions of appstream-compose */
-               gs_flatpak_remove_prefixed_names (app);
-
-               /* add */
-               as_app_set_scope (app, self->scope);
-               as_app_set_origin (app, flatpak_remote_get_name (xremote));
-               as_app_add_keyword (app, NULL, "flatpak");
-               g_debug ("adding %s", as_app_get_unique_id (app));
-               g_ptr_array_add (app_filtered, app);
+       if (g_settings_get_boolean (settings, "filter-default-branch")) {
+               xb_builder_source_add_node_func (source, "FilterDefaultbranch",
+                                                gs_flatpak_filter_default_branch_cb,
+                                                flatpak_remote_get_default_branch (xremote),
+                                                g_free);
        }
 
-       /* add them to the main store */
-       as_store_add_apps (self->store, app_filtered);
-
-       /* ensure the token cache for all apps */
-       as_store_load_search_cache (store);
-
+       /* success */
+       xb_builder_import_source (builder, source);
        return TRUE;
 }
 
+#if 0
 static gchar *
 gs_flatpak_discard_desktop_suffix (const gchar *app_id)
 {
@@ -360,7 +377,9 @@ gs_flatpak_discard_desktop_suffix (const gchar *app_id)
        app_prefix_len = strlen (app_id) - strlen (desktop_suffix);
        return g_strndup (app_id, app_prefix_len);
 }
+#endif
 
+#if 0
 static void
 gs_flatpak_rescan_installed (GsFlatpak *self,
                             GCancellable *cancellable,
@@ -385,7 +404,7 @@ gs_flatpak_rescan_installed (GsFlatpak *self,
        while ((fn = g_dir_read_name (dir)) != NULL) {
                g_autofree gchar *fn_desktop = NULL;
                g_autoptr(GError) error_local = NULL;
-               g_autoptr(AsApp) app = NULL;
+               g_autoptr(XbNode) app = NULL;
                g_autoptr(AsFormat) format = as_format_new ();
                g_autoptr(FlatpakInstalledRef) app_ref = NULL;
                g_autofree gchar *app_id = NULL;
@@ -406,7 +425,7 @@ gs_flatpak_rescan_installed (GsFlatpak *self,
                /* fix up icons */
                icons = as_app_get_icons (app);
                for (guint i = 0; i < icons->len; i++) {
-                       AsIcon *ic = g_ptr_array_index (icons, i);
+                       XbNode *ic = g_ptr_array_index (icons, i);
                        if (as_icon_get_kind (ic) == AS_ICON_KIND_UNKNOWN) {
                                as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
                                as_icon_set_prefix (ic, path_exports);
@@ -437,19 +456,41 @@ gs_flatpak_rescan_installed (GsFlatpak *self,
                as_app_set_branch (app, flatpak_ref_get_branch (FLATPAK_REF (app_ref)));
                as_app_set_icon_path (app, path_exports);
                as_app_add_keyword (app, NULL, "flatpak");
-               as_store_add_app (self->store, app);
+               as_store_add_app (self->silo, app);
        }
 }
+#endif
 
 static gboolean
 gs_flatpak_rescan_appstream_store (GsFlatpak *self,
                                   GCancellable *cancellable,
                                   GError **error)
 {
+       const gchar *const *locales = g_get_language_names ();
+       g_autofree gchar *blobfn = NULL;
+       g_autoptr(GFile) file = NULL;
        g_autoptr(GPtrArray) xremotes = NULL;
+       g_autoptr(XbBuilder) builder = xb_builder_new ();
+
+       /* everything is okay */
+       if (self->silo != NULL && xb_silo_is_valid (self->silo)) {
+               g_debug ("silo valid, returning");
+               return TRUE;
+       }
+
+       /* drat! silo needs regenerating */
+       g_clear_object (&self->silo);
+
+       /* verbose profiling */
+       if (g_getenv ("GS_DEBUG") != NULL) {
+               xb_builder_set_profile_flags (builder,
+                                             XB_SILO_PROFILE_FLAG_XPATH |
+                                             XB_SILO_PROFILE_FLAG_DEBUG);
+       }
 
-       /* remove all components */
-       as_store_remove_all (self->store);
+       /* add current locales */
+       for (guint i = 0; locales[i] != NULL; i++)
+               xb_builder_add_locale (builder, locales[i]);
 
        /* go through each remote adding metadata */
        xremotes = flatpak_installation_list_remotes (self->installation,
@@ -465,13 +506,30 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self,
                        continue;
                g_debug ("found remote %s",
                         flatpak_remote_get_name (xremote));
-               if (!gs_flatpak_add_apps_from_xremote (self, xremote, cancellable, error))
+               if (!gs_flatpak_add_apps_from_xremote (self, builder, xremote, cancellable, error))
                        return FALSE;
        }
 
        /* add any installed files without AppStream info */
-       gs_flatpak_rescan_installed (self, cancellable, error);
+//     gs_flatpak_rescan_installed (self, cancellable, error);
+
+       /* create per-user cache */
+       blobfn = gs_utils_get_cache_filename (gs_flatpak_get_id (self),
+                                             "components.xmlb",
+                                             GS_UTILS_CACHE_FLAG_WRITEABLE,
+                                             error);
+       if (blobfn == NULL)
+               return FALSE;
+       file = g_file_new_for_path (blobfn);
+       g_debug ("ensuring %s", blobfn);
+       self->silo = xb_builder_ensure (builder, file,
+                                       XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID |
+                                       XB_BUILDER_COMPILE_FLAG_SINGLE_LANG,
+                                       NULL, error);
+       if (self->silo == NULL)
+               return FALSE;
 
+       /* success */
        return TRUE;
 }
 
@@ -605,7 +663,6 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, guint cache_age,
                              GCancellable *cancellable, GError **error)
 {
        gboolean ret;
-       gboolean something_changed = FALSE;
        g_autoptr(GPtrArray) xremotes = NULL;
 
        /* get remotes */
@@ -677,21 +734,15 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, guint cache_age,
                        continue;
                }
 
-               /* add the new AppStream repo to the shared store */
+               /* add the new AppStream repo to the shared silo */
                file = flatpak_remote_get_appstream_dir (xremote, NULL);
                appstream_fn = g_file_get_path (file);
                g_debug ("using AppStream metadata found at: %s", appstream_fn);
-
-               /* trigger the symlink rebuild */
-               something_changed = TRUE;
        }
 
-       /* ensure the AppStream store is up to date */
-       if (something_changed ||
-           as_store_get_size (self->store) == 0) {
-               if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
-                       return FALSE;
-       }
+       /* ensure the AppStream silo is up to date */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
 
        return TRUE;
 }
@@ -829,6 +880,10 @@ gs_flatpak_add_sources (GsFlatpak *self, GsAppList *list,
        g_autoptr(GPtrArray) xrefs = NULL;
        g_autoptr(GPtrArray) xremotes = NULL;
 
+       /* refresh */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+
        /* get installed apps and runtimes */
        xrefs = flatpak_installation_list_installed_refs (self->installation,
                                                          cancellable,
@@ -1044,10 +1099,10 @@ gs_flatpak_app_install_source (GsFlatpak *self, GsApp *app,
        }
 
        /* refresh the AppStream data manually */
-       if (!gs_flatpak_add_apps_from_xremote (self, xremote, cancellable, error)) {
-               g_prefix_error (error, "cannot refresh remote AppStream: ");
-               return FALSE;
-       }
+//     if (!gs_flatpak_add_apps_from_xremote (self, builder, xremote, cancellable, error)) {
+//             g_prefix_error (error, "cannot refresh remote AppStream: ");
+//             return FALSE;
+//     }
 
        /* success */
        gs_app_set_state (app, AS_APP_STATE_INSTALLED);
@@ -1136,6 +1191,10 @@ gs_flatpak_add_updates (GsFlatpak *self, GsAppList *list,
 {
        g_autoptr(GPtrArray) xrefs = NULL;
 
+       /* ensure valid */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+
        /* get all the updatable apps and runtimes */
        xrefs = flatpak_installation_list_installed_refs_for_update (self->installation,
                                                                     cancellable,
@@ -1235,8 +1294,20 @@ gs_flatpak_refresh (GsFlatpak *self,
                return FALSE;
        }
 
+       /* manually do this in case we created the first appstream file */
+       if (self->silo != NULL)
+               xb_silo_invalidate (self->silo);
+
        /* update AppStream metadata */
-       return gs_flatpak_refresh_appstream (self, cache_age, cancellable, error);
+       if (!gs_flatpak_refresh_appstream (self, cache_age, cancellable, error))
+               return FALSE;
+
+       /* ensure valid */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+
+       /* success */
+       return TRUE;
 }
 
 static gboolean
@@ -1426,6 +1497,10 @@ gs_flatpak_refine_app_state (GsFlatpak *self,
        g_autoptr(FlatpakInstalledRef) ref = NULL;
        g_autoptr(GError) error_local = NULL;
 
+       /* ensure valid */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+
        /* already found */
        if (gs_app_get_state (app) != AS_APP_STATE_UNKNOWN)
                return TRUE;
@@ -1819,56 +1894,51 @@ gs_plugin_refine_item_size (GsFlatpak *self,
 }
 
 static void
-gs_flatpak_refine_appstream_release (AsApp *item, GsApp *app)
+gs_flatpak_refine_appstream_release (XbNode *component, GsApp *app)
 {
-       AsRelease *rel = as_app_get_release_default (item);
-       if (rel == NULL)
-               return;
-       if (as_release_get_version (rel) == NULL)
+       const gchar *version;
+
+       /* get first release */
+       version = xb_node_query_attr (component, "releases/release", "version", NULL);
+       if (version == NULL)
                return;
        switch (gs_app_get_state (app)) {
        case AS_APP_STATE_INSTALLED:
        case AS_APP_STATE_AVAILABLE:
        case AS_APP_STATE_AVAILABLE_LOCAL:
-               gs_app_set_version (app, as_release_get_version (rel));
+               gs_app_set_version (app, version);
                break;
        default:
                g_debug ("%s is not installed, so ignoring version of %s",
-                        as_app_get_id (item), as_release_get_version (rel));
+                        gs_app_get_unique_id (app), version);
                break;
        }
 }
 
 static gboolean
-gs_flatpak_refine_appstream (GsFlatpak *self, GsApp *app, GError **error)
+gs_flatpak_refine_appstream (GsFlatpak *self,
+                            GsApp *app,
+                            XbSilo *silo,
+                            GsPluginRefineFlags flags,
+                            GError **error)
 {
-       AsApp *item;
-       const gchar *unique_id = gs_app_get_unique_id (app);
+       const gchar *id = gs_app_get_id (app);
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(XbNode) component = NULL;
 
-       if (unique_id == NULL)
-               return TRUE;
-       item = as_store_get_app_by_unique_id (self->store,
-                                             unique_id,
-                                             AS_STORE_SEARCH_FLAG_USE_WILDCARDS);
-       if (item == NULL) {
-               g_autoptr(GPtrArray) apps = NULL;
-               apps = as_store_get_apps_by_id (self->store, gs_app_get_id (app));
-               if (apps->len > 0) {
-                       g_debug ("potential matches for %s:", unique_id);
-                       for (guint i = 0; i < apps->len; i++) {
-                               AsApp *app_tmp = g_ptr_array_index (apps, i);
-                               g_debug ("- %s", as_app_get_unique_id (app_tmp));
-                       }
-               }
+       if (id == NULL)
                return TRUE;
-       }
 
-       if (!gs_appstream_refine_app (self->plugin, app, item, error))
+       /* find using ID */
+       xpath = g_strdup_printf ("components/component/id[text()='%s']/..", id);
+       component = xb_silo_query_first (silo, xpath, NULL);
+       if (component == NULL)
+               return TRUE;
+       if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, error))
                return FALSE;
 
        /* use the default release as the version number */
-       gs_flatpak_refine_appstream_release (item, app);
-
+       gs_flatpak_refine_appstream_release (component, app);
        return TRUE;
 }
 
@@ -1880,9 +1950,14 @@ gs_flatpak_refine_app (GsFlatpak *self,
                       GError **error)
 {
        AsAppState old_state = gs_app_get_state (app);
+//FIXME: only do for bundle type flatpak
+
+       /* ensure valid */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
 
        /* always do AppStream properties */
-       if (!gs_flatpak_refine_appstream (self, app, error))
+       if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, error))
                return FALSE;
 
        /* AppStream sets the source to appname/arch/branch */
@@ -1903,7 +1978,7 @@ gs_flatpak_refine_app (GsFlatpak *self,
 
        /* if the state was changed, perhaps set the version from the release */
        if (old_state != gs_app_get_state (app)) {
-               if (!gs_flatpak_refine_appstream (self, app, error))
+               if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, error))
                        return FALSE;
        }
 
@@ -1954,38 +2029,35 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app,
                            GCancellable *cancellable, GError **error)
 {
        const gchar *id;
-       guint i;
-       g_autoptr(GPtrArray) items = NULL;
+       g_autofree gchar *xpath = NULL;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GPtrArray) components = NULL;
 
-       /* not valid */
+       /* not enough info to find */
        id = gs_app_get_id (app);
        if (id == NULL)
                return TRUE;
 
+       /* ensure valid */
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+
        /* find all apps when matching any prefixes */
-       items = as_store_get_apps_by_id (self->store, id);
-       for (i = 0; i < items->len; i++) {
-               AsApp *item = g_ptr_array_index (items, i);
+       xpath = g_strdup_printf ("components/component/id[text()='%s']/..", id);
+       components = xb_silo_query (self->silo, xpath, 0, &error_local);
+       if (components == NULL) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       return TRUE;
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+                       return TRUE;
+               g_propagate_error (error, g_steal_pointer (&error_local));
+               return FALSE;
+       }
+       for (guint i = 0; i < components->len; i++) {
+               XbNode *component = g_ptr_array_index (components, i);
                g_autoptr(GsApp) new = NULL;
-
-               /* is compatible */
-               if (!as_utils_unique_id_equal (gs_app_get_unique_id (app),
-                                              as_app_get_unique_id (item))) {
-                       g_debug ("does not match unique ID constraints");
-                       continue;
-               }
-
-               /* does the app have an installation method */
-               if (as_app_get_bundle_default (item) == NULL) {
-                       g_debug ("not using %s for wildcard as no bundle",
-                                as_app_get_id (item));
-                       continue;
-               }
-
-               /* new app */
-               g_debug ("found %s for wildcard %s",
-                        as_app_get_unique_id (item), id);
-               new = gs_appstream_create_app (self->plugin, item, NULL);
+               g_debug ("found component for wildcard %s", id);
+               new = gs_appstream_create_app (self->plugin, self->silo, component, error);
                if (new == NULL)
                        return FALSE;
                gs_flatpak_claim_app (self, new);
@@ -1993,6 +2065,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app,
                        return FALSE;
                gs_app_list_add (list, new);
        }
+
+       /* success */
        return TRUE;
 }
 
@@ -2116,9 +2190,9 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self,
                g_autoptr(GInputStream) stream_gz = NULL;
                g_autoptr(GInputStream) stream_data = NULL;
                g_autoptr(GBytes) appstream = NULL;
-               g_autoptr(AsStore) store = NULL;
+               g_autoptr(XbSilo) silo = NULL;
                g_autofree gchar *id = NULL;
-               AsApp *item;
+//             XbNode *component;
 
                /* decompress data */
                decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
@@ -2136,23 +2210,24 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self,
                        gs_flatpak_error_convert (error);
                        return NULL;
                }
-               store = as_store_new ();
-               if (!as_store_from_bytes (store, appstream, cancellable, error)) {
-                       gs_flatpak_error_convert (error);
-                       return NULL;
-               }
+//             silo = xb_silo_new ();
+//             if (!as_store_from_bytes (silo, appstream, cancellable, error)) {
+//                     gs_flatpak_error_convert (error);
+//                     return NULL;
+//             }
+#if 0
 
                /* allow peeking into this for debugging */
-               if (g_getenv ("GS_FLATPAK_DEBUG_APPSTREAM") != NULL) {
+               if (g_getenv ("GS_XMLB_VERBOSE") != NULL) {
                        g_autoptr(GString) str = NULL;
-                       str = as_store_to_xml (store,
+                       str = as_store_to_xml (silo,
                                               AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
                                               AS_NODE_TO_XML_FLAG_FORMAT_INDENT);
                        g_debug ("showing AppStream data: %s", str->str);
                }
 
                /* check for sanity */
-               if (as_store_get_size (store) == 0) {
+               if (as_store_get_size (silo) == 0) {
                        g_set_error_literal (error,
                                             GS_PLUGIN_ERROR,
                                             GS_PLUGIN_ERROR_NOT_SUPPORTED,
@@ -2160,12 +2235,12 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self,
                        return NULL;
                }
                g_debug ("%u applications found in AppStream data",
-                        as_store_get_size (store));
+                        as_store_get_size (silo));
 
                /* find app */
                id = g_strdup_printf ("%s.desktop", gs_flatpak_app_get_ref_name (app));
-               item = as_store_get_app_by_id (store, id);
-               if (item == NULL) {
+               component = as_store_get_app_by_id (silo, id);
+               if (component == NULL) {
                        g_set_error (error,
                                     GS_PLUGIN_ERROR,
                                     GS_PLUGIN_ERROR_INVALID_FORMAT,
@@ -2175,8 +2250,11 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self,
                }
 
                /* copy details from AppStream to app */
-               if (!gs_appstream_refine_app (self->plugin, app, item, error))
+               if (!gs_appstream_refine_app (self->plugin, app, silo, component,
+                                             GS_PLUGIN_REFINE_FLAGS_DEFAULT,
+                                             error))
                        return NULL;
+#endif
        } else {
                g_warning ("no appstream metadata in file");
                gs_app_set_name (app, GS_APP_QUALITY_LOWEST,
@@ -2223,6 +2301,7 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self,
                            GError **error)
 {
        GsApp *runtime;
+       const gchar *const *locales = g_get_language_names ();
        const gchar *remote_name;
        gsize len = 0;
        g_autofree gchar *contents = NULL;
@@ -2232,6 +2311,8 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self,
        g_autoptr(GsApp) app = NULL;
        g_autoptr(FlatpakRemote) xremote = NULL;
        g_autoptr(GKeyFile) kf = NULL;
+       g_autoptr(XbBuilder) builder = xb_builder_new ();
+       g_autoptr(XbSilo) silo = NULL;
        g_autofree gchar *origin_url = NULL;
        g_autofree gchar *ref_comment = NULL;
        g_autofree gchar *ref_description = NULL;
@@ -2240,6 +2321,10 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self,
        g_autofree gchar *ref_title = NULL;
        g_autofree gchar *ref_name = NULL;
 
+       /* add current locales */
+       for (guint i = 0; locales[i] != NULL; i++)
+               xb_builder_add_locale (builder, locales[i]);
+
        /* get file data */
        if (!g_file_load_contents (file,
                                   cancellable,
@@ -2368,11 +2453,29 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self,
        }
 
        /* parse it */
-       if (!gs_flatpak_add_apps_from_xremote (self, xremote, cancellable, error))
+       if (!gs_flatpak_add_apps_from_xremote (self, builder, xremote, cancellable, error))
                return NULL;
 
+       /* build silo */
+       silo = xb_builder_compile (builder,
+                                  XB_BUILDER_COMPILE_FLAG_SINGLE_LANG,
+                                  cancellable,
+                                  error);
+       if (silo == NULL)
+               return NULL;
+       if (g_getenv ("GS_XMLB_VERBOSE") != NULL) {
+               g_autofree gchar *xml = NULL;
+               xml = xb_silo_export (silo,
+                                     XB_NODE_EXPORT_FLAG_FORMAT_INDENT |
+                                     XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE,
+                                     NULL);
+               g_debug ("showing AppStream data: %s", xml);
+       }
+
        /* get extra AppStream data if available */
-       if (!gs_flatpak_refine_appstream (self, app, error))
+       if (!gs_flatpak_refine_appstream (self, app, silo,
+                                         G_MAXUINT64,
+                                         error))
                return NULL;
 
        /* success */
@@ -2387,8 +2490,10 @@ gs_flatpak_search (GsFlatpak *self,
                   GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_store_search (self->plugin, self->store, values, list_tmp,
-                                       cancellable, error))
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_search (self->plugin, self->silo, values, list_tmp,
+                                 cancellable, error))
                return FALSE;
        gs_flatpak_claim_app_list (self, list_tmp);
        gs_app_list_add_list (list, list_tmp);
@@ -2403,9 +2508,11 @@ gs_flatpak_add_category_apps (GsFlatpak *self,
                              GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_store_add_category_apps (self->plugin, self->store,
-                                                  category, list_tmp,
-                                                  cancellable, error))
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_add_category_apps (self->plugin, self->silo,
+                                            category, list_tmp,
+                                            cancellable, error))
                return FALSE;
        gs_flatpak_claim_app_list (self, list_tmp);
        gs_app_list_add_list (list, list_tmp);
@@ -2418,8 +2525,10 @@ gs_flatpak_add_categories (GsFlatpak *self,
                           GCancellable *cancellable,
                           GError **error)
 {
-       return gs_appstream_store_add_categories (self->plugin, self->store,
-                                                 list, cancellable, error);
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       return gs_appstream_add_categories (self->plugin, self->silo,
+                                           list, cancellable, error);
 }
 
 gboolean
@@ -2429,10 +2538,11 @@ gs_flatpak_add_popular (GsFlatpak *self,
                        GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_add_popular (self->plugin, self->store, list_tmp,
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_add_popular (self->plugin, self->silo, list_tmp,
                                       cancellable, error))
                return FALSE;
-       gs_flatpak_claim_app_list (self, list_tmp);
        gs_app_list_add_list (list, list_tmp);
        return TRUE;
 }
@@ -2444,10 +2554,11 @@ gs_flatpak_add_featured (GsFlatpak *self,
                         GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_add_featured (self->plugin, self->store, list_tmp,
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_add_featured (self->plugin, self->silo, list_tmp,
                                        cancellable, error))
                return FALSE;
-       gs_flatpak_claim_app_list (self, list_tmp);
        gs_app_list_add_list (list, list_tmp);
        return TRUE;
 }
@@ -2460,7 +2571,9 @@ gs_flatpak_add_alternates (GsFlatpak *self,
                           GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_add_alternates (self->plugin, self->store, app, list_tmp,
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_add_alternates (self->plugin, self->silo, app, list_tmp,
                                          cancellable, error))
                return FALSE;
        gs_app_list_add_list (list, list_tmp);
@@ -2475,7 +2588,9 @@ gs_flatpak_add_recent (GsFlatpak *self,
                       GError **error)
 {
        g_autoptr(GsAppList) list_tmp = gs_app_list_new ();
-       if (!gs_appstream_add_recent (self->plugin, self->store, list_tmp, age,
+       if (!gs_flatpak_rescan_appstream_store (self, cancellable, error))
+               return FALSE;
+       if (!gs_appstream_add_recent (self->plugin, self->silo, list_tmp, age,
                                      cancellable, error))
                return FALSE;
        gs_flatpak_claim_app_list (self, list_tmp);
@@ -2483,24 +2598,11 @@ gs_flatpak_add_recent (GsFlatpak *self,
        return TRUE;
 }
 
-static void
-gs_flatpak_store_app_added_cb (AsStore *store, AsApp *app, GsFlatpak *self)
-{
-       gs_appstream_add_extra_info (self->plugin, app);
-}
-
-static void
-gs_flatpak_store_app_removed_cb (AsStore *store, AsApp *app, GsFlatpak *self)
-{
-       g_debug ("AppStream app was removed, doing delete from global cache");
-       gs_plugin_cache_remove (self->plugin, as_app_get_unique_id (app));
-}
-
 const gchar *
 gs_flatpak_get_id (GsFlatpak *self)
 {
        if (self->id == NULL) {
-               GString *str = g_string_new ("GsFlatpak");
+               GString *str = g_string_new ("flatpak");
                g_string_append_printf (str, "-%s",
                                        as_app_scope_to_string (self->scope));
                if (flatpak_installation_get_id (self->installation) != NULL) {
@@ -2537,11 +2639,12 @@ gs_flatpak_finalize (GObject *object)
                g_signal_handler_disconnect (self->monitor, self->changed_id);
                self->changed_id = 0;
        }
+       if (self->silo != NULL)
+               g_object_unref (self->silo);
 
        g_free (self->id);
        g_object_unref (self->installation);
        g_object_unref (self->plugin);
-       g_object_unref (self->store);
        g_hash_table_unref (self->broken_remotes);
 
        G_OBJECT_CLASS (gs_flatpak_parent_class)->finalize (object);
@@ -2559,22 +2662,6 @@ gs_flatpak_init (GsFlatpak *self)
 {
        self->broken_remotes = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                      g_free, NULL);
-       self->store = as_store_new ();
-       g_signal_connect (self->store, "app-added",
-                         G_CALLBACK (gs_flatpak_store_app_added_cb),
-                         self);
-       g_signal_connect (self->store, "app-removed",
-                         G_CALLBACK (gs_flatpak_store_app_removed_cb),
-                         self);
-       as_store_set_add_flags (self->store, AS_STORE_ADD_FLAG_USE_UNIQUE_ID);
-       as_store_set_watch_flags (self->store, AS_STORE_WATCH_FLAG_REMOVED);
-       as_store_set_search_match (self->store,
-                                  AS_APP_SEARCH_MATCH_MIMETYPE |
-                                  AS_APP_SEARCH_MATCH_PKGNAME |
-                                  AS_APP_SEARCH_MATCH_COMMENT |
-                                  AS_APP_SEARCH_MATCH_NAME |
-                                  AS_APP_SEARCH_MATCH_KEYWORD |
-                                  AS_APP_SEARCH_MATCH_ID);
 }
 
 GsFlatpak *
diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c
index 1b7c4d58..ee01017b 100644
--- a/plugins/flatpak/gs-plugin-flatpak.c
+++ b/plugins/flatpak/gs-plugin-flatpak.c
@@ -295,6 +295,12 @@ gs_plugin_flatpak_refine_app (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        GsFlatpak *flatpak = NULL;
 
+       /* not us */
+       if (gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_FLATPAK) {
+               g_debug ("%s not a package, ignoring", gs_app_get_unique_id (app));
+               return TRUE;
+       }
+
        /* we have to look for the app in all GsFlatpak stores */
        if (gs_app_get_scope (app) == AS_APP_SCOPE_UNKNOWN) {
                for (guint i = 0; i < priv->flatpaks->len; i++) {
diff --git a/plugins/flatpak/gs-self-test.c b/plugins/flatpak/gs-self-test.c
index 03a771df..23782d0a 100644
--- a/plugins/flatpak/gs-self-test.c
+++ b/plugins/flatpak/gs-self-test.c
@@ -22,6 +22,8 @@
 
 #include "config.h"
 
+#include <glib/gstdio.h>
+
 #include "gnome-software-private.h"
 
 #include "gs-flatpak-app.h"
@@ -230,6 +232,8 @@ gs_plugins_flatpak_app_with_runtime_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -323,6 +327,8 @@ gs_plugins_flatpak_app_with_runtime_func (GsPluginLoader *plugin_loader)
                                         "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME |
                                                         GS_PLUGIN_REFINE_FLAGS_REQUIRE_PERMISSIONS |
                                                         GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION |
+                                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_KUDOS |
+                                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_RUNTIME |
                                                         GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
                                         NULL);
        list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
@@ -496,6 +502,8 @@ gs_plugins_flatpak_app_missing_runtime_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -642,6 +650,8 @@ gs_plugins_flatpak_runtime_repo_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* write a flatpakrepo file */
@@ -677,7 +687,7 @@ gs_plugins_flatpak_runtime_repo_func (GsPluginLoader *plugin_loader)
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE_LOCAL);
        g_assert_cmpstr (gs_app_get_id (app), ==, "org.test.Chiron.desktop");
        g_assert (as_utils_unique_id_equal (gs_app_get_unique_id (app),
-                       "user/flatpak/org.test.Chiron-origin/desktop/org.test.Chiron.desktop/master"));
+                       "user/flatpak/*/desktop/org.test.Chiron.desktop/master"));
        g_assert (gs_app_get_local_file (app) != NULL);
 
        /* get runtime */
@@ -777,6 +787,8 @@ gs_plugins_flatpak_runtime_repo_redundant_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* write a flatpakrepo file */
@@ -841,7 +853,7 @@ gs_plugins_flatpak_runtime_repo_redundant_func (GsPluginLoader *plugin_loader)
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE_LOCAL);
        g_assert_cmpstr (gs_app_get_id (app), ==, "org.test.Chiron.desktop");
        g_assert (as_utils_unique_id_equal (gs_app_get_unique_id (app),
-                       "user/flatpak/org.test.Chiron-origin/desktop/org.test.Chiron.desktop/master"));
+                       "user/flatpak/*/desktop/org.test.Chiron.desktop/master"));
        g_assert (gs_app_get_local_file (app) != NULL);
 
        /* get runtime */
@@ -930,6 +942,8 @@ gs_plugins_flatpak_broken_remote_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -1018,6 +1032,8 @@ gs_plugins_flatpak_ref_func (GsPluginLoader *plugin_loader)
        g_autoptr(GString) str = g_string_new (NULL);
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -1093,6 +1109,8 @@ gs_plugins_flatpak_ref_func (GsPluginLoader *plugin_loader)
        plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_FILE_TO_APP,
                                         "file", file,
                                         "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION |
+                                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL |
+                                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION |
                                                         GS_PLUGIN_REFINE_FLAGS_REQUIRE_RUNTIME,
                                         NULL);
        app = gs_plugin_loader_job_process_app (plugin_loader, plugin_job, NULL, &error);
@@ -1237,6 +1255,8 @@ gs_plugins_flatpak_app_update_func (GsPluginLoader *plugin_loader)
        g_autoptr(GMainLoop) loop = g_main_loop_new (NULL, FALSE);
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -1484,6 +1504,8 @@ gs_plugins_flatpak_runtime_extension_func (GsPluginLoader *plugin_loader)
        g_autoptr(GMainLoop) loop = g_main_loop_new (NULL, FALSE);
 
        /* drop all caches */
+       g_unlink ("/var/tmp/self-test/flatpak-user/components.xmlb");
+       g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* no flatpak, abort */
@@ -1703,7 +1725,9 @@ main (int argc, char **argv)
 
        g_test_init (&argc, &argv, NULL);
        g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+       g_setenv ("GS_XMLB_VERBOSE", "1", TRUE);
        g_setenv ("GS_SELF_TEST_FLATPAK_DATADIR", tmp_root, TRUE);
+       g_setenv ("GS_SELF_TEST_CORE_DATADIR", tmp_root, TRUE);         //FIXME
        g_setenv ("GS_SELF_TEST_PLUGIN_ERROR_FAIL_HARD", "1", TRUE);
 
        /* allow dist'ing with no gnome-software installed */
diff --git a/plugins/flatpak/meson.build b/plugins/flatpak/meson.build
index 25b853db..ac82bfc0 100644
--- a/plugins/flatpak/meson.build
+++ b/plugins/flatpak/meson.build
@@ -19,7 +19,11 @@ shared_module(
   install : true,
   install_dir: plugin_dir,
   c_args : cargs,
-  dependencies : [ plugin_libs, flatpak ]
+  dependencies : [
+    plugin_libs,
+    flatpak,
+    libxmlb,
+  ],
 )
 metainfo = 'org.gnome.Software.Plugin.Flatpak.metainfo.xml'
 
diff --git a/plugins/modalias/gs-self-test.c b/plugins/modalias/gs-self-test.c
index dbe628a7..1aa11a30 100644
--- a/plugins/modalias/gs-self-test.c
+++ b/plugins/modalias/gs-self-test.c
@@ -21,6 +21,8 @@
 
 #include "config.h"
 
+#include <glib/gstdio.h>
+
 #include "gnome-software-private.h"
 
 #include "gs-test.h"
@@ -34,6 +36,9 @@ gs_plugins_modalias_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsAppList) list = NULL;
        g_autoptr(GsPluginJob) plugin_job = NULL;
 
+       /* drop all caches */
+//     g_unlink ("/var/tmp/self-test/appstream/components.xmlb");
+
        /* get search result based on addon keyword */
        plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_SEARCH,
                                         "search", "colorhug2",
@@ -56,6 +61,7 @@ gs_plugins_modalias_func (GsPluginLoader *plugin_loader)
 int
 main (int argc, char **argv)
 {
+       const gchar *tmp_root = "/var/tmp/self-test";
        gboolean ret;
        g_autofree gchar *xml = NULL;
        g_autoptr(GError) error = NULL;
@@ -83,6 +89,7 @@ main (int argc, char **argv)
                "  </component>\n"
                "</components>\n");
        g_setenv ("GS_SELF_TEST_APPSTREAM_XML", xml, TRUE);
+       g_setenv ("GS_SELF_TEST_CORE_DATADIR", tmp_root, TRUE);
 
        /* only critical and error are fatal */
        g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
diff --git a/plugins/shell-extensions/gs-plugin-shell-extensions.c 
b/plugins/shell-extensions/gs-plugin-shell-extensions.c
index 28259adb..13f03c83 100644
--- a/plugins/shell-extensions/gs-plugin-shell-extensions.c
+++ b/plugins/shell-extensions/gs-plugin-shell-extensions.c
@@ -26,6 +26,7 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <json-glib/json-glib.h>
+#include <xmlb.h>
 
 #include <gnome-software.h>
 
@@ -437,7 +438,8 @@ gs_plugin_refine_app (GsPlugin *plugin,
 
 static gboolean
 gs_plugin_shell_extensions_parse_version (GsPlugin *plugin,
-                                         AsApp *app,
+                                         const gchar *component_id,
+                                         XbBuilderNode *app,
                                          JsonObject *ver_map,
                                          GError **error)
 {
@@ -445,7 +447,7 @@ gs_plugin_shell_extensions_parse_version (GsPlugin *plugin,
        JsonObject *json_ver = NULL;
        gint64 version;
        g_autofree gchar *shell_version = NULL;
-       g_autoptr(AsRelease) release = NULL;
+       g_autoptr(XbBuilderNode) release = NULL;
 
        /* look for version, major.minor.micro */
        if (json_object_has_member (ver_map, priv->shell_version)) {
@@ -468,7 +470,7 @@ gs_plugin_shell_extensions_parse_version (GsPlugin *plugin,
        /* FIXME: mark as incompatible? */
        if (json_ver == NULL) {
                g_debug ("no version_map for %s: %s",
-                        as_app_get_id (app),
+                        component_id,
                         priv->shell_version);
                return TRUE;
        }
@@ -485,108 +487,106 @@ gs_plugin_shell_extensions_parse_version (GsPlugin *plugin,
        shell_version = g_strdup_printf ("%" G_GINT64_FORMAT, version);
 
        /* add a dummy release */
-       release = as_release_new ();
-       as_release_set_version (release, shell_version);
-       as_app_add_release (app, release);
+       xb_builder_node_insert_text (app, "release", NULL,
+                                    "version", shell_version,
+                                    NULL);
        return TRUE;
 }
 
-static AsApp *
+
+
+static XbBuilderNode *
 gs_plugin_shell_extensions_parse_app (GsPlugin *plugin,
                                      JsonObject *json_app,
                                      GError **error)
 {
-       g_autoptr(AsApp) app = NULL;
        JsonObject *json_ver_map;
        const gchar *tmp;
+       g_autofree gchar *component_id = NULL;
+       g_autoptr(XbBuilderNode) app = NULL;
+       g_autoptr(XbBuilderNode) metadata = NULL;
 
-       app = as_app_new ();
-       as_app_set_kind (app, AS_APP_KIND_SHELL_EXTENSION);
-       as_app_set_project_license (app, "GPL-2.0+");
+       app = xb_builder_node_new ("component");
+       xb_builder_node_set_attr (app, "kind", "shell-extension");
+       xb_builder_node_insert_text (app, "project_license", "GPL-2.0+", NULL);
+       metadata = xb_builder_node_insert (app, "metadata", NULL);
 
        tmp = json_object_get_string_member (json_app, "description");
        if (tmp != NULL) {
-               g_autofree gchar *desc = NULL;
-               desc = as_markup_import (tmp, AS_MARKUP_CONVERT_FORMAT_SIMPLE, error);
-               if (desc == NULL) {
-                       gs_utils_error_convert_appstream (error);
-                       return NULL;
-               }
-               as_app_set_description (app, NULL, desc);
+               g_auto(GStrv) paras = g_strsplit (tmp, "\n", -1);
+               g_autoptr(XbBuilderNode) desc = xb_builder_node_insert (app, "description", NULL);
+               for (guint i = 0; paras[i] != NULL; i++)
+                       xb_builder_node_insert_text (desc, "p", paras[i], NULL);
        }
        tmp = json_object_get_string_member (json_app, "screenshot");
        if (tmp != NULL) {
-               g_autoptr(AsScreenshot) ss = NULL;
-               g_autoptr(AsImage) im = NULL;
+               g_autoptr(XbBuilderNode) screenshots = NULL;
+               g_autoptr(XbBuilderNode) screenshot = NULL;
                g_autofree gchar *uri = NULL;
+               screenshots = xb_builder_node_insert (app, "screenshots", NULL);
+               screenshot = xb_builder_node_insert (screenshots, "screenshot",
+                                                    "kind", "default",
+                                                    NULL);
                uri = g_build_path ("/", SHELL_EXTENSIONS_API_URI, tmp, NULL);
-               im = as_image_new ();
-               as_image_set_kind (im, AS_IMAGE_KIND_SOURCE);
-               as_image_set_url (im, uri);
-               ss = as_screenshot_new ();
-               as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_DEFAULT);
-               as_screenshot_add_image (ss, im);
-               as_app_add_screenshot (app, ss);
+               xb_builder_node_insert_text (screenshot, "image", uri,
+                                            "kind", "source",
+                                            NULL);
        }
        tmp = json_object_get_string_member (json_app, "name");
        if (tmp != NULL)
-               as_app_set_name (app, NULL, tmp);
+               xb_builder_node_insert_text (app, "name", tmp, NULL);
        tmp = json_object_get_string_member (json_app, "uuid");
        if (tmp != NULL) {
-               g_autofree gchar *id = NULL;
-               id = as_utils_appstream_id_build (tmp);
-               as_app_set_id (app, id);
-               as_app_add_metadata (app, "shell-extensions::uuid", tmp);
+               component_id = as_utils_appstream_id_build (tmp);
+               xb_builder_node_insert_text (app, "id", component_id, NULL);
+               xb_builder_node_insert_text (metadata, "value", tmp,
+                                            "key", "shell-extensions::uuid",
+                                            NULL);
        }
        tmp = json_object_get_string_member (json_app, "link");
        if (tmp != NULL) {
                g_autofree gchar *uri = NULL;
                uri = g_build_filename (SHELL_EXTENSIONS_API_URI, tmp, NULL);
-               as_app_add_url (app, AS_URL_KIND_HOMEPAGE, uri);
+               xb_builder_node_insert_text (app, "url", uri,
+                                            "type", "homepage",
+                                            NULL);
        }
        tmp = json_object_get_string_member (json_app, "icon");
        if (tmp != NULL) {
-               g_autoptr(AsIcon) ic = NULL;
                /* just use a stock icon as the remote icons are
                 * sometimes missing, poor quality and low resolution */
-               ic = as_icon_new ();
-               as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
-               as_icon_set_name (ic, "application-x-addon-symbolic");
-               as_app_add_icon (app, ic);
+               xb_builder_node_insert_text (app, "icon",
+                                            "application-x-addon-symbolic",
+                                            "type", "stock",
+                                            NULL);
        }
 
        /* try to get version */
        json_ver_map = json_object_get_object_member (json_app, "shell_version_map");
        if (json_ver_map != NULL) {
                if (!gs_plugin_shell_extensions_parse_version (plugin,
+                                                              component_id,
                                                               app,
                                                               json_ver_map,
                                                               error))
                        return NULL;
        }
 
-       /* we have no data :/ */
-       as_app_add_metadata (app, "GnomeSoftware::Plugin",
-                            gs_plugin_get_name (plugin));
-       as_app_add_metadata (app, "GnomeSoftware::OriginHostnameUrl",
-                            SHELL_EXTENSIONS_API_URI);
-
        return g_steal_pointer (&app);
 }
 
-static GPtrArray *
+static XbBuilderNode *
 gs_plugin_shell_extensions_parse_apps (GsPlugin *plugin,
                                       const gchar *data,
                                       gssize data_len,
                                       GError **error)
 {
-       GPtrArray *apps;
        JsonArray *json_extensions_array;
        JsonNode *json_extensions;
        JsonNode *json_root;
        JsonObject *json_item;
-       guint i;
        g_autoptr(JsonParser) json_parser = NULL;
+       g_autoptr(XbBuilderNode) apps = NULL;
 
        /* nothing */
        if (data == NULL) {
@@ -628,7 +628,7 @@ gs_plugin_shell_extensions_parse_apps (GsPlugin *plugin,
        }
 
        /* load extensions */
-       apps = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+       apps = xb_builder_node_new ("components");
        json_extensions = json_object_get_member (json_item, "extensions");
        if (json_extensions == NULL) {
                g_set_error_literal (error,
@@ -647,36 +647,36 @@ gs_plugin_shell_extensions_parse_apps (GsPlugin *plugin,
        }
 
        /* parse each app */
-       for (i = 0; i < json_array_get_length (json_extensions_array); i++) {
-               AsApp *app;
+       for (guint i = 0; i < json_array_get_length (json_extensions_array); i++) {
+               XbBuilderNode *component;
                JsonNode *json_extension;
                JsonObject *json_extension_obj;
                json_extension = json_array_get_element (json_extensions_array, i);
                json_extension_obj = json_node_get_object (json_extension);
-               app = gs_plugin_shell_extensions_parse_app (plugin,
+               component = gs_plugin_shell_extensions_parse_app (plugin,
                                                            json_extension_obj,
                                                            error);
-               if (app == NULL)
+               if (component == NULL)
                        return NULL;
-               g_ptr_array_add (apps, app);
+               xb_builder_node_add_child (apps, component);
        }
 
-       return apps;
+       return g_steal_pointer (&apps);
 }
 
-static GPtrArray *
+static XbBuilderNode *
 gs_plugin_shell_extensions_get_apps (GsPlugin *plugin,
                                     guint cache_age,
                                     GCancellable *cancellable,
                                     GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GPtrArray *apps;
        g_autofree gchar *cachefn = NULL;
        g_autofree gchar *uri = NULL;
-       g_autoptr(GFile) cachefn_file = NULL;
        g_autoptr(GBytes) data = NULL;
+       g_autoptr(GFile) cachefn_file = NULL;
        g_autoptr(GsApp) app_dl = gs_app_new (gs_plugin_get_name (plugin));
+       g_autoptr(XbBuilderNode) apps = NULL;
 
        /* look in the cache */
        cachefn = gs_utils_get_cache_filename ("shell-extensions",
@@ -730,7 +730,7 @@ gs_plugin_shell_extensions_get_apps (GsPlugin *plugin,
                                  error))
                return NULL;
 
-       return apps;
+       return g_steal_pointer (&apps);
 }
 
 static gboolean
@@ -740,25 +740,26 @@ gs_plugin_shell_extensions_refresh (GsPlugin *plugin,
                                    GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       AsApp *app;
        gboolean repo_enabled;
        const gchar *fn_test;
-       guint i;
        g_autofree gchar *fn = NULL;
-       g_autoptr(GPtrArray) apps = NULL;
-       g_autoptr(AsStore) store = NULL;
+       g_autoptr(GError) error_local = NULL;
        g_autoptr(GFile) file = NULL;
+       g_autoptr(XbBuilder) builder = xb_builder_new ();
+       g_autoptr(XbBuilderNode) apps = NULL;
+       g_autoptr(XbSilo) silo = NULL;
 
        /* check age */
        fn_test = g_getenv ("GS_SELF_TEST_SHELL_EXTENSIONS_XML_FN");
        if (fn_test != NULL) {
                fn = g_strdup (fn_test);
        } else {
-               fn = g_build_filename (g_get_user_data_dir (),
-                                      "app-info",
-                                      "xmls",
-                                      "extensions-web.xml",
-                                      NULL);
+               fn = gs_utils_get_cache_filename ("shell-extensions",
+                                                 "extensions-web.xmlb",
+                                                 GS_UTILS_CACHE_FLAG_WRITEABLE,
+                                                 error);
+               if (fn == NULL)
+                       return FALSE;
        }
 
        /* remove old appstream data if the repo is disabled */
@@ -785,25 +786,22 @@ gs_plugin_shell_extensions_refresh (GsPlugin *plugin,
        if (apps == NULL)
                return FALSE;
 
-       /* add to local store */
-       store = as_store_new ();
-       as_store_set_origin (store, "extensions-web");
-       for (i = 0; i < apps->len; i++) {
-               app = g_ptr_array_index (apps, i);
-               g_debug ("adding to local store %s", as_app_get_id (app));
-               as_store_add_app (store, app);
-       }
+       /* add to builder */
+       xb_builder_import_node (builder, apps);
 
        /* save to disk */
-       if (!gs_mkdir_parent (fn, error))
+       silo = xb_builder_ensure (builder, file,
+                                 XB_BUILDER_COMPILE_FLAG_NONE,
+                                 cancellable, &error_local);
+       if (silo == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "failed to compile %s",
+                            error_local->message);
                return FALSE;
-       g_debug ("saving to %s", fn);
-       return as_store_to_file (store, file,
-                                AS_NODE_TO_XML_FLAG_ADD_HEADER |
-                                AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
-                                AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
-                                cancellable,
-                                error);
+       }
+       return TRUE;
 }
 
 gboolean
diff --git a/plugins/shell-extensions/gs-self-test.c b/plugins/shell-extensions/gs-self-test.c
index b0fb993d..29db7106 100644
--- a/plugins/shell-extensions/gs-self-test.c
+++ b/plugins/shell-extensions/gs-self-test.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include <glib/gstdio.h>
+#include <xmlb.h>
 
 #include "gnome-software-private.h"
 
@@ -77,12 +78,13 @@ gs_plugins_shell_extensions_installed_func (GsPluginLoader *plugin_loader)
 static void
 gs_plugins_shell_extensions_remote_func (GsPluginLoader *plugin_loader)
 {
-       const gchar *xml_fn = "/var/tmp/self-test/extensions-web.xml";
+       const gchar *xml_fn = "/var/tmp/self-test/extensions-web.xmlb";
        gboolean ret;
        g_autoptr(GError) error = NULL;
        g_autoptr(GFile) file = NULL;
+       g_autoptr(GPtrArray) components = NULL;
        g_autoptr(GsPluginJob) plugin_job = NULL;
-       g_autoptr(AsStore) store = NULL;
+       g_autoptr(XbSilo) silo = NULL;
 
        /* no shell-extensions, abort */
        if (!gs_plugin_loader_get_enabled (plugin_loader, "shell-extensions")) {
@@ -103,12 +105,17 @@ gs_plugins_shell_extensions_remote_func (GsPluginLoader *plugin_loader)
        g_assert (ret);
 
        /* ensure file was populated */
-       store = as_store_new ();
+       silo = xb_silo_new ();
        file = g_file_new_for_path (xml_fn);
-       ret = as_store_from_file (store, file, NULL, NULL, &error);
+       ret = xb_silo_load_from_file (silo, file,
+                                     XB_SILO_LOAD_FLAG_NONE,
+                                     NULL, &error);
        g_assert_no_error (error);
        g_assert (ret);
-       g_assert_cmpint (as_store_get_size (store), >, 20);
+       components = xb_silo_query (silo, "components/component", 0, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (components);
+       g_assert_cmpint (components->len, >, 20);
 }
 
 int
diff --git a/plugins/shell-extensions/meson.build b/plugins/shell-extensions/meson.build
index 655562bc..dec9d97d 100644
--- a/plugins/shell-extensions/meson.build
+++ b/plugins/shell-extensions/meson.build
@@ -11,7 +11,10 @@ sources : 'gs-plugin-shell-extensions.c',
   install : true,
   install_dir: plugin_dir,
   c_args : cargs,
-  dependencies : plugin_libs
+  dependencies : [
+    plugin_libs,
+    libxmlb,
+  ],
 )
 
 if get_option('tests')
@@ -27,6 +30,7 @@ if get_option('tests')
     ],
     dependencies : [
       plugin_libs,
+      libxmlb,
     ],
     link_with : [
       libgnomesoftware


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]