diff --git a/src/bggpipe/static/apple-touch-icon.png b/src/bggpipe/static/apple-touch-icon.png
new file mode 100644
index 0000000..cdbfecb
Binary files /dev/null and b/src/bggpipe/static/apple-touch-icon.png differ
diff --git a/src/bggpipe/templates/shell.html b/src/bggpipe/templates/shell.html
index 22fde12..0d9385c 100644
--- a/src/bggpipe/templates/shell.html
+++ b/src/bggpipe/templates/shell.html
@@ -5,6 +5,8 @@
+
+
diff --git a/src/bggpipe/webreview.py b/src/bggpipe/webreview.py
index 81e821f..41ef90b 100644
--- a/src/bggpipe/webreview.py
+++ b/src/bggpipe/webreview.py
@@ -1089,8 +1089,19 @@ def create_app(
"logo.jpg": "image/jpeg",
"logo-full.jpg": "image/jpeg",
"favicon.png": "image/png",
+ "apple-touch-icon.png": "image/png",
}
+ @app.get("/apple-touch-icon.png")
+ @app.get("/apple-touch-icon-precomposed.png")
+ def apple_touch_icon() -> Response:
+ # iOS probes these root paths (cookie-less) when saving to the
+ # home screen; the guard already treats them as public
+ data = (
+ resources.files("bggpipe") / "static" / "apple-touch-icon.png"
+ ).read_bytes()
+ return Response(content=data, media_type="image/png")
+
@app.get("/static/{name}")
def static_asset(name: str) -> Response:
media_type = _STATIC.get(name) # allowlist: no traversal possible
diff --git a/tests/test_webreview.py b/tests/test_webreview.py
index 774c753..70f3f7b 100644
--- a/tests/test_webreview.py
+++ b/tests/test_webreview.py
@@ -1048,3 +1048,14 @@ def test_lan_403_is_html_for_navigations(tmp_path):
res = phone.get("/titles", headers={"accept": "text/html,application/xhtml+xml"})
assert res.status_code == 403
assert "access key needed" in res.text # a person sees prose, not JSON
+
+
+def test_home_screen_icon_is_served_and_public(tmp_path):
+ cfg = make_cfg(tmp_path)
+ app = create_app(cfg, client=unauthorized_client(tmp_path), lan_token="sekret")
+ phone = TestClient(app, base_url="http://192.168.1.99:8377")
+ # iOS probes cookie-less; both the root probe and the linked path work
+ assert phone.get("/apple-touch-icon.png").status_code == 200
+ assert phone.get("/static/apple-touch-icon.png").status_code == 200
+ page = phone.get("/?k=sekret", follow_redirects=True)
+ assert 'rel="apple-touch-icon"' in page.text