LumaSync-Site
releasev1.1.44Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.
README Snapshot
LumaSync-Site
Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.
- Site: https://lumasync.app
- App repo: https://github.com/voyvodka/LumaSync
Stack
- Astro 6 + MDX content collections (
docs,compare,legal,blog) - Pagefind for offline in-browser search; Umami for cookie-free analytics
- Cloudflare Pages (direct-upload via
wrangler) + Cloudflare DNS - IBM Plex Sans / Mono (self-hosted WOFF2, preloaded)
- No secrets bundled; the site never talks to the desktop app
Develop
Requires Node 22.12+ and Bun (pinned via packageManager in package.json). Bun is the package manager only; the build itself runs on Node.
bun install
bun run dev # http://localhost:4321
bun run lint # prettier --check
bun run check # astro check (type-check + content schema)
bun run build # astro build + pagefind index
Deploy
Every push to main triggers .github/workflows/deploy.yml, which lint- and type-checks, builds, and ships dist/ to Cloudflare Pages.
Required repo secrets for CI: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID.
License
- Code (Astro components, layouts, CI, styling) — MIT
- Content (MDX under
src/content/, docs, comparisons, blog) — CC BY 4.0
See /license for the public-facing summary and LICENSE for the full text.
Contributing
Issues and PRs welcome — see CONTRIBUTING.md for scope, local preview, and commit conventions. For app-level bugs (firmware, USB pipeline, Hue streaming), file against the LumaSync app repo instead.
Security
Report vulnerabilities through GitHub's Private Vulnerability Reporting — see SECURITY.md or /.well-known/security.txt.
Changelog
Changelog — lumasync-site
This is the changelog for the marketing/docs site at lumasync.app. The LumaSync app's own release notes live in the app repo and surface on /changelog — site versions track independently from app versions.
The site follows Semantic Versioning at its own cadence; bumping the LumaSync app submodule does not require bumping the site version.
[1.1.44] — 2026-09-21
Changed
- Bun replaces pnpm as the package manager; the site it builds is unchanged.
pnpm-lock.yamlis gone andbun.locktakes its place, generated by Bun 1.4.2 migrating the pnpm lockfile rather than re-resolving from scratch — so every installed version stayed where it was, security floors included. Bun does not readpnpm.overrides, so the floors moved to the top-leveloverrideskey;fflatestill resolves to0.7.5, which matters because an unpinnedfflateonce turned every OG card into a placeholder without failing the build. Bun is the package manager only: Astro, Pagefind and Lighthouse CI still run on Node from.nvmrc. Evidence that the output did not move: the builtdist/has the same file list as the pnpm build, 40 of 40 OG cards render, and a sampled card is byte-identical to the one in production. - The CVE gate now covers dev dependencies too.
pnpm audit --prodgated only the shipped tree;bun audithas no production-only mode, so a high-severity advisory in build tooling now fails CI as well. That is stricter, not looser. A dev-only advisory with no available fix is waived by its GHSA id with--ignore, not by dropping the gate. The licence gate keeps its production-only scope throughbun pm licenses --prod, and Dependabot now tracks thebunecosystem. Last-Modifiedreflects this release.
[1.1.43] — 2026-09-21
Fixed
- The homepage has a title longer than its own brand name.
<title>wasLumaSync— eight characters, no category term, nothing for a non-brand query to match, and short enough to be a standing invitation for Google to write its own title instead. It now readsLumaSync — Ambilight for Philips Hue, WLED and WS2812B: 54 characters, brand first so the brand query still reads cleanly, then the category term the site already uses for itself throughout, then the three sinks the app drives. "Philips Hue" earns its place on measured demand rather than on taste —hue entertainment areaandphilips hue entertainment areaare the site's highest-impression non-brand queries, and the homepage carried no text matching either. Nothing in the new title is a new claim.SEO.astro's suffix rule moved from atitle === 'LumaSync'string test to a route check: that test was really asking "is this the homepage", which is why the homepage title could never be anything but the bare brand without doubling into… · LumaSync. A substring test would have been wrong — comparison pages carry "LumaSync" in their own titles and keep the suffix. - The homepage description fits the snippet. It was 201 characters against a display limit around 155–160, so
Local-only, brand-agnostic, MIT-licensed— the positioning, and the part a reader most needs — was cut on every result. The rewritten 146-character version leads with it.Philips Hue Entertainment areasis kept in full rather than shortened: that exact phrase is a query the site already receives impressions for, and trading a keyword match for four characters is a bad trade. /sitemap.xmlanswers instead of 404ing. Nothing was broken — Astro's sitemap integration names the indexsitemap-index.xml, robots.txt points at it, and Search Console has it submitted and reading successfully. But crawlers and audit tools probe the conventional path by habit. It now forwards with a 301 frompublic/_redirectsrather than duplicating the file, so there stays one sitemap with one generator.
Changed
Last-Modifiedreflects this release.
Not changed, deliberately
- A GEO audit scores the site's AI-bot posture as three critical failures —
GPTBot,CCBotandBytespiderblocked. All three are training crawlers, and the tooling's claim that blockingGPTBotcosts ChatGPT search visibility is wrong per OpenAI's own bot documentation: that isOAI-SearchBot's job. The liverobots.txtallows every retrieval crawler —OAI-SearchBot,ChatGPT-User,PerplexityBot,Claude-User— so citation visibility is intact and the score is low by choice. Recorded here so the next audit does not read it as a regression and "fix" it.
[1.1.42] — 2026-09-21
Fixed
- Legacy aliases resolve in one edge 301 instead of two hops. All thirteen entries in Astro's
redirectsmap were taking two hops with a meta-refresh as the final step: Astro renders each entry as an HTML page carryinghttp-equiv="refresh", andtrailingSlash: 'always'308s the slashless request before that page is ever reached —/quick-start→ 308 →/quick-start/→ meta-refresh →/docs/getting-started/first-setup/. The comment above the map claimed this was prevented and had been wrong for as long as it had been there; it addressed the redirect target's trailing slash, while the second hop comes from the source's missing one. Measured on all thirteen with a hop counter that follows meta-refresh, whichcurl -Ldoes not. The rules move topublic/_redirects, where this repo already resolves/securityin a single hop, and where Cloudflare applies them "regardless of whether or not an asset matches the incoming request" — ahead of both the trailing-slash layer and the asset lookup. The stale comment describing an emission that no longer happens is gone, along with the"/led-calibration/" has no <html> elementbuild warning those generated pages produced. - The six docs group hubs have OG cards instead of a 404.
/docs/getting-started/,/docs/hue/,/docs/usb-leds/,/docs/ambilight/,/docs/advanced/and/docs/reference/each advertised anog:imagethat returned 404 — six of the forty URLs in the sitemap sharing a card that renders blank everywhere it is posted.SEO.astro'sderivedOgPath()strips the slashes off the pathname, so/docs/hue/asks for/og/docs/hue.png; the generator keyed every individual doc, every comparison page and both top-level hubs, but never the six group hubs in between, and a missing key produces a request-time 404 rather than a build error. The entries are now derived fromDOC_GROUPSso a seventh group cannot reintroduce the gap, andGROUP_LEDEmoved intosrc/lib/content.tsso the card and the page it fronts read from one source rather than two copies.dist/og/goes from 34 cards to 40, matching the sitemap exactly. X-Markdown-Tokensno longer reports0on a HEAD request.passThrough()computed the estimate fromawait response.text()unconditionally, and on a bodyless HEAD response that resolves to an empty string — so the header went out as a confident zero. That is the worst possible wrong value: an agent probes with HEAD precisely to size the fetch before making it, which is the only reason the header exists, and zero says the document is empty rather than "no estimate available". The GET path is untouched and its verified numbers do not move; only the bodyless case falls back to the declaredContent-Length, and with neither a body nor a length it now sends no estimate at all.
Changed
Last-Modifiedreflects this release. The middleware constant is bumped alongside the deploy, as the release checklist requires.
[1.1.41] — 2026-09-21
Fixed
- The site stopped claiming analytics it does not run. The privacy notice, the telemetry reference and
llms.txtall described lumasync.app as collecting aggregate page metrics through self-hosted Umami atumami.lumasync.app— down to a field-by-field list of what was recorded per page view. None of it was running. The subdomain is NXDOMAIN, the Pages production environment sets neitherPUBLIC_UMAMI_SITE_IDnorPUBLIC_UMAMI_SRC, so the layout's guard has never been true in production, and the served HTML carries no tracking script of any kind; the site's own CSP (script-src 'self',connect-src 'self') would have blocked the script from that origin regardless. All three surfaces now say the site collects nothing, name Cloudflare's edge traffic data as the only record of a visit, and describe the Umami wiring accurately — present in the source, dormant, gated on two build variables production does not set.effective:on the privacy notice is unchanged: the policy did not change, its description of the site was wrong. - Every external footer link carries
rel="noopener noreferrer", not just the tracked ones. The attribute was gated onlink.umamiTarget, so a link's reverse-tabnabbing andReferer-leak protection depended on whether someone had remembered to tag it for analytics. The condition now reads the URL itself. fflateis floored at 0.7.5, closing GHSA-px8p-9vwx-vf98. Satori pulled 0.7.3 transitively — twice — into the shipped tree, whereunzipSynccan spin forever on a malformed ZIP64 archive. Rated moderate, so it sat under the CI audit gate's high threshold and never failed a build;pnpm audit --prodnow reports no known vulnerabilities at all. The override is capped below 0.8.0 on purpose: an open-ended floor resolves to 0.8.3, which changes how@shuding/opentype.jsdecodes the bundled WOFF fonts and turns every OG image on the site into rows of tofu boxes — while the build exits 0, warns about nothing, and writes 28 valid 1200×630 PNGs. Caught by decoding the PNG pixel data and diffing it against a pre-change build.
Changed
- Dependencies moved to latest stable, with three deliberate exceptions. Astro 7.2.2 → 7.3.3,
@astrojs/mdx7.0.5 → 8.0.1,@astrojs/sitemap3.7.3 → 3.7.4, Satori 0.29.0 → 0.33.4,marked18.0.9 → 18.0.13, DOMPurify 3.4.13 → 3.4.15,isomorphic-dompurify3.22.0 → 4.3.0, Prettier 3.9.6 → 3.9.8, andpnpm/action-setup6.0.10 → 6.1.0 in both workflows. Held back: TypeScript stays on 6.x becauseastro checkrefuses to run against the 7.x native compiler, which does not yet expose the programmatic API the language server drives — upgrading turns a CI gate into a hard failure with no diagnostics.prettier-plugin-astrostays on 0.14.1 because 1.0.1 does not converge; formattingsrc/pages/index.astrofour times in a row produces four different files, soprettier --checkcan never pass against it.@astrojs/language-serveris pinned to 2.16.13 throughpnpm.overridesbecause 2.17.0 mis-parses the multi-lineset:htmlexpression inSchema.astroand reports five phantom syntax errors on a fileastro buildcompiles without complaint. - The build output was verified unchanged across the upgrade. Every emitted file is byte-identical to a pre-upgrade build except the three Pagefind core scripts, which
stamp-pagefind.mjsrewrites on each build by design, and the sitemap'slastmodvalues, which derive from source-file mtimes.
[1.1.40] — 2026-08-20
Fixed
- The docs stop describing behaviour the app does not have. Three claims were false against the shipped code. WLED boards were presented as auto-discovered over mDNS by browsing
_wled._tcp.local.in seven places, whilediscover_wled_devicesis a single-IP/json/infoprobe and the only mDNS browser the app registers is Hue's — so the WLED service name was listed on the Telemetry page as a LAN request that is never made. The config-file reference namedapp.jsonon every platform and~/.configon Linux, so the r
Releases
- v1.1.44 — Bun replaces pnpm, with an unchanged build and a stricter CVE gateOpen on GitHub
Changed
- Bun replaces pnpm as the package manager; the site it builds is unchanged.
pnpm-lock.yamlis gone andbun.locktakes its place, generated by Bun 1.4.2 migrating the pnpm lockfile rather than re-resolving from scratch — so every installed version stayed where it was, security floors included. Bun does not readpnpm.overrides, so the floors moved to the top-leveloverrideskey;fflatestill resolves to0.7.5, which matters because an unpinnedfflateonce turned every OG card into a placeholder without failing the build. Bun is the package manager only: Astro, Pagefind and Lighthouse CI still run on Node from.nvmrc. Evidence that the output did not move: the builtdist/has the same file list as the pnpm build, 40 of 40 OG cards render, and a sampled card is byte-identical to the one in production. - The CVE gate now covers dev dependencies too.
pnpm audit --prodgated only the shipped tree;bun audithas no production-only mode, so a high-severity advisory in build tooling now fails CI as well. That is stricter, not looser. A dev-only advisory with no available fix is waived by its GHSA id with--ignore, not by dropping the gate. The licence gate keeps its production-only scope throughbun pm licenses --prod, and Dependabot now tracks thebunecosystem. Last-Modifiedreflects this release.
- Bun replaces pnpm as the package manager; the site it builds is unchanged.
- v1.1.43Open on GitHub
Fixed
- The homepage has a title longer than its own brand name.
<title>wasLumaSync— eight characters, no category term, nothing for a non-brand query to match, and short enough to be a standing invitation for Google to write its own title instead. It now readsLumaSync — Ambilight for Philips Hue, WLED and WS2812B: 54 characters, brand first so the brand query still reads cleanly, then the category term the site already uses for itself throughout, then the three sinks the app drives. "Philips Hue" earns its place on measured demand rather than on taste —hue entertainment areaandphilips hue entertainment areaare the site's highest-impression non-brand queries, and the homepage carried no text matching either. Nothing in the new title is a new claim.SEO.astro's suffix rule moved from atitle === 'LumaSync'string test to a route check: that test was really asking "is this the homepage", which is why the homepage title could never be anything but the bare brand without doubling into… · LumaSync. A substring test would have been wrong — comparison pages carry "LumaSync" in their own titles and keep the suffix. - The homepage description fits the snippet. It was 201 characters against a display limit around 155–160, so
Local-only, brand-agnostic, MIT-licensed— the positioning, and the part a reader most needs — was cut on every result. The rewritten 146-character version leads with it.Philips Hue Entertainment areasis kept in full rather than shortened: that exact phrase is a query the site already receives impressions for, and trading a keyword match for four characters is a bad trade. /sitemap.xmlanswers instead of 404ing. Nothing was broken — Astro's sitemap integration names the indexsitemap-index.xml, robots.txt points at it, and Search Console has it submitted and reading successfully. But crawlers and audit tools probe the conventional path by habit. It now forwards with a 301 frompublic/_redirectsrather than duplicating the file, so there stays one sitemap with one generator.
Changed
Last-Modifiedreflects this release.
Not changed, deliberately
- A GEO audit scores the site's AI-bot posture as three critical failures —
GPTBot,CCBotandBytespiderblocked. All three are training crawlers, and the tooling's claim that blockingGPTBotcosts ChatGPT search visibility is wrong per OpenAI's own bot documentation: that isOAI-SearchBot's job. The liverobots.txtallows every retrieval crawler —OAI-SearchBot,ChatGPT-User,PerplexityBot,Claude-User— so citation visibility is intact and the score is low by choice. Recorded here so the next audit does not read it as a regression and "fix" it.
- The homepage has a title longer than its own brand name.
- v1.1.42Open on GitHub
Fixed
- Legacy aliases resolve in one edge 301 instead of two hops. All thirteen entries in Astro's
redirectsmap were taking two hops with a meta-refresh as the final step: Astro renders each entry as an HTML page carryinghttp-equiv="refresh", andtrailingSlash: 'always'308s the slashless request before that page is ever reached —/quick-start→ 308 →/quick-start/→ meta-refresh →/docs/getting-started/first-setup/. The comment above the map claimed this was prevented and had been wrong for as long as it had been there; it addressed the redirect target's trailing slash, while the second hop comes from the source's missing one. Measured on all thirteen with a hop counter that follows meta-refresh, whichcurl -Ldoes not. The rules move topublic/_redirects, where this repo already resolves/securityin a single hop, and where Cloudflare applies them "regardless of whether or not an asset matches the incoming request" — ahead of both the trailing-slash layer and the asset lookup. The stale comment describing an emission that no longer happens is gone, along with the"/led-calibration/" has no <html> elementbuild warning those generated pages produced. - The six docs group hubs have OG cards instead of a 404.
/docs/getting-started/,/docs/hue/,/docs/usb-leds/,/docs/ambilight/,/docs/advanced/and/docs/reference/each advertised anog:imagethat returned 404 — six of the forty URLs in the sitemap sharing a card that renders blank everywhere it is posted.SEO.astro'sderivedOgPath()strips the slashes off the pathname, so/docs/hue/asks for/og/docs/hue.png; the generator keyed every individual doc, every comparison page and both top-level hubs, but never the six group hubs in between, and a missing key produces a request-time 404 rather than a build error. The entries are now derived fromDOC_GROUPSso a seventh group cannot reintroduce the gap, andGROUP_LEDEmoved intosrc/lib/content.tsso the card and the page it fronts read from one source rather than two copies.dist/og/goes from 34 cards to 40, matching the sitemap exactly. X-Markdown-Tokensno longer reports0on a HEAD request.passThrough()computed the estimate fromawait response.text()unconditionally, and on a bodyless HEAD response that resolves to an empty string — so the header went out as a confident zero. That is the worst possible wrong value: an agent probes with HEAD precisely to size the fetch before making it, which is the only reason the header exists, and zero says the document is empty rather than "no estimate available". The GET path is untouched and its verified numbers do not move; only the bodyless case falls back to the declaredContent-Length, and with neither a body nor a length it now sends no estimate at all.
Changed
Last-Modifiedreflects this release. The middleware constant is bumped alongside the deploy, as the release checklist requires.
- Legacy aliases resolve in one edge 301 instead of two hops. All thirteen entries in Astro's
- v1.1.41Open on GitHub
Fixed
- The site stopped claiming analytics it does not run. The privacy notice, the telemetry reference and
llms.txtall described lumasync.app as collecting aggregate page metrics through self-hosted Umami atumami.lumasync.app— down to a field-by-field list of what was recorded per page view. None of it was running. The subdomain is NXDOMAIN, the Pages production environment sets neitherPUBLIC_UMAMI_SITE_IDnorPUBLIC_UMAMI_SRC, so the layout's guard has never been true in production, and the served HTML carries no tracking script of any kind; the site's own CSP (script-src 'self',connect-src 'self') would have blocked the script from that origin regardless. All three surfaces now say the site collects nothing, name Cloudflare's edge traffic data as the only record of a visit, and describe the Umami wiring accurately — present in the source, dormant, gated on two build variables production does not set.effective:on the privacy notice is unchanged: the policy did not change, its description of the site was wrong. - Every external footer link carries
rel="noopener noreferrer", not just the tracked ones. The attribute was gated onlink.umamiTarget, so a link's reverse-tabnabbing andReferer-leak protection depended on whether someone had remembered to tag it for analytics. The condition now reads the URL itself. fflateis floored at 0.7.5, closing GHSA-px8p-9vwx-vf98. Satori pulled 0.7.3 transitively — twice — into the shipped tree, whereunzipSynccan spin forever on a malformed ZIP64 archive. Rated moderate, so it sat under the CI audit gate's high threshold and never failed a build;pnpm audit --prodnow reports no known vulnerabilities at all. The override is capped below 0.8.0 on purpose: an open-ended floor resolves to 0.8.3, which changes how@shuding/opentype.jsdecodes the bundled WOFF fonts and turns every OG image on the site into rows of tofu boxes — while the build exits 0, warns about nothing, and writes 28 valid 1200×630 PNGs. Caught by decoding the PNG pixel data and diffing it against a pre-change build.
Changed
- Dependencies moved to latest stable, with three deliberate exceptions. Astro 7.2.2 → 7.3.3,
@astrojs/mdx7.0.5 → 8.0.1,@astrojs/sitemap3.7.3 → 3.7.4, Satori 0.29.0 → 0.33.4,marked18.0.9 → 18.0.13, DOMPurify 3.4.13 → 3.4.15,isomorphic-dompurify3.22.0 → 4.3.0, Prettier 3.9.6 → 3.9.8, andpnpm/action-setup6.0.10 → 6.1.0 in both workflows. Held back: TypeScript stays on 6.x becauseastro checkrefuses to run against the 7.x native compiler, which does not yet expose the programmatic API the language server drives — upgrading turns a CI gate into a hard failure with no diagnostics.prettier-plugin-astrostays on 0.14.1 because 1.0.1 does not converge; formattingsrc/pages/index.astrofour times in a row produces four different files, soprettier --checkcan never pass against it.@astrojs/language-serveris pinned to 2.16.13 throughpnpm.overridesbecause 2.17.0 mis-parses the multi-lineset:htmlexpression inSchema.astroand reports five phantom syntax errors on a fileastro buildcompiles without complaint. - The build output was verified unchanged across the upgrade. Every emitted file is byte-identical to a pre-upgrade build except the three Pagefind core scripts, which
stamp-pagefind.mjsrewrites on each build by design, and the sitemap'slastmodvalues, which derive from source-file mtimes.
- The site stopped claiming analytics it does not run. The privacy notice, the telemetry reference and
- v1.1.40 — honest docs published, freshness metadata refreshedOpen on GitHub
Fixed
- The docs stop describing behaviour the app does not have. Three claims were false against the shipped code. WLED boards were presented as auto-discovered over mDNS by browsing
_wled._tcp.local.in seven places, whilediscover_wled_devicesis a single-IP/json/infoprobe and the only mDNS browser the app registers is Hue's — so the WLED service name was listed on the Telemetry page as a LAN request that is never made. The config-file reference namedapp.jsonon every platform and~/.configon Linux, so the reset instructions told users to delete a file that does not exist; the real file isshell-state.jsonunder the XDG data directory, and the wrong name had spread to eleven further pages including the privacy notice. That page's field table was fictional in shape as well, documenting nestedhue.*/wled.*groups and hand-edit advice for keys absent fromShellState, two schema versions behind. And the performance page published measured-looking figures for a Wayland capture path that is neither implemented nor supported. - Privacy and telemetry now agree on where the Hue credential lives. One page had it in the JSON state file; it has been in the OS keychain since v1.5.0.
Last-Modifiedreflects this release. The middleware constant had been sitting at 10 August while the content underneath it changed, andupdated:frontmatter on the thirteen corrected pages still claimed dates as old as April — so the JSON-LDdateModifiedthat answer engines read was dating a rewritten page to before its rewrite.
Changed
sanitizeUrlin the search component returns the parser's own output rather than validating one string and handing back another. An automated report claimed ajavascript:bypass through control characters; it does not exist — the WHATWG URL parser strips tab, LF and CR before parsing, exactly as the HTML parser does, and a sweep of every C0 character against a simulation of the browser'shrefpipeline found no input the guard reads ashttp:that the DOM would execute. The shape was tightened anyway, because validate-one-value-return-another is what a real bypass would need.- The deploy contract is written down.
deploy.ymlhas fired only on a published release for some time, butCLAUDE.mdstill documented a manualwranglerpush as the deploy path, which is how this release's content fixes sat merged and unpublished for five days. Merging is now documented as not shipping, with the ad-hocworkflow_dispatchpath and a verify-by-content step alongside it. - Dependencies: Astro 7.2.0 → 7.2.2.
- The docs stop describing behaviour the app does not have. Three claims were false against the shipped code. WLED boards were presented as auto-discovered over mDNS by browsing
- v1.1.39 — honest first-launch documentationOpen on GitHub
Fixed
- The install guide now describes the first launch that actually happens. LumaSync is not notarized by Apple, so macOS blocks the app on first open and — since Sequoia removed the Control-click override — the user has to go through System Settings → Privacy & Security → Open Anyway. The guide previously jumped straight to the Screen Recording prompt, a step nobody could reach, and any user who searched for help found the Control-click advice that no longer works. Both paths are now written out, and the Windows section documents the SmartScreen "unknown publisher" prompt along with the fact that Smart App Control blocks unsigned installers outright.
- "Signed" no longer means two different things on the same page. The Windows section called the MSI signed while the only signature involved is minisign, which the updater uses to verify a download before replacing anything on disk. That is not an Authenticode signature and does not affect the SmartScreen prompt; the page now says so.
- Inline links, code spans, and emphasis no longer swallow the space before them. In
.astro, a newline between text and an inline tag is deleted rather than collapsed, so fifteen places across the landing, license, community, compare, docs and search surfaces rendered as "standardCode of Conduct", "stack),WLED", or "affiliation.Philips Hue". Verified against the built HTML across all pages rather than the source. - The
/comparelisting heading uses the sans-serif page title like every other listing page.global.cssreserves the serif display face for editorial moments — the landing hero and the individual comparison pages — and the listing was quietly using it.
Changed
- System requirements read macOS 12.3, matching the app.
tauri.conf.jsonsetsminimumSystemVersionto 12.3 while the site said 13+, telling supported users they were unsupported. Corrected in the install guide, the USB controller driver notes, and the FAQ schema that search engines read. - The Flathub package is no longer promised. It is gated on Linux Wayland capture, which does not exist yet, and Flathub review would not favour an X11-only app requesting the broadest device permission available. The landing page roadmap now lists Wayland capture via
xdg-desktop-portalin its place — the actual prerequisite, and something that can be honestly queued.
- v1.1.38 — LumaSync v1.5.4 documentation syncOpen on GitHub
Site documentation synced to LumaSync v1.5.4. The vendor submodule pin moves to v1.5.4, so
/changelog/and every version-derived string on the site now quote the new release.Added
- Hue Bridge Pro documented as a supported bridge — it serves its local API over HTTPS only, which the app's plain-HTTP CLIP v1 calls could not reach before v1.5.4.
- New Bridge Pro pairing section on the Hue pairing page, with a symptom-first counterpart in Hue troubleshooting for the "Auth error / Credentials expired" report.
- Bridge Pro named in the hardware checklist, install prerequisites, and the landing page's hardware FAQ answer.
Changed
- Pairing and troubleshooting pages record that a rejected link button no longer surfaces as an expired-credentials error.
- Telemetry outbound-call table corrected — the bridge row claimed HTTPS while the app was calling HTTP; it now reads HTTPS with HTTP fallback.
- Manual-IP guidance notes the v1.5.4 stream-readiness IPv4 guard, the Hue counterpart to the documented
WLED_INVALID_IPcheck. - Room map editor notes extended for v1.5.4 — context-menu accessible names and single-pass drag traversal.
Fixed
- Landing page "Shipped" roadmap bullets rewritten. The heading interpolates the pinned version while the bullets are hand-written, so the submodule bump would have retitled v1.5.3 content as v1.5.4.
- v1.1.37 — fast-uri override floor raised to clear GHSA-7p8r-x3mc-p8w7Open on GitHub
Security
- The
fast-urioverride no longer pins the toolchain to a vulnerable release. GHSA-7p8r-x3mc-p8w7 (host confusion via a backslash authority introducer) affectsfast-uribelow 3.1.5, and the override floor set in v1.1.35 was still^3.1.2— which the lockfile satisfied at 3.1.4. The floor moves to^3.1.5and the lockfile resolves accordingly. - The package is development-scoped only (
@astrojs/check→@astrojs/language-server→volar-service-yaml→yaml-language-server→ajv), so it never reached the shipped bundle and the--prodaudit gate stayed green throughout.pnpm audit --audit-level=highacross the full tree, dev included, is now clean too. - Dependabot's security update for
fast-urican complete again. The caret override capped resolution below 4.x, so Dependabot's attempt to move to 4.1.2 failed with "the latest possible version that can be installed is 3.1.4" and the update job errored on every run. Raising the floor within the 3.x line resolves the advisory without taking the major.
Full changelog: https://github.com/voyvodka/LumaSync-Site/compare/v1.1.36...v1.1.37
- The
- v1.1.36 — CVE override floors, Last-Modified refresh, dependency bumpsOpen on GitHub
Security
- Two high-severity CVEs in the shipped dependency tree are closed —
js-yaml(GHSA-5p4m-2wfm-xmqj, quadratic CPU consumption resolving!!omap) andnanoid(GHSA-2v37-7h3g-55p8, custom generators can loop indefinitely whensizeis zero). - Neither is a direct dependency:
js-yamlarrives via@astrojs/mdx→@astrojs/internal-helpers,nanoidvia@tailwindcss/vite→vite→postcss. The refreshed lockfile resolves both to patched releases (4.3.1 and 3.3.18). - Transitive override floors raised to match the new advisories —
js-yamlmoves from^4.3.0(itself now vulnerable) to^4.3.1, andnanoidjoinspnpm.overridesat>=3.3.17. Both floors already match what the lockfile resolves, so there is no resolution churn. - The
pnpm audit --prod --audit-level=highgate had been failing onmainand passes again.
Fixed
- The site-wide
Last-Modifiedfreshness signal is current again. The edge middleware'sLAST_MODIFIEDconstant had been left at 26 Jun 2026 across v1.1.34 and v1.1.35, so HTML and markdown responses Cloudflare doesn't already stamp were advertising two-month-old content to crawlers and AI answer engines — the exact staleness the constant exists to prevent.
Dependencies
- Minor/patch group bump across four packages —
astro7.1.5 → 7.2.0 (which also moves vite 8.1.5 → 8.2.1),marked18.0.7 → 18.0.9,dompurify3.4.12 → 3.4.13,isomorphic-dompurify3.19.0 → 3.22.0. Manifest and lockfile only, no source change.
CI
pnpm/action-setuppinned forward from v6.0.9 to v6.0.10 in both the CI and deploy workflows, commit-SHA pinned as before.
Full changelog: https://github.com/voyvodka/LumaSync-Site/compare/v1.1.35...v1.1.36
- Two high-severity CVEs in the shipped dependency tree are closed —
- v1.1.35 — transitive CVE overrides, Astro 7, dependency refreshOpen on GitHub
Security
- Four high-severity CVEs closed in the shipped dependency tree —
js-yaml(GHSA-52cp-r559-cp3m),svgo(GHSA-2p49-hgcm-8545),sharp(GHSA-f88m-g3jw-g9cj),postcss(GHSA-r28c-9q8g-f849). - All four arrive transitively through
astro,@astrojs/mdxand@tailwindcss/vite; each is pinned to its patched range viapnpm.overrides. - The
pnpm audit --prod --audit-level=highgate had been failing onmainand passes again.
Fixed
- TV sizes in the hardware checklist now use
U+2033(double prime) instead of an ambiguous straight quote — the correct character for inches. - The USB controllers page heading's opening quote no longer renders as a closing quote.
Dependencies
- Astro 6.4.8 → 7.1.5 and
@astrojs/mdx6.0.3 → 7.0.5, which also moves vite 7 → 8. - Verified against a baseline build: identical output tree (272 files, 54 HTML pages, 34 byte-identical OG images), scoped-style hashes resolve.
- Remaining deltas are the generator meta tag, regenerated
data-astro-cidhashes, and tighter minification. - TypeScript held at 6.0.3 — the 7.x native compiler drops the programmatic Language Service API
astro checkneeds (withastro/roadmap#1321). - Minor/patch group bump across 11 packages:
satori,tailwindcss,@tailwindcss/vite, three@fontsourcefamilies,marked,isomorphic-dompurify,prettier,prettier-plugin-tailwindcss,@astrojs/check.
CI
actions/setup-nodev6 → v7 in the CI and deploy workflows.
- Four high-severity CVEs closed in the shipped dependency tree —
- v1.1.34 — inline-link press states, dependency bumpsOpen on GitHub
Fixed
- Press-state feedback on inline links now actually renders. The 404 page's
.home-ctaand the/download/version pill both declared aprefers-reduced-motion-gatedtransform: scale(0.96)on:active, but both are anchors laid out as non-replaced inline boxes — where CSStransformdoes not apply. The press animation was silently dead CSS. Both now setdisplay: inline-block, so the tactile:activetreatment shipped for the version pill in v1.1.31 finally takes effect.
Accessibility
- The landing page's
.inline-ctalinks press like the site's other CTAs —display: inline-block, atransformtransition, and aprefers-reduced-motion-gatedscale(0.96)on:active, matching the primary/secondary CTAs and compare cards.
Dependencies
- Minor/patch group bumps across two batches —
tailwindcssand@tailwindcss/vite4.3.0 → 4.3.2,marked18.0.5 → 18.0.6,dompurify3.4.11 → 3.4.12,prettier3.9.1 → 3.9.5. Manifest + lockfile only; all CI gates pass unchanged.
Full changelog: https://github.com/voyvodka/LumaSync-Site/compare/v1.1.33...v1.1.34
- Press-state feedback on inline links now actually renders. The 404 page's
- v1.1.33Open on GitHub
Added
Content-UsageAI-preference signal inrobots.txt. Emitted alongside the existing CloudflareContent-Signal— the IETF AIPREF standards-track successor (Content-Usage: search=y, train-ai=n) that updates RFC 9309. Policy unchanged: indexing yes, model training no. The AIPREF vocabulary defines onlysearch/train-aiso far, so the RAG axis stays onContent-Signal.- Web Bot Auth directory placeholder.
/.well-known/http-message-signatures-directorynow serves an empty JWKS ({ "keys": [] }) asapplication/json, satisfying agent-readiness probes (RFC 9421). The site signs no outbound requests, so it publishes no keys.
CI
- Lighthouse CI pinned to
@lhci/[email protected](was@latest) for deterministic runs. Adding acategories:agentic-browsingassertion is deferred until lhci bundles Lighthouse ≥ 13.3.0.
- v1.1.32Open on GitHub
Security
- Pagefind stamp script no longer has a check-then-act file race. The build-time stamper probed each core file with
existsSync()and then appended to it by path — a time-of-check/time-of-use window (CWE-367) where the file referenced by the name could change between the two operations. It now opens each file once with anr+descriptor (fails closed withENOENTwhen absent, preserving the stamp-only-if-present behaviour) and appends through that descriptor, so the check and the write target the same handle. CodeQLjs/file-system-racealert resolved.
- Pagefind stamp script no longer has a check-then-act file race. The build-time stamper probed each core file with
- v1.1.31Open on GitHub
Security
- Pagefind search results now scheme-validate their link
href— asanitizeUrl()guard parses each result URL with the nativeURLconstructor and allow-lists onlyhttp:/https:(falling back to#) before HTML-escaping, blockingjavascript:/data:URIs from a poisoned index. Defense-in-depth.
Accessibility
- The
/download/version pill now reads as the link it is: color-inverting:hover, a 2px--focus-ring:focus-visibleoutline, aprefers-reduced-motion-gatedscale(0.96):activepress, and a descriptivetitle.
Dependencies
- Dev-only
prettierbump 3.8.4 → 3.9.1 (minor/patch group) — manifest + lockfile only, all gates pass unchanged.
- Pagefind search results now scheme-validate their link
- v1.1.30Open on GitHub
Fixed
- Cmd+K search now actually loads its WebAssembly — Pagefind core JS is cache-busted per deploy. The v1.1.28 CSP fix (
wasm-unsafe-eval) was correct at the origin, but Cloudflare had edge-cached/pagefind/pagefind-worker.jswith the old CSP header. Because that file's bytes never change between deploys, conditional revalidation kept returning304and serving the stale header — a "Purge Everything" was re-revalidated straight back to it, so search stayed broken. The build now appends a build-unique stamp to Pagefind's non-fingerprinted core JS (pagefind.js,pagefind-worker.js,pagefind-ui.js) so their ETag changes each deploy, forcing a full200that ships the current CSP. The search worker can compile its WASM module again.
- Cmd+K search now actually loads its WebAssembly — Pagefind core JS is cache-busted per deploy. The v1.1.28 CSP fix (
- v1.1.29Open on GitHub
Changed
- Landing roadmap "Shipped" column scoped to the current release. It had been accumulating the full v1.5.0–v1.5.3 feature list (13 bullets), dwarfing the "Next" and "Never" columns and stretching the section well past a screen. It now lists only the latest release's highlights (v1.5.3) — matching the column's version header — so the three columns read at comparable length, with the full history one click away under "Full changelog →".
- v1.1.28Open on GitHub
Fixed
- Cmd+K search restored — the Content-Security-Policy
script-srcwas missing'wasm-unsafe-eval', so Pagefind could not compile its WebAssembly module and search silently failed with a CSP console error on every page. Added the narrow'wasm-unsafe-eval'source (WASM compilation only, not generaleval).
Build
- Pinned the
vendor/lumasyncsubmodule to the v1.5.3 release commit so the deployed build resolves the v1.5.3 version surfaces (roadmap header, JSON-LDsoftwareVersion, compare cells) and renders the v1.5.3 release notes on/changelog/— completing the v1.1.27 app sync.
- Cmd+K search restored — the Content-Security-Policy
- v1.1.27Open on GitHub
Marketing/docs site sync to the LumaSync app v1.5.3 release.
Documentation
- macOS launch-crash fix (1.5.2 builds crashed on Macs without Xcode) documented across the install guide, error-handling reference, download page, and USB troubleshooting.
- Hue troubleshooting expanded: self-clearing active-streamer banner, fixed "Reconnecting" stall, and the
HUE_STOP_TIMEOUT_PARTIAL"Retry Stop" hint. - Added v1.5.3 shutdown-hardening continuation, transient-notice timer-leak fix, visibility-aware Hue polling, output hot-path optimizations, room-map template-selector a11y, and single-HTTP-client gamut fetching. Bumped
updated:frontmatter on every touched doc.
Content
- Landing roadmap "Shipped" column now leads with the v1.5.3 highlights; the macOS platform card notes the 1.5.2 → 1.5.3 crash fix.
Accessibility
- Focus-visible rings on the landing page's inline CTA, feature-grid, and trust-section links (incorporates PR #103).
SEO
- Bumped the site-wide
Last-Modifiedfreshness signal.
- v1.1.26Open on GitHub
Security
- Finished the external-link
noopenerrollout on the render-timehreflinks the 1.1.25 pass missed: 404 recovery links,/community/FAQ inline links and forum cards, and/download/per-OS asset cards.rel="noopener noreferrer"is guarded the same way as eachhref, so internal and disabled links stay untouched.
Accessibility
- Skip-to-content link now draws a visible
:focus-visiblering (2px--focus-ring) plus a reduced-motion-gatedscale(0.96)press state once it slides into view. - 404 page recovery cards and the
← Homelink gained the standard:focus-visibleoutline so keyboard users can see which target holds focus.
Dependencies
- Minor/patch group:
astro6.4.7 → 6.4.8,dompurify3.4.8 → 3.4.11,isomorphic-dompurify3.16.0 → 3.18.0 (manifest + lockfile only).
Build
- GitHub Actions:
actions/checkout6 → 7 across CI/CodeQL/deploy, andpnpm/action-setup6.0.8 → 6.0.9 (SHA-pinned). CI-only.
- Finished the external-link
- v1.1.25Open on GitHub
Security
- esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): pnpm override pins
esbuildto>=0.28.1; thepnpm audit --prod --audit-level=highCI gate is green again. - yaml advisory cleared (GHSA-48c2-rrv3-qjmp): pnpm override forces
yamlto>=2.8.3(was 2.7.1 via@astrojs/check).pnpm auditnow reports no known vulnerabilities at any severity, prod or dev. - External-link hardening completed site-wide:
rel="noopener noreferrer"extended to the remaining outbound links on the home, download, and community pages, finishing the rollout begun in 1.1.24.
Accessibility
- Disabled community forum links now show their native
titletooltip on hover (.forums a.disabled:pointer-events: none→cursor: default), with no interactivity regression.
Dependencies
astro6.4.4 → 6.4.7 (manifest + lockfile), plus a full lockfile refresh for the latest in-range patches across the tree.
- esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): pnpm override pins