LumaSync-Site

releasev1.1.44

Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.

0stars
0forks
0watchers
0open issues
owner: voyvodkastatus: CORElanguage: MDXbranch: mainlicense: Otherupdated: last push:
adalightambilightastrobias-lightingled-stripmarketing-siteopensourcephilips-huetauriws2812b

README Snapshot

LumaSync-Site

Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.

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.yaml is gone and bun.lock takes 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 read pnpm.overrides, so the floors moved to the top-level overrides key; fflate still resolves to 0.7.5, which matters because an unpinned fflate once 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 built dist/ 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 --prod gated only the shipped tree; bun audit has 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 through bun pm licenses --prod, and Dependabot now tracks the bun ecosystem.
  • Last-Modified reflects this release.

[1.1.43] — 2026-09-21

Fixed

  • The homepage has a title longer than its own brand name. <title> was LumaSync — 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 reads LumaSync — 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 area and philips hue entertainment area are 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 a title === '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 areas is 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.xml answers instead of 404ing. Nothing was broken — Astro's sitemap integration names the index sitemap-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 from public/_redirects rather than duplicating the file, so there stays one sitemap with one generator.

Changed

  • Last-Modified reflects this release.

Not changed, deliberately

  • A GEO audit scores the site's AI-bot posture as three critical failures — GPTBot, CCBot and Bytespider blocked. All three are training crawlers, and the tooling's claim that blocking GPTBot costs ChatGPT search visibility is wrong per OpenAI's own bot documentation: that is OAI-SearchBot's job. The live robots.txt allows 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 redirects map were taking two hops with a meta-refresh as the final step: Astro renders each entry as an HTML page carrying http-equiv="refresh", and trailingSlash: '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, which curl -L does not. The rules move to public/_redirects, where this repo already resolves /security in 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> element build 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 an og:image that returned 404 — six of the forty URLs in the sitemap sharing a card that renders blank everywhere it is posted. SEO.astro's derivedOgPath() 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 from DOC_GROUPS so a seventh group cannot reintroduce the gap, and GROUP_LEDE moved into src/lib/content.ts so 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-Tokens no longer reports 0 on a HEAD request. passThrough() computed the estimate from await 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 declared Content-Length, and with neither a body nor a length it now sends no estimate at all.

Changed

  • Last-Modified reflects 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.txt all described lumasync.app as collecting aggregate page metrics through self-hosted Umami at umami.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 neither PUBLIC_UMAMI_SITE_ID nor PUBLIC_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 on link.umamiTarget, so a link's reverse-tabnabbing and Referer-leak protection depended on whether someone had remembered to tag it for analytics. The condition now reads the URL itself.
  • fflate is floored at 0.7.5, closing GHSA-px8p-9vwx-vf98. Satori pulled 0.7.3 transitively — twice — into the shipped tree, where unzipSync can 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 --prod now 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.js decodes 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/mdx 7.0.5 → 8.0.1, @astrojs/sitemap 3.7.3 → 3.7.4, Satori 0.29.0 → 0.33.4, marked 18.0.9 → 18.0.13, DOMPurify 3.4.13 → 3.4.15, isomorphic-dompurify 3.22.0 → 4.3.0, Prettier 3.9.6 → 3.9.8, and pnpm/action-setup 6.0.10 → 6.1.0 in both workflows. Held back: TypeScript stays on 6.x because astro check refuses 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-astro stays on 0.14.1 because 1.0.1 does not converge; formatting src/pages/index.astro four times in a row produces four different files, so prettier --check can never pass against it. @astrojs/language-server is pinned to 2.16.13 through pnpm.overrides because 2.17.0 mis-parses the multi-line set:html expression in Schema.astro and reports five phantom syntax errors on a file astro build compiles 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.mjs rewrites on each build by design, and the sitemap's lastmod values, 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, while discover_wled_devices is a single-IP /json/info probe 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 named app.json on every platform and ~/.config on Linux, so the r

Releases

  • v1.1.44 — Bun replaces pnpm, with an unchanged build and a stricter CVE gate

    Changed

    • Bun replaces pnpm as the package manager; the site it builds is unchanged. pnpm-lock.yaml is gone and bun.lock takes 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 read pnpm.overrides, so the floors moved to the top-level overrides key; fflate still resolves to 0.7.5, which matters because an unpinned fflate once 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 built dist/ 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 --prod gated only the shipped tree; bun audit has 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 through bun pm licenses --prod, and Dependabot now tracks the bun ecosystem.
    • Last-Modified reflects this release.
    Open on GitHub
  • v1.1.43

    Fixed

    • The homepage has a title longer than its own brand name. <title> was LumaSync — 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 reads LumaSync — 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 area and philips hue entertainment area are 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 a title === '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 areas is 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.xml answers instead of 404ing. Nothing was broken — Astro's sitemap integration names the index sitemap-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 from public/_redirects rather than duplicating the file, so there stays one sitemap with one generator.

    Changed

    • Last-Modified reflects this release.

    Not changed, deliberately

    • A GEO audit scores the site's AI-bot posture as three critical failures — GPTBot, CCBot and Bytespider blocked. All three are training crawlers, and the tooling's claim that blocking GPTBot costs ChatGPT search visibility is wrong per OpenAI's own bot documentation: that is OAI-SearchBot's job. The live robots.txt allows 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.
    Open on GitHub
  • v1.1.42

    Fixed

    • Legacy aliases resolve in one edge 301 instead of two hops. All thirteen entries in Astro's redirects map were taking two hops with a meta-refresh as the final step: Astro renders each entry as an HTML page carrying http-equiv="refresh", and trailingSlash: '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, which curl -L does not. The rules move to public/_redirects, where this repo already resolves /security in 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> element build 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 an og:image that returned 404 — six of the forty URLs in the sitemap sharing a card that renders blank everywhere it is posted. SEO.astro's derivedOgPath() 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 from DOC_GROUPS so a seventh group cannot reintroduce the gap, and GROUP_LEDE moved into src/lib/content.ts so 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-Tokens no longer reports 0 on a HEAD request. passThrough() computed the estimate from await 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 declared Content-Length, and with neither a body nor a length it now sends no estimate at all.

    Changed

    • Last-Modified reflects this release. The middleware constant is bumped alongside the deploy, as the release checklist requires.
    Open on GitHub
  • v1.1.41

    Fixed

    • The site stopped claiming analytics it does not run. The privacy notice, the telemetry reference and llms.txt all described lumasync.app as collecting aggregate page metrics through self-hosted Umami at umami.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 neither PUBLIC_UMAMI_SITE_ID nor PUBLIC_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 on link.umamiTarget, so a link's reverse-tabnabbing and Referer-leak protection depended on whether someone had remembered to tag it for analytics. The condition now reads the URL itself.
    • fflate is floored at 0.7.5, closing GHSA-px8p-9vwx-vf98. Satori pulled 0.7.3 transitively — twice — into the shipped tree, where unzipSync can 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 --prod now 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.js decodes 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/mdx 7.0.5 → 8.0.1, @astrojs/sitemap 3.7.3 → 3.7.4, Satori 0.29.0 → 0.33.4, marked 18.0.9 → 18.0.13, DOMPurify 3.4.13 → 3.4.15, isomorphic-dompurify 3.22.0 → 4.3.0, Prettier 3.9.6 → 3.9.8, and pnpm/action-setup 6.0.10 → 6.1.0 in both workflows. Held back: TypeScript stays on 6.x because astro check refuses 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-astro stays on 0.14.1 because 1.0.1 does not converge; formatting src/pages/index.astro four times in a row produces four different files, so prettier --check can never pass against it. @astrojs/language-server is pinned to 2.16.13 through pnpm.overrides because 2.17.0 mis-parses the multi-line set:html expression in Schema.astro and reports five phantom syntax errors on a file astro build compiles 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.mjs rewrites on each build by design, and the sitemap's lastmod values, which derive from source-file mtimes.
    Open on GitHub
  • v1.1.40 — honest docs published, freshness metadata refreshed

    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, while discover_wled_devices is a single-IP /json/info probe 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 named app.json on every platform and ~/.config on Linux, so the reset instructions told users to delete a file that does not exist; the real file is shell-state.json under 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 nested hue.* / wled.* groups and hand-edit advice for keys absent from ShellState, 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-Modified reflects this release. The middleware constant had been sitting at 10 August while the content underneath it changed, and updated: frontmatter on the thirteen corrected pages still claimed dates as old as April — so the JSON-LD dateModified that answer engines read was dating a rewritten page to before its rewrite.

    Changed

    • sanitizeUrl in the search component returns the parser's own output rather than validating one string and handing back another. An automated report claimed a javascript: 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's href pipeline found no input the guard reads as http: 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.yml has fired only on a published release for some time, but CLAUDE.md still documented a manual wrangler push 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-hoc workflow_dispatch path and a verify-by-content step alongside it.
    • Dependencies: Astro 7.2.0 → 7.2.2.
    Open on GitHub
  • v1.1.39 — honest first-launch documentation

    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 /compare listing heading uses the sans-serif page title like every other listing page. global.css reserves 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.json sets minimumSystemVersion to 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-portal in its place — the actual prerequisite, and something that can be honestly queued.
    Open on GitHub
  • v1.1.38 — LumaSync v1.5.4 documentation sync

    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_IP check.
    • 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.
    Open on GitHub
  • v1.1.37 — fast-uri override floor raised to clear GHSA-7p8r-x3mc-p8w7

    Security

    • The fast-uri override no longer pins the toolchain to a vulnerable release. GHSA-7p8r-x3mc-p8w7 (host confusion via a backslash authority introducer) affects fast-uri below 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.5 and the lockfile resolves accordingly.
    • The package is development-scoped only (@astrojs/check@astrojs/language-servervolar-service-yamlyaml-language-serverajv), so it never reached the shipped bundle and the --prod audit gate stayed green throughout. pnpm audit --audit-level=high across the full tree, dev included, is now clean too.
    • Dependabot's security update for fast-uri can 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

    Open on GitHub
  • v1.1.36 — CVE override floors, Last-Modified refresh, dependency bumps

    Security

    • Two high-severity CVEs in the shipped dependency tree are closed — js-yaml (GHSA-5p4m-2wfm-xmqj, quadratic CPU consumption resolving !!omap) and nanoid (GHSA-2v37-7h3g-55p8, custom generators can loop indefinitely when size is zero).
    • Neither is a direct dependency: js-yaml arrives via @astrojs/mdx@astrojs/internal-helpers, nanoid via @tailwindcss/vitevitepostcss. 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-yaml moves from ^4.3.0 (itself now vulnerable) to ^4.3.1, and nanoid joins pnpm.overrides at >=3.3.17. Both floors already match what the lockfile resolves, so there is no resolution churn.
    • The pnpm audit --prod --audit-level=high gate had been failing on main and passes again.

    Fixed

    • The site-wide Last-Modified freshness signal is current again. The edge middleware's LAST_MODIFIED constant 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 — astro 7.1.5 → 7.2.0 (which also moves vite 8.1.5 → 8.2.1), marked 18.0.7 → 18.0.9, dompurify 3.4.12 → 3.4.13, isomorphic-dompurify 3.19.0 → 3.22.0. Manifest and lockfile only, no source change.

    CI

    • pnpm/action-setup pinned 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

    Open on GitHub
  • v1.1.35 — transitive CVE overrides, Astro 7, dependency refresh

    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/mdx and @tailwindcss/vite; each is pinned to its patched range via pnpm.overrides.
    • The pnpm audit --prod --audit-level=high gate had been failing on main and 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/mdx 6.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-cid hashes, and tighter minification.
    • TypeScript held at 6.0.3 — the 7.x native compiler drops the programmatic Language Service API astro check needs (withastro/roadmap#1321).
    • Minor/patch group bump across 11 packages: satori, tailwindcss, @tailwindcss/vite, three @fontsource families, marked, isomorphic-dompurify, prettier, prettier-plugin-tailwindcss, @astrojs/check.

    CI

    • actions/setup-node v6 → v7 in the CI and deploy workflows.
    Open on GitHub
  • v1.1.34 — inline-link press states, dependency bumps

    Fixed

    • Press-state feedback on inline links now actually renders. The 404 page's .home-cta and the /download/ version pill both declared a prefers-reduced-motion-gated transform: scale(0.96) on :active, but both are anchors laid out as non-replaced inline boxes — where CSS transform does not apply. The press animation was silently dead CSS. Both now set display: inline-block, so the tactile :active treatment shipped for the version pill in v1.1.31 finally takes effect.

    Accessibility

    • The landing page's .inline-cta links press like the site's other CTAsdisplay: inline-block, a transform transition, and a prefers-reduced-motion-gated scale(0.96) on :active, matching the primary/secondary CTAs and compare cards.

    Dependencies

    • Minor/patch group bumps across two batchestailwindcss and @tailwindcss/vite 4.3.0 → 4.3.2, marked 18.0.5 → 18.0.6, dompurify 3.4.11 → 3.4.12, prettier 3.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

    Open on GitHub
  • v1.1.33

    Added

    • Content-Usage AI-preference signal in robots.txt. Emitted alongside the existing Cloudflare Content-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 only search/train-ai so far, so the RAG axis stays on Content-Signal.
    • Web Bot Auth directory placeholder. /.well-known/http-message-signatures-directory now serves an empty JWKS ({ "keys": [] }) as application/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 a categories:agentic-browsing assertion is deferred until lhci bundles Lighthouse ≥ 13.3.0.
    Open on GitHub
  • v1.1.32

    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 an r+ descriptor (fails closed with ENOENT when absent, preserving the stamp-only-if-present behaviour) and appends through that descriptor, so the check and the write target the same handle. CodeQL js/file-system-race alert resolved.
    Open on GitHub
  • v1.1.31

    Security

    • Pagefind search results now scheme-validate their link href — a sanitizeUrl() guard parses each result URL with the native URL constructor and allow-lists only http:/https: (falling back to #) before HTML-escaping, blocking javascript:/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-visible outline, a prefers-reduced-motion-gated scale(0.96) :active press, and a descriptive title.

    Dependencies

    • Dev-only prettier bump 3.8.4 → 3.9.1 (minor/patch group) — manifest + lockfile only, all gates pass unchanged.
    Open on GitHub
  • v1.1.30

    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.js with the old CSP header. Because that file's bytes never change between deploys, conditional revalidation kept returning 304 and 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 full 200 that ships the current CSP. The search worker can compile its WASM module again.
    Open on GitHub
  • v1.1.29

    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 →".
    Open on GitHub
  • v1.1.28

    Fixed

    • Cmd+K search restored — the Content-Security-Policy script-src was 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 general eval).

    Build

    • Pinned the vendor/lumasync submodule to the v1.5.3 release commit so the deployed build resolves the v1.5.3 version surfaces (roadmap header, JSON-LD softwareVersion, compare cells) and renders the v1.5.3 release notes on /changelog/ — completing the v1.1.27 app sync.
    Open on GitHub
  • v1.1.27

    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-Modified freshness signal.
    Open on GitHub
  • v1.1.26

    Security

    • Finished the external-link noopener rollout on the render-time href links 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 each href, so internal and disabled links stay untouched.

    Accessibility

    • Skip-to-content link now draws a visible :focus-visible ring (2px --focus-ring) plus a reduced-motion-gated scale(0.96) press state once it slides into view.
    • 404 page recovery cards and the ← Home link gained the standard :focus-visible outline so keyboard users can see which target holds focus.

    Dependencies

    • Minor/patch group: astro 6.4.7 → 6.4.8, dompurify 3.4.8 → 3.4.11, isomorphic-dompurify 3.16.0 → 3.18.0 (manifest + lockfile only).

    Build

    • GitHub Actions: actions/checkout 6 → 7 across CI/CodeQL/deploy, and pnpm/action-setup 6.0.8 → 6.0.9 (SHA-pinned). CI-only.
    Open on GitHub
  • v1.1.25

    Security

    • esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): pnpm override pins esbuild to >=0.28.1; the pnpm audit --prod --audit-level=high CI gate is green again.
    • yaml advisory cleared (GHSA-48c2-rrv3-qjmp): pnpm override forces yaml to >=2.8.3 (was 2.7.1 via @astrojs/check). pnpm audit now 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 title tooltip on hover (.forums a.disabled: pointer-events: nonecursor: default), with no interactivity regression.

    Dependencies

    • astro 6.4.4 → 6.4.7 (manifest + lockfile), plus a full lockfile refresh for the latest in-range patches across the tree.
    Open on GitHub