diff --git a/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala b/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala
index 649b5d89..22e2aa7c 100644
--- a/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala
+++ b/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala
@@ -34,7 +34,7 @@ case class AddonMeta(
     s"${meta.name}-${meta.version}"
 
   def parseResult: Boolean =
-    options.exists(_.collectOutput)
+    options.forall(_.collectOutput)
 
   def ignoreResult: Boolean =
     !parseResult
@@ -200,7 +200,7 @@ object AddonMeta {
       )
     )
     zipFile
-      .through(Zip.unzip(8192, Glob("**/docspell-addon.*")))
+      .through(Zip.unzip(8192, Glob("docspell-addon.*|**/docspell-addon.*")))
       .filter(bin => !bin.name.endsWith("/"))
       .flatMap { bin =>
         if (bin.extensionIn(Set("json"))) Stream.eval(AddonMeta.fromJsonBytes(bin.data))
diff --git a/modules/addonlib/src/test/resources/minimal-addon.zip b/modules/addonlib/src/test/resources/minimal-addon.zip
new file mode 100644
index 00000000..07563f5a
Binary files /dev/null and b/modules/addonlib/src/test/resources/minimal-addon.zip differ
diff --git a/modules/addonlib/src/test/scala/docspell/addons/AddonArchiveTest.scala b/modules/addonlib/src/test/scala/docspell/addons/AddonArchiveTest.scala
index 702292d2..7cd98dcd 100644
--- a/modules/addonlib/src/test/scala/docspell/addons/AddonArchiveTest.scala
+++ b/modules/addonlib/src/test/scala/docspell/addons/AddonArchiveTest.scala
@@ -31,6 +31,54 @@ class AddonArchiveTest extends CatsEffectSuite with TestLoggingConfig with Fixtu
     } yield ()
   }
 
+  test("Read archive from zip") {
+    for {
+      aa <- AddonArchive.read[IO](dummyAddonUrl, UrlReader.defaultReader[IO], None)
+      _ = {
+        assertEquals(aa.name, "dummy-addon")
+        assertEquals(aa.version, "2.9")
+        assertEquals(aa.url, dummyAddonUrl)
+      }
+    } yield ()
+  }
+
+  tempDir.test("Read generated addon from path") { dir =>
+    AddonGenerator.successAddon("mini-addon").use { addon =>
+      for {
+        archive <- IO(AddonArchive(addon.url, "", ""))
+        path <- archive.extractTo[IO](UrlReader.defaultReader[IO], dir)
+
+        read <- AddonArchive.read[IO](addon.url, UrlReader.defaultReader[IO], path.some)
+        _ = assertEquals(addon, read)
+      } yield ()
+    }
+  }
+
+  test("Read generated addon from zip") {
+    AddonGenerator.successAddon("mini-addon").use { addon =>
+      for {
+        read <- AddonArchive.read[IO](addon.url, UrlReader.defaultReader[IO], None)
+        _ = assertEquals(addon, read)
+      } yield ()
+    }
+  }
+
+  tempDir.test("Read minimal addon from path") { dir =>
+    for {
+      archive <- IO(AddonArchive(miniAddonUrl, "", ""))
+      path <- archive.extractTo(UrlReader.defaultReader[IO], dir)
+      aa <- AddonArchive.read(miniAddonUrl, UrlReader.defaultReader[IO], path.some)
+      _ = assertEquals(aa, AddonArchive(miniAddonUrl, "minimal-addon", "0.1.0"))
+    } yield ()
+  }
+
+  test("Read minimal addon from zip") {
+    for {
+      aa <- AddonArchive.read(miniAddonUrl, UrlReader.defaultReader[IO], None)
+      _ = assertEquals(aa, AddonArchive(miniAddonUrl, "minimal-addon", "0.1.0"))
+    } yield ()
+  }
+
   test("Read archive from zip file") {
     for {
       archive <- AddonArchive.read[IO](dummyAddonUrl, UrlReader.defaultReader[IO])
diff --git a/modules/addonlib/src/test/scala/docspell/addons/Fixtures.scala b/modules/addonlib/src/test/scala/docspell/addons/Fixtures.scala
index 20abfa95..12cb24c8 100644
--- a/modules/addonlib/src/test/scala/docspell/addons/Fixtures.scala
+++ b/modules/addonlib/src/test/scala/docspell/addons/Fixtures.scala
@@ -28,6 +28,9 @@ trait Fixtures extends TestLoggingConfig { self: CatsEffectSuite =>
   val dummyAddonUrl =
     LenientUri.fromJava(getClass.getResource("/docspell-dummy-addon-master.zip"))
 
+  val miniAddonUrl =
+    LenientUri.fromJava(getClass.getResource("/minimal-addon.zip"))
+
   val dummyAddonMeta =
     AddonMeta(
       meta =
diff --git a/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala b/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala
index 6074f557..4e220a4a 100644
--- a/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala
+++ b/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala
@@ -72,7 +72,7 @@ final class AddonValidate[F[_]: Async](
         ).cast
       )
       _ <- EitherT.cond(
-        meta.options.exists(_.isUseful),
+        meta.options.forall(_.isUseful),
         (),
         InvalidAddon(
           "Addon defines no output and no networking. It can't do anything useful."
diff --git a/website/site/content/docs/addons/writing.md b/website/site/content/docs/addons/writing.md
index 9edfbb21..5e68969e 100644
--- a/website/site/content/docs/addons/writing.md
+++ b/website/site/content/docs/addons/writing.md
@@ -265,7 +265,7 @@ options:
   # docspell itself to apply any changes and the addon can run
   # completely isolated.
   #
-  # Default is false.
+  # Default is true.
   collectOutput: true
 ```