{
  "name": "Manifest Updater",
  "slug": "manifest-updater",
  "version": "1.1.0",
  "download_url": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/manifest-updater/manifest-updater-1.1.0.zip",
  "homepage": "https://gitea.mulligan.casa/mmulligan/manifest-updater",
  "requires": "6.1",
  "tested": "7.0",
  "requires_php": "8.1",
  "last_updated": "2026-06-23 18:24:11",
  "icons": {
    "1x": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/manifest-updater/icon-128x128.png",
    "2x": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/manifest-updater/icon-256x256.png"
  },
  "author": "Mike Mulligan",
  "author_homepage": "https://gitea.mulligan.casa/mmulligan",
  "tags": [
    "updates",
    "plugin updates",
    "theme updates",
    "self-hosted",
    "update uri"
  ],
  "sections": {
    "description": "<p>A tiny, universal, self-hosted updater for <strong>plugins and themes</strong>. Network-activate<br />\nit once; any project that opts in via its <code>Update URI</code> header gets WordPress-native<br />\nupdate notifications, \"View details\", and one-click / auto updates — <strong>network-wide</strong>,<br />\nwith <strong>no per-project updater code</strong> and <strong>no tokens</strong>.</p>\n<p>It's \"Git Updater meets PUC,\" but thin: WordPress already does the hard part<br />\n(scanning every installed project's header, routing by host, building the update<br />\ntransient, rendering the row, running the install). This plugin only supplies the<br />\npiece core can't — reading <em>your</em> JSON manifest and returning the update.</p>\n<h2>Opt in — the <code>#</code> fragment is the trigger</h2>\n<p>Append a fragment to the project's <code>Update URI</code> header. <strong>The fragment is the only<br />\ntrigger</strong> (we never sniff paths), so this can't collide with a project that updates<br />\nsome other way:</p>\n<pre><code># A plugin, JSON manifest:\nUpdate URI: https://your-host/path/to/my-plugin.json#manifest-updater.json\n\n# A theme, JSON manifest (in style.css):\nUpdate URI: https://your-host/path/to/my-theme.json#manifest-updater.json\n</code></pre>\n<ul>\n<li>The part <strong>before</strong> <code>#</code> is the source (here, the manifest URL).</li>\n<li><code>#manifest-updater.json</code> opts the project in <strong>and</strong> selects JSON-manifest mode.</li>\n<li><code>#manifest-updater.git</code> is reserved for a future git-repo mode (recognised now,<br />\n  not yet implemented — such a project is simply not updated until it lands).</li>\n</ul>\n<p>The <code>Update URI</code> header also earns the project automatic protection from being<br />\noverwritten by a same-named wordpress.org plugin/theme — for free, from core.</p>\n<p>Then <strong>network-activate Manifest Updater</strong> (it is <code>Network: true</code>). That's it.</p>\n<h2>Why it's network-wide (and lets everything else be site-active)</h2>\n<p><code>get_plugins()</code> / <code>wp_get_themes()</code> return every installed project regardless of<br />\nactivation, and the <code>update_plugins</code> / <code>update_themes</code> transients are network-level<br />\non multisite. So updates surface and install network-wide while the projects<br />\nthemselves can be <strong>site-active, network-active, or inactive</strong>. A project no longer<br />\nhas to be network-active just to receive updates — only this one does.</p>\n<h2>Manifest format</h2>\n<p>The PUC (\"Plugin Update Checker\") metadata format — works for plugins and themes.<br />\nOnly <code>version</code> and <code>download_url</code> are required; everything else enriches the row<br />\nand the details modal. See <strong><code>example-manifest.json</code></strong> for every supported field:</p>\n<p><code>name</code>, <code>slug</code>, <code>version</code>, <code>download_url</code>, <code>homepage</code>, <code>requires</code>, <code>tested</code>,<br />\n<code>requires_php</code>, <code>requires_plugins</code>, <code>last_updated</code>, <code>upgrade_notice</code>, <code>author</code>,<br />\n<code>author_homepage</code>, <code>sections</code> (<code>description</code>/<code>installation</code>/<code>changelog</code>/<code>faq</code>/any<br />\ncustom tab), <code>icons</code>, <code>banners</code>, <code>banners_rtl</code>, <code>screenshot_url</code> (themes),<br />\n<code>translations</code>, and the optional wp.org-style stats <code>rating</code>/<code>num_ratings</code>/<br />\n<code>downloaded</code>/<code>active_installs</code>.</p>\n<h2>Notes</h2>\n<ul>\n<li>Manifests are cached in a network site-transient for 12h; the Updates screen's<br />\n  \"Check again\" button busts the cache.</li>\n<li>The plugin self-updates the same way (its own <code>Update URI</code> carries the fragment).</li>\n<li>Requires WordPress 6.1+ (the plugin <strong>and</strong> theme <code>Update URI</code> headers) and PHP 8.1+.</li>\n</ul>",
    "changelog": "<p>All notable changes to Manifest Updater are documented here. The format follows<br />\n<a href=\"https://keepachangelog.com/\">Keep a Changelog</a> and the project adheres to<br />\nsemantic versioning.</p>\n<h2>[1.1.0] - 2026-06-23</h2>\n<h3>Added</h3>\n<ul>\n<li><strong>Same-origin image proxy</strong> for plugin/theme icons + banners. The browser was<br />\n  failing to load icons hosted on a self-hosted forge (Chrome Private Network<br />\n  Access on private IPs, NAT hairpin, CORS, service workers). MU now rewrites the<br />\n  update-response icon/banner URLs to a same-origin <code>admin-ajax</code> endpoint that<br />\n  fetches the image server-side and re-serves it from this site. SSRF-contained<br />\n  (same host as the manifest, no redirect-following), raster-only (SVG excluded),<br />\n  content-type allow-listed, cached, and size-capped.</li>\n<li><code>tags</code> passed through to the \"View details\" modal sidebar.</li>\n<li>Self-hosting through the shared update channel, plus a reusable release<br />\n  pipeline under <code>.ci/</code> that MU mirrors to the channel on release.</li>\n</ul>\n<h2>[1.0.0]</h2>\n<h3>Added</h3>\n<ul>\n<li>Network-wide self-hosted updates for plugins <strong>and</strong> themes via WordPress's<br />\n  native <code>Update URI</code> header, opted in with a <code>#manifest-updater.json</code> fragment.</li>\n<li>Full PUC manifest field support, 12h manifest cache, and self-update.</li>\n</ul>"
  }
}
