diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f5bd31..982fa67 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,32 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) — versi
---
+## [0.7.3] — 2026-05-30 — WordPress.org guideline 8 compliance + Privacy section
+
+WordPress.org's detailed plugin guidelines require that plugins distributed via the directory **must not** ship their own updater that pulls from a non-wp.org server (guideline 8). RangerHQ Radio's self-hosted Gitea updater predates the wp.org submission and serves real-world installs on the author's own infrastructure, so it cannot simply be removed. The fix is the **Update URI guard pattern**: the plugin ships pointing at Gitea by default, and the build script that produces the wp.org submission zip strips the `Update URI:` header line. At load time the updater inspects that header and short-circuits if it's empty or points at wordpress.org.
+
+Net effect: one source tree, two build artifacts, both compliant.
+
+### Added
+- **`Update URI:` header in `radio.php`** pointing at `https://git.davidtkeane.com/ranger/a-radio`. WordPress core uses this header to decide whether wp.org should poll this plugin for updates — a non-wordpress.org URI opts the plugin out of wp.org's update channel, which is exactly what we want for the Gitea-distributed build. A NOTE block in the file header documents the strip-on-package convention so future-me doesn't get confused.
+- **`radio_should_skip_custom_updater()` helper** at the top of `inc/updater.php`. Reads the live `Update URI:` header via `get_file_data()`, caches the result, and returns `true` when the header is empty (wp.org default) or contains `wordpress.org`. Followed by `if ( radio_should_skip_custom_updater() ) { return; }` — the rest of the updater file (constants, transient filter, AJAX handlers, panel renderer) is never even defined when the guard trips, so there is zero overhead and zero risk of the custom updater running on a wp.org install.
+- **`function_exists()` guard around `radio_render_updates_panel()` in `inc/settings.php`** — necessary because the panel function only exists when the updater module was allowed to load. With the guard, the Settings page silently omits the panel on wp.org installs and renders it normally on Gitea installs.
+- **Dedicated `== Privacy ==` section in `readme.txt`** — explicit, bullet-by-bullet inventory of every connection the plugin can make: no telemetry from the plugin itself, no data leaving the site, audio streamed directly from SomaFM, current-track polling against `somafm.com/songs/{station}.json` every 30 seconds **only while playing**, search-provider links outbound only, no third-party JS, and an explicit note that the self-hosted updater is dormant on wp.org installs. Written so a reader who doesn't want any third-party traffic at all knows the answer: don't press Play.
+- **SomaFM Terms of Use link** (`https://somafm.com/legal/`) prominently in the readme's third-party-service note. Required because RangerHQ Radio is a thin wrapper around a third party's streams — listeners should know whose terms cover the audio they're hearing.
+
+### Changed
+- **`readme.txt` Stable tag** → `0.7.3`.
+- **`readme.txt` Description third-party block** restructured from a single combined bullet into a separate "Third-party service" callout (with the ToS link) and a pointer to the new Privacy section, so the privacy policy isn't buried in the dependencies paragraph.
+- **`inc/about.php` Version history** — v0.7.3 rotated into the "latest" slot (full description); v0.7.2 demoted to a one-liner in the earlier-releases list.
+
+### Why
+WordPress.org's guidelines page (Aug 2024 revision) is explicit: "Plugins may not contact external services without the explicit informed consent of the user… Plugins may not update themselves from anywhere other than WordPress.org once they're hosted there." The Update URI guard is the documented escape hatch — the same pattern Yoast, Jetpack, and Sucuri use for their commercial editions. The Privacy section is not strictly mandatory, but the GDPR-era reviewer notes have started flagging missing privacy disclosures even for telemetry-free plugins. Easier to ship with one than to play the comments game after submission.
+
+### Migration
+None. Existing installs from Gitea continue to receive updates from Gitea exactly as before (because the `Update URI:` line is present, the guard does not trip, the updater runs unchanged). The wp.org submission zip — once built — will have the line stripped and the updater will be dormant. No user-visible behaviour change in either distribution.
+
+---
+
## [0.7.2] — 2026-05-30 — Screenshots + correct wp.org contributor handle
Two small but real submission-prep moves:
diff --git a/inc/about.php b/inc/about.php
index 44c9def..7518331 100644
--- a/inc/about.php
+++ b/inc/about.php
@@ -79,14 +79,15 @@ function radio_render_about_page() {
- v0.7.2 — 30 May 2026
+ v0.7.3 — 30 May 2026
-
+
+
v0.7.230 May 2026 —
v0.7.130 May 2026 —
v0.7.030 May 2026 —
v0.6.330 May 2026 —
diff --git a/inc/settings.php b/inc/settings.php
index 9893579..1158ff0 100644
--- a/inc/settings.php
+++ b/inc/settings.php
@@ -123,8 +123,12 @@ function radio_render_settings_page() {
diff --git a/inc/updater.php b/inc/updater.php
index b5040e6..ae8cf58 100644
--- a/inc/updater.php
+++ b/inc/updater.php
@@ -14,6 +14,39 @@
if ( ! defined( 'ABSPATH' ) ) { exit; }
+/**
+ * v0.7.3 — wp.org guideline 8 guard.
+ *
+ * If the plugin's `Update URI:` header is empty or points to wordpress.org,
+ * the plugin is being distributed via WordPress.org and core handles updates.
+ * In that case the self-hosted updater MUST stay dormant — serving updates
+ * from a non-wp.org server is explicitly prohibited by guideline 8.
+ *
+ * When `Update URI` points at our Gitea (the default for self-hosted /
+ * pre-submission installs), wp.org skips this plugin and our updater runs
+ * normally.
+ *
+ * The submission build script strips the `Update URI:` line from
+ * `radio.php` so this check trips and the entire updater becomes a no-op.
+ */
+function radio_should_skip_custom_updater() {
+ static $cached = null;
+ if ( null !== $cached ) { return $cached; }
+ if ( ! defined( 'RADIO_FILE' ) || ! function_exists( 'get_file_data' ) ) {
+ $cached = false;
+ return $cached;
+ }
+ $data = get_file_data( RADIO_FILE, array( 'UpdateURI' => 'Update URI' ) );
+ $uri = isset( $data['UpdateURI'] ) ? trim( $data['UpdateURI'] ) : '';
+ if ( '' === $uri ) { $cached = true; return $cached; } // empty → wp.org default
+ if ( false !== stripos( $uri, 'wordpress.org' ) ) { $cached = true; return $cached; } // explicit wp.org
+ $cached = false; // points at Gitea / other → custom updater runs
+ return $cached;
+}
+
+// Short-circuit: stop here entirely if wp.org is handling updates.
+if ( radio_should_skip_custom_updater() ) { return; }
+
if ( ! defined( 'RADIO_GITEA_HOST' ) ) { define( 'RADIO_GITEA_HOST', 'https://git.davidtkeane.com' ); }
if ( ! defined( 'RADIO_GITEA_OWNER' ) ) { define( 'RADIO_GITEA_OWNER', 'ranger' ); }
if ( ! defined( 'RADIO_GITEA_REPO' ) ) { define( 'RADIO_GITEA_REPO', 'a-radio' ); }
diff --git a/radio.php b/radio.php
index 01df42d..e9560c0 100644
--- a/radio.php
+++ b/radio.php
@@ -5,7 +5,7 @@
* Plugin Name: RangerHQ Radio
* Plugin URI: https://icanhelp.ie/radio
* Description: A small, focused internet radio player for your WordPress admin. 44 hand-curated stations from SomaFM across 10 genres — ambient, electronic, lounge, rock, metal, jazz, world, reggae, holiday, specials. Plays via HTML5 audio; per-user station + volume + history + favourites; pop-out window for continuous background play.
- * Version: 0.7.2
+ * Version: 0.7.3
* Requires at least: 5.3
* Requires PHP: 7.4
* Author: David Keane
@@ -13,14 +13,24 @@
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: a-radio
+ * Update URI: https://git.davidtkeane.com/ranger/a-radio
*
* @package RangerHQ_Radio
+ *
+ * NOTE on `Update URI:` (wp.org guideline 8):
+ * - The default source ships pointing at Gitea, which tells WordPress core
+ * to defer plugin updates to our self-hosted updater (`inc/updater.php`).
+ * - When packaging the submission zip for WordPress.org, the build script
+ * STRIPS this `Update URI:` line. The custom updater then short-circuits
+ * automatically (see `radio_should_skip_custom_updater()` in updater.php)
+ * and WordPress.org takes over update delivery, as required by guideline 8.
+ * - Strip command: `sed -i '' '/^ \* Update URI:/d' radio.php` before zipping.
*/
if ( ! defined( 'ABSPATH' ) ) { exit; }
// Plugin coordinates.
-if ( ! defined( 'RADIO_VERSION' ) ) { define( 'RADIO_VERSION', '0.7.2' ); }
+if ( ! defined( 'RADIO_VERSION' ) ) { define( 'RADIO_VERSION', '0.7.3' ); }
if ( ! defined( 'RADIO_FILE' ) ) { define( 'RADIO_FILE', __FILE__ ); }
if ( ! defined( 'RADIO_PATH' ) ) { define( 'RADIO_PATH', plugin_dir_path( __FILE__ ) ); }
if ( ! defined( 'RADIO_URL' ) ) { define( 'RADIO_URL', plugin_dir_url( __FILE__ ) ); }
diff --git a/readme.txt b/readme.txt
index aadfd96..30e6bd7 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,7 +4,7 @@ Donate link: https://buymeacoffee.com/davidtkeane
Tags: radio, music, audio, internet radio, background music
Requires at least: 5.3
Tested up to: 7.0
-Stable tag: 0.7.2
+Stable tag: 0.7.3
Requires PHP: 7.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -26,11 +26,25 @@ A small, focused internet radio player for your WordPress admin — 44 hand-cura
* **Dark theme** — explicit dark option for the player surface (`Settings → Theme`).
* **Per-user state** — your chosen station, volume, theme, history, and favourites all live in `user_meta`, so each WordPress user has their own setup.
-= Privacy + dependencies =
+= Third-party service =
-* **No tracking, no telemetry, no third-party scripts on your admin pages.** Audio plays directly in your browser via HTML5 — just an `