diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a6d28d..fb2857b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Unreleased — targeting v0.1.0
+- Added a PNG export of the existing community artwork and switched template/profile
+ icon URLs to PNG for Docker Manager compatibility. Original SVG preserved.
- Changed the default host WebUI/API port to `6969` for CPU, CUDA 12 and CUDA 13
installations. Internal port `8080` and WebUI port resolution remain unchanged;
existing installations retain their configured host port.
diff --git a/assets/README.md b/assets/README.md
index b6a296f..22a9a1e 100644
--- a/assets/README.md
+++ b/assets/README.md
@@ -4,6 +4,18 @@
the white waveform on a blue/violet tile. Only a final newline was added; artwork
and original SVG metadata are preserved. It requires no external font.
+`icon.png` is a 256 × 256 raster export of that same artwork for the Community
+Apps profile and Docker template. Docker Manager uses PNG-oriented icon caching
+and rendering; the SVG remains the unchanged source asset. From the repository
+root, export with:
+
+```sh
+rsvg-convert --width 256 --height 256 --output assets/icon.png assets/icon.svg
+```
+
+The raster export does not carry the SVG's embedded provenance metadata; that
+metadata remains in the original SVG. No artwork was regenerated or redesigned.
+
On 2026-09-06, the project owner (`lozenge0`) confirmed that the artwork was
generated from scratch using their own prompts with Claude/AI, without supplied
third-party images or assets. This records the owner's provenance statement;
@@ -13,7 +25,7 @@ it is not an independent originality, copyright or trademark clearance.
On 2026-09-12, the project owner (`lozenge0`) approved applying
[CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) to
-`icon.svg`, to the extent they hold copyright and related rights in it.
+`icon.svg` and its `icon.png` export, to the extent they hold copyright and related rights in them.
The [CC0 legal code](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en)
governs this dedication, including its waiver, fallback licence and disclaimers.
SPDX identifier: `CC0-1.0`.
@@ -21,7 +33,7 @@ SPDX identifier: `CC0-1.0`.
This does not assert that AI-generated artwork necessarily has copyright
protection, clear third-party rights, or grant trademark rights. This is community
integration artwork, not official audio.cpp branding or endorsement. The root MIT
-licence covers integration code/docs; `icon.svg` is separately covered by CC0.
+licence covers integration code/docs; both icon formats are separately covered by CC0.
## Maintainer presentation checks
diff --git a/assets/icon.png b/assets/icon.png
new file mode 100644
index 0000000..33d35c7
Binary files /dev/null and b/assets/icon.png differ
diff --git a/ca_profile.xml b/ca_profile.xml
index b31082d..1e6b5f8 100644
--- a/ca_profile.xml
+++ b/ca_profile.xml
@@ -1,6 +1,6 @@
audio.cpp for Unraid: community-maintained templates using unmodified upstream Docker images. This project maintains the Unraid integration, not audio.cpp itself, and is not an official upstream endorsement. Installation support belongs in this repository's issues. Beta integration: see the README for tested hardware and remaining deployment checks.
- https://raw.githubusercontent.com/lozenge0/audio-cpp-unraid/main/assets/icon.svg
+ https://raw.githubusercontent.com/lozenge0/audio-cpp-unraid/main/assets/icon.png
https://github.com/lozenge0/audio-cpp-unraid
diff --git a/docs/RELEASE-REVIEW.md b/docs/RELEASE-REVIEW.md
index cd5f7d1..bbf40fe 100644
--- a/docs/RELEASE-REVIEW.md
+++ b/docs/RELEASE-REVIEW.md
@@ -104,6 +104,7 @@ LICENSE
README.md
SECURITY.md
assets/README.md
+assets/icon.png
assets/icon.svg
ca_profile.xml
docs/CONFIGURATION.md
diff --git a/templates/audio-cpp.xml b/templates/audio-cpp.xml
index da44279..5bfcbbe 100644
--- a/templates/audio-cpp.xml
+++ b/templates/audio-cpp.xml
@@ -9,7 +9,7 @@
true
AI Tools:
http://[IP]:[PORT:8080]/
- https://raw.githubusercontent.com/lozenge0/audio-cpp-unraid/main/assets/icon.svg
+ https://raw.githubusercontent.com/lozenge0/audio-cpp-unraid/main/assets/icon.png
https://github.com/0xShug0/audio.cpp
https://github.com/lozenge0/audio-cpp-unraid/issues
https://raw.githubusercontent.com/lozenge0/audio-cpp-unraid/main/templates/audio-cpp.xml
diff --git a/tests/test_publication.py b/tests/test_publication.py
index b7a514a..3a9812b 100644
--- a/tests/test_publication.py
+++ b/tests/test_publication.py
@@ -8,7 +8,7 @@
'.github/dependabot.yml', '.github/workflows/validate.yml',
'.gitignore', 'CHANGELOG.md', 'LICENSE', 'README.md',
'CONTRIBUTING.md', 'SECURITY.md',
- 'assets/README.md', 'assets/icon.svg', 'ca_profile.xml',
+ 'assets/README.md', 'assets/icon.svg', 'assets/icon.png', 'ca_profile.xml',
'docs/CONFIGURATION.md', 'docs/MAINTAINER.md',
'docs/CPU-RETEST-20260912.md', 'docs/FIRST-RUN-FINDINGS.md',
'docs/PLAN.md', 'docs/PUBLISHING.md', 'docs/RECREATION-TEST.md', 'docs/RELEASE-REVIEW.md',
@@ -98,7 +98,8 @@ def test_no_selected_private_data_patterns_in_public_content(self):
r'\bgithub_pat_[A-Za-z0-9_]{30,}\b',
)
for name in sorted(EXPECTED):
- if name.startswith('tests/'):
+ if name.startswith('tests/') or name == 'assets/icon.png':
+ # PNG structure/chunk allowlist is checked by test_template.
continue
text = (ROOT / name).read_text()
for pattern in patterns:
diff --git a/tests/test_template.py b/tests/test_template.py
index f5d981a..3e069c4 100644
--- a/tests/test_template.py
+++ b/tests/test_template.py
@@ -129,6 +129,32 @@ def test_profile(self):
self.assertTrue(profile.findtext("Profile"))
self.assertEqual(profile.findtext("Icon"), self.root.findtext("Icon"))
+ def test_deployment_icon_is_256_pixel_png(self):
+ import struct
+ import zlib
+ data = (ROOT / "assets/icon.png").read_bytes()
+ self.assertEqual(data[:8], b"\x89PNG\r\n\x1a\n")
+ offset = 8
+ chunks = []
+ while offset < len(data):
+ size = struct.unpack(">I", data[offset:offset + 4])[0]
+ kind = data[offset + 4:offset + 8]
+ payload = data[offset + 8:offset + 8 + size]
+ crc = struct.unpack(">I", data[offset + 8 + size:offset + 12 + size])[0]
+ self.assertEqual(crc, zlib.crc32(kind + payload))
+ self.assertIn(kind, (b"IHDR", b"bKGD", b"IDAT", b"IEND"))
+ chunks.append(kind)
+ if kind == b"IHDR":
+ self.assertEqual(struct.unpack(">II", payload[:8]), (256, 256))
+ offset += 12 + size
+ self.assertEqual(offset, len(data))
+ self.assertEqual(chunks[0], b"IHDR")
+ self.assertIn(b"IDAT", chunks)
+ self.assertEqual(chunks[-1], b"IEND")
+ for tag, root in variants(self.root):
+ with self.subTest(variant=tag):
+ self.assertTrue(root.findtext("Icon").endswith("/assets/icon.png"))
+
def test_icon_is_self_contained_svg(self):
icon = ET.parse(ROOT / "assets/icon.svg").getroot()
self.assertEqual(icon.tag, "{http://www.w3.org/2000/svg}svg")
@@ -171,7 +197,7 @@ def test_selected_publication_destinations(self):
self.assertEqual(self.root.findtext("Support"), base + "/issues")
self.assertEqual(self.root.findtext("TemplateURL"), raw + "templates/audio-cpp.xml")
self.assertEqual(self.root.findtext("ReadMe"), raw + "README.md")
- self.assertEqual(self.root.findtext("Icon"), raw + "assets/icon.svg")
+ self.assertEqual(self.root.findtext("Icon"), raw + "assets/icon.png")
profile = ET.parse(ROOT / "ca_profile.xml").getroot()
self.assertEqual(profile.findtext("WebPage"), base)
for path in (ROOT / "templates/audio-cpp.xml", ROOT / "ca_profile.xml"):