diff --git a/gplugin/gplugin-source.h b/gplugin/gplugin-source.h
new file mode 100644
--- /dev/null
+++ b/gplugin/gplugin-source.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011-2022 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPLUGIN_SOURCE_PRIVATE_H
+#define GPLUGIN_SOURCE_PRIVATE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_TYPE_SOURCE (gplugin_source_get_type())
+G_DECLARE_INTERFACE(GPluginSource, gplugin_source, GPLUGIN, SOURCE,
+                    GObject)
+
+struct _GPluginSourceInterface {
+    /*< private >*/
+    GTypeInterface parent;
+
+    /*< public >*/
+    gboolean (*scan)(GPluginSource *source);
+
+    /*< private >*/
+    gpointer reserved[4];
+};
+
+gboolean gplugin_source_scan(GPluginSource *source);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_SOURCE_PRIVATE_H */
# HG changeset patch
# Node ID 0db25818f820
# Parent  5dca05f85b18
diff --git a/gplugin/gplugin-source.c b/gplugin/gplugin-source.c
new file mode 100644
--- /dev/null
+++ b/gplugin/gplugin-source.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011-2022 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "gplugin-source.h"
+
+/**
+ * GPluginSource:
+ *
+ * An interface that the manager will call during refresh to query plugins.
+ *
+ * Since: 0.39.0
+ */
+
+G_DEFINE_INTERFACE(GPluginSource, gplugin_source, G_TYPE_OBJECT)
+
+/******************************************************************************
+ * GInterface implementation
+ *****************************************************************************/
+static void
+gplugin_source_default_init(G_GNUC_UNUSED GPluginSourceInterface *iface) {
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/**
+ * gplugin_source_scan:
+ * @source: The instance.
+ *
+ * This method is called when [method@GPlugin.Manager.refresh] is running. The
+ * source should scan its available sources for plugins. For the filesystem
+ * source, this is paths that have been registered with the manager.
+ *
+ * The implementation should return TRUE if it found a new unqueried plugin,
+ * which will tell the manager to continue scanning.
+ *
+ * Returns: %TRUE if an unqueried plugin was found, %FALSE otherwise.
+ *
+ * Since: 0.39.0
+ */
+gboolean
+gplugin_source_scan(GPluginSource *source) {
+    GPluginSourceInterface *iface = NULL;
+
+    g_return_val_if_fail(GPLUGIN_IS_SOURCE(source), FALSE);
+
+    iface = GPLUGIN_SOURCE_GET_IFACE(source);
+    if(iface && iface->scan) {
+        return iface->scan(source);
+    }
+
+    return FALSE;
+}
diff --git a/gplugin/meson.build b/gplugin/meson.build
--- a/gplugin/meson.build
+++ b/gplugin/meson.build
@@ -35,10 +35,12 @@
 
 GPLUGIN_PRIVATE_HEADERS = [
 	'gplugin-file-tree.h',
+	'gplugin-source.h',
 ]
 
 GPLUGIN_PRIVATE_SOURCES = [
 	'gplugin-file-tree.c',
+	'gplugin-source.c',
 ]
 
 GPLUGIN_PRIVATE_BUILT_HEADERS = [
diff --git a/gplugin/tests/meson.build b/gplugin/tests/meson.build
--- a/gplugin/tests/meson.build
+++ b/gplugin/tests/meson.build
@@ -71,6 +71,10 @@
 	dependencies : [gplugin_dep, GLIB, GOBJECT])
 test('Signals', e)
 
+e = executable('test-source', 'test-source.c',
+	dependencies : [gplugin_dep, GLIB, GOBJECT])
+test('Source', e)
+
 e = executable('test-version-compare', 'test-version-compare.c',
 	dependencies : [gplugin_dep, GLIB, GOBJECT])
 test('Version Compare', e)
diff --git a/gplugin/tests/test-source.c b/gplugin/tests/test-source.c
new file mode 100644
--- /dev/null
+++ b/gplugin/tests/test-source.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <gplugin.h>
+
+#include "gplugin-source.h"
+
+/******************************************************************************
+ * TestSource Implementation
+ *****************************************************************************/
+G_DECLARE_FINAL_TYPE(TestGPluginSource, test_gplugin_source, TEST_GPLUGIN,
+                     SOURCE, GObject)
+
+struct _TestGPluginSource {
+	GObject parent;
+
+	gboolean scan_result;
+};
+
+static gboolean
+test_gplugin_source_scan(GPluginSource *source) {
+	TestGPluginSource *test_source = TEST_GPLUGIN_SOURCE(source);
+
+	return test_source->scan_result;
+}
+
+static void
+test_gplugin_source_iface_init(GPluginSourceInterface *iface) {
+	iface->scan = test_gplugin_source_scan;
+}
+
+G_DEFINE_TYPE_WITH_CODE(TestGPluginSource, test_gplugin_source, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_SOURCE, test_gplugin_source_iface_init))
+
+static void
+test_gplugin_source_init(G_GNUC_UNUSED TestGPluginSource *source) {
+}
+
+static void
+test_gplugin_source_class_init(G_GNUC_UNUSED TestGPluginSourceClass *klass) {
+}
+
+static GPluginSource *
+test_gplugin_source_new(gboolean scan_result) {
+	TestGPluginSource *source = g_object_new(test_gplugin_source_get_type(), NULL);
+
+	source->scan_result = scan_result;
+
+	return GPLUGIN_SOURCE(source);
+}
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_gplugin_source_scan_false(void) {
+	GPluginSource *source = test_gplugin_source_new(FALSE);
+
+	g_assert_false(gplugin_source_scan(source));
+
+	g_clear_object(&source);
+}
+
+static void
+test_gplugin_source_scan_true(void) {
+	GPluginSource *source = test_gplugin_source_new(TRUE);
+
+	g_assert_true(gplugin_source_scan(source));
+
+	g_clear_object(&source);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv)
+{
+	g_test_init(&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+
+	g_test_add_func("/source/scan/false", test_gplugin_source_scan_false);
+	g_test_add_func("/source/scan/true", test_gplugin_source_scan_true);
+
+	return g_test_run();
+}
diff --git a/po/POTFILES b/po/POTFILES
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -23,6 +23,7 @@
 gplugin/gplugin-plugin-info.c
 gplugin/gplugin-plugin.c
 gplugin/gplugin-private.c
+gplugin/gplugin-source.c
 gplugin/gplugin-version.c
 lua/gplugin-lua-core.c
 lua/gplugin-lua-loader.c
