Repository navigation
Releases: OmniRise/LinkBreeze
Release list
LinkBreeze v1.5.1
Added
-
Admin UI and README in Italian, Russian, Turkish, and Japanese — four new languages, bringing the admin to 11 and the README to 11 translations: the Settings language dropdown, /login, /setup, the 2FA step, and the global error screen all switch (Russian uses the formal register standard for Russian software; Japanese drops ICU plural forms by design, as Chinese already does). Public pages stay English by design. Translated by AI agents and verified by a terminology-review pass; as with all machine-drafted locales, a native-speaker read before relying on a locale in production is worth it (docs/TRANSLATIONS.md policy).
-
Catalog packaging for CasaOS, Runtipi, and Unraid (
packaging/) — LinkBreeze now ships first-party app-store packaging as canonical sources underpackaging/: a CasaOS App Store app definition (packaging/casaos/, pinned image, icon, screenshot, en_US metadata), a Runtipi v4 app-store repository (packaging/runtipi/, to be published asOmniRise/LinkBreeze-Runtipi-Store), and an Unraid Community Applications template repository (packaging/unraid/, dockerMan-format XML plus repository profile and MIT license, to be published asOmniRise/LinkBreeze-Unraid-Templates). All three pull the officialdocker.io/omnirise/linkbreezeimage with a pinned semver tag, mount the data volume, and need no environment configuration (the setup wizard handles first run). The packaging policy is documented in CONTRIBUTING.md: these three catalogs are maintained by the project; other platforms and hosting-provider deploy buttons stay community-PR territory. Per-release maintenance is one step: bump the pinned version in the three manifests during the release fold. -
Update status card in Settings (Data tab) (#123) — A new card shows the installed version next to the latest release from GitHub and from the website, with a status line ("You're on the latest version." or "You can update to vX.Y.Z." with release-notes link and the pull command), a Recheck button that bypasses the 24h cache, last-checked time, and a "last known value" note when a source is unreachable. The GitHub source is a new unauthenticated latest-release check (24h-cached like the website poll); the dashboard update banner is unchanged. This was announced in the v1.5.0 release notes but its merge landed just after the
v1.5.0tag was cut — it actually ships in this release.
Changed
-
Dependency updates (#126, #127) — Merged two Dependabot minor-and-patch groups: #126 (
lucide-react1.59.0→1.61.0,shadcn4.22.0→4.22.1,sql.js1.15.0→1.15.1) and #127 (next16.4.2→16.4.3,eslint-config-next16.4.2→16.4.3, a bug-fix backport). Both merged with green CI and byte-exact lockfiles. Also bumped thefast-urioverride ^3.1.7→^3.1.9 and added abody-parser^2.4.0 override, resolving Dependabot alerts #66–70 (both packages are transitive through the shadcn CLI's express/MCP SDK chain, dev-tooling only).node-forge(#58) stays unchanged: no patched release exists, andpasskit-generatoruses it only for pass signing, never verification. -
Locale parity gate in CI — CI now runs the locale hash gate (
npm run i18n:check) and a generalizedscripts/locale-audit.mjsacross all ten locales, checking key parity, ICU placeholder parity, and untranslated-string suspects. The audit previously covered only French and Spanish and ran manually, so a broken{placeholder}in another language could reach main; its ICU handling was also corrected (it stripped plural placeholders, which would falsely flag Japanese and Chinese — both correctly drop ICU plurals — as mismatches). -
Language lists reordered: English first, then alphabetical — one convention everywhere languages are listed: the admin language dropdown (picker order), the README translation rows in all eleven READMEs,
docs/TRANSLATIONS.md, and the website locale lists (routing, sitemap, hreflang alternates) now read English · العربية · 中文 · Français · Deutsch · Italiano · 日本語 · Português (Brasil) · Русский · Español · Türkçe instead of historical add-order. -
Canonical repo URLs point at OmniRise/LinkBreeze — All living documentation (README ×7, CONTRIBUTING, SECURITY, TROUBLESHOOTING) now references
github.com/OmniRise/LinkBreezeinstead of the pre-transferManak-hash/LinkBreezeURLs. The old links redirect, so nothing was broken; this makes the canonical location explicit and consistent with the Docker Hub namespace, the website, and the app-store listings.
Fixed
-
The update checker's GitHub fallback still queried the pre-transfer repo — the update-check module called
api.github.com/repos/Manak-hash/LinkBreeze/releases/latest; the canonical-URL pass in this release covered documentation only, and this one code path kept the old name. GitHub's rename redirect kept it working, but the checker now queriesOmniRise/LinkBreezedirectly. -
Local
npm run testfailed on the TOTP lifecycle test (#128) — the enroll → activate → verify → disable lifecycle hashes 8 recovery codes with bcryptjs at cost 12 (single-threaded JavaScript: ~7.9s measured on modest dev hardware), well past vitest's 5-second default, so every local full-suite run died onTest timed out in 5000mswhile CI's faster runner squeaked under and kept main green. The test now carries an explicit 20s budget with a comment; the hashing strength (cost 12) is untouched. Diagnosed as budget, not regression: the clean tree failed identically and the same run passed at a 30s timeout. -
Importing a Linktree page pulled Linktree's own affiliate links instead of the account's — the migration wizard's
__NEXT_DATA__extractor recursively searched the entire Next.js payload for any{url, title}array and swallowedprops.pageProps.statsigInitValues, a statsig feature-flag blob whose sponsorship list is Linktree's global affiliate catalog (an identical ~26-item array on every profile: Hulu, HelloFresh, Headspace, and more). Importing any Linktree URL returned those promo links and buried or dropped the account's real links, and client-rendered mega-accounts returned only the promo list. The extractor now reads Linktree's real link containers first (pageProps.links/socialLinksand theiraccount.*mirrors), the recursive walk skips config subtrees (statsigInitValues,googleAdConfig, blog teasers), and the inline-script tier no longer re-parses__NEXT_DATA__(which was re-introducing the blob). Client-rendered accounts now correctly report "No links found on this page" (use the File tab) instead of the affiliate list; Bento, Hopp, and Beacons extraction is unchanged, and regression tests cover the blob, the empty-SSR case, and the non-Linktree fallback path. -
Migration wizard's import button and social chip were hardcoded English — the confirm button (Import N items) and the fallback label for an unrecognized social profile (Social) rendered in English regardless of locale; both now come from the locale dictionaries across all ten non-English languages, with correct per-language ICU plurals (Russian four-form, Arabic six-form, Japanese and Chinese plural-less by design).
Security
- Update-check server actions skipped the demo guard (#128) — the three update-check actions (the settings Recheck, the update-notification toggle, and the dashboard banner's refresh) checked the session but never called
demoGuard(), unlike every other mutating action. On a publicDEMO_MODEinstance — whereDEMO_AUTO_LOGINhands every visitor a session — any visitor could flip update checks for all demo users and bypass the 24-hour cache on demand, driving uncached outbound fetches to the GitHub and website endpoints from the host server. All three now guard first and throw the standard read-only-demo message before touching the session or the network, covered by a new action test.
LinkBreeze v1.5.0
LinkBreeze v1.5.0
Added
-
@ in page slugs (requested by @stuff-by-rene) — Page slugs can now optionally start with an @ (
/@churchname), the handle style people know from other link tools. Opt-in per page: slugs without @ behave exactly as before,/go/redirect slugs stay letters-and-hyphens, and an @-slug can never shadow an app route (it is always a different string from every reserved name). Percent-encoded forms (%40) resolve to the same page. -
Language selector redesigned as a compact dropdown — The row of seven pill buttons on /login, /setup, the 2FA step, and the Settings language card is replaced by one shared dropdown: a small pill showing only the current language that opens a panel listing every locale with its endonym, English name, and locale-code chip. The panel is keyboard-navigable (arrows/Enter/Escape), flips upward when the trigger sits low on the page, follows the trigger on scroll, and renders above cards and modals. No locale-file changes — new languages appear in it automatically.
-
Custom domains for pages (#85, reported by @einlichtvogel) — Bind your own domain names to pages, one instance serving many: each page can own a primary domain plus aliases (e.g.
jane.com+jane.io+www.jane.com), and visitors land on the bare domain to see the page — no/slug. The new Domains admin section manages bindings with per-alias behavior (serve / 301 to the primary domain / 301 to the page slug) and per-page scope: exclusive (the domain IS the page — other slugs 404) or hub (page at the root, every slug still resolves). An instance-wide routing policy card controls the primary host (also via theLINKBREEZE_PRIMARY_HOSTenv var), a strict admin host lock (404s admin/login/setup on non-primary hosts — anyone can point a domain at your server; your login page never serves there), unknown-host fallback (path routing / 404 / redirect to primary), implicit www handling, and the canonical strategy (301 slug→domain by default, canonical-tag-only, or off). Bindings flip from pending to active on their first real request — DNS propagation becomes self-verifying — with a manual activate-now toggle and a DNS-record checker for pre-flight testing. SEO is first-class: canonical/og URLs point at the domain root, sitemaps list every active identity, and slug URLs 301 to their domain. Zero behavior change for instances that never bind a domain (migration0028_custom_domainsadds one empty table + one nullable column); full reverse-proxy recipes (Caddy on-demand TLS, nginx, Traefik) indocs/custom-domains.md. All new UI ships in all seven locales. -
OG auto card redesigned as a "Poster Hero" — The auto-generated social card no longer floats small text in an empty frame: big centered identity (170px avatar or theme-colored initial, 82px name, full-opacity bio) plus the page's top four link titles as themed pills with a "+N more" tail, so shares preview the actual content. The real LinkBreeze mark sits top-left and the two-tone wordmark top-right. Everything derives from the page theme (text/primary/secondary colors, all ten background types incl. photo backdrops with the theme overlay) via the new satori-safe
withAlphacolor helper andselectOgLinkspicker — verified on dark, light, warm-gradient and aurora presets. -
Automatic scheduled backups (#83) — A "Scheduled backups" card in Settings → Data turns on app-internal backups with no cron required: daily/weekly/monthly frequency plus a preferred time of day (runs at the first activity on or after it), keep-last-N retention (default 7), and the exact same JSON format as the manual export so restore is the proven path — stored backups restore through the identical validated transactional flow, with date/size listed and one-click restore/delete behind the same confirmation dialogs. Backups write atomically to
./data/backups(env-overridable viaBACKUP_DIR), a disk-space guard (BACKUP_MIN_FREE_MB, default 50) skips writing and reports instead of failing, and concurrency rules hold: never two backups at once, never a backup mid-restore. The trigger is opportunistic on page traffic and admin loads, off by default, and a dismissible dashboard notice (re-arming only on the next failure) reports failed runs. No migration — configuration lives in the existing settings table. -
Custom /go/ redirect slugs (#96) — Links can now carry an optional custom redirect slug alongside their numeric ID, so a shared, printed, or embedded redirect URL survives delete-and-recreate:
/go/my-consultinginstead of/go/42. Slugs are lowercase[a-z0-9-], unique, up to 64 characters, with a reserved-word blocklist (login,setup,dashboard,links,settings,api,sitemap.xml,robots.txt, and all pure numbers) so they can never shadow app routes. The link dialog gains a slug field with live availability checking, deleting a link releases its slug for reuse, and a deactivated link that has a slug redirects visitors to its public page instead of a bare 404. Numeric/go/:idURLs keep working unchanged, and backup restore sanitizes invalid or duplicate slugs rather than rejecting the file. -
Official OmniRise Docker Hub image (
omnirise/linkbreeze) — LinkBreeze is moving under the OmniRise brand with an official Docker Hub presence as the default public pull path. The release workflow now publishes per-platform images to both Docker Hub and GHCR and assembles both manifest lists from the same content-addressed digests (one build, two registries, byte-identical images), and the dry-run mode still builds without pushing anything. First published tags:latestandv1.4.1, multi-archlinux/amd64+linux/arm64, anonymous pulls verified working. -
One-shot update script (
update.sh) — Self-hosted instances on any install method can now update withcurl -fsSL https://linkbreeze.omnirise.dev/update.sh | sh. The script finds the existing container (by name or by image), backs up the data volume to~/.linkbreeze/backups/<timestamp>/, pulls the current image, and recreates the container with the same port mapping, volumes, environment variables, and restart policy — then prints the new version, the backup path, and a ready-to-paste rollback command. Tested end-to-end against both a standard named-volume install and a bind-mount compose install: data, env vars, and ports all survive; the script warns and continues if the backup itself fails, never touching the data volume until the new image is pulled. -
Update banner shows the exact update command — The "new version available" banner previously linked to release notes only, leaving operators to figure out the update steps themselves. It now displays the copy-paste command (
docker pull omnirise/linkbreeze:latest && docker restart linkbreeze) plus the one-shotupdate.shURL right in the banner, next to the release-notes link. -
Social preview (OG) images — theme-following or your own upload — Every page now renders a 1200×630 share card at
/<slug>/opengraph-image. Auto mode draws the card from the page's live theme through a pure resolver (src/lib/og-theme.ts): every background type gets a faithful static rendering (multi-stop gradients at the theme's angle, radial at the same focal point, mesh as layered radial stops, image/gif backdrops with the theme's overlay, aurora as base color + accent glows, video as the loading-state gradient), plus the page avatar and title in the theme's text/link-card styling — change the theme and the share card changes with it, no re-upload. Custom mode serves an uploaded image directly asog:image, with the shared fit/focus/zoom picker (adjustment-only, non-destructive) and a live in-card preview; crawlers' aggressive caching is called out in the UI with guidance to re-validate. The card is managed from its own autosaved card in Settings (mode toggle saves instantly, uploads pair with custom mode in one action). Migration0027_og_imageadds six nullablepagescolumns (og_image_mode/url/fit/pos_x/pos_y/zoom) — existing installs boot unchanged and default to auto.
Changed
-
Dependency updates (#117, #118, #119, #120, #121) — CI:
actions/download-artifact7 → 8. npm minor-and-patch group (16 packages):react/react-dom19.3.1,lucide-react1.59.0 (#119: 1.58.0, #120: 1.59.0 — icon render-contract test green, full suite passing),shadcn4.22.0 (#120, dev CLI),hono4.13.13 (#121),next-intl4.14.9,passkit-generator3.7.0,drizzle-orm0.45.4,zod4.6.7,@base-ui/react1.9.0, Playwright 1.64.1 + axe 4.14,drizzle-kit0.31.11,eslint-config-next16.4.2,sql.js1.15.0,vitest5.0.5 — no breaking changes. -
Install script and update checks served from linkbreeze.omnirise.dev instead of raw.githubusercontent — The one-line installer lives at
linkbreeze.omnirise.dev/install.sh(marketing site, propertext/x-shellscriptcontent type), and the update checker pollslinkbreeze.omnirise.dev/latest-version.json. Both previously depended onraw.githubusercontent.com, which is unreachable during any GitHub account visibility issue — instances v1.2.0–v1.4.0 polled a 404 and silently never saw update banners; this change restores the notification chain for every install from this release forward (older installs pick it up when they next update). The website-servedlatest-version.jsonis the source of truth for update checks, and itsurlfield points at the canonical GitHub release, so the update banner link stays live even if the mirror lags — the GitLab mirror remains a passive pull copy. -
All documentation and examples reference the official registries — Every README translation (7 languages), TROUBLESHOOTING, docker-compose.yml, the demo compose file, all seven example compose files, and kubernetes.yaml now use
docker.io/omnirise/linkbreeze:latest. The README gains a "Repositories & Registry" section (GitHub, GitLa...
LinkBreeze v1.4.1
LinkBreeze v1.4.1
Live preview that actually follows your edits — plus autosave, image positioning, and a round of security fixes.
✨ Admin experience
🔴 Live preview follows every edit
The preview pane used to lag one save behind — save, hit refresh, and still see the last-but-one state. Now:
- Cache-busted reloads (
?v=<timestamp>) after every save, including the manual refresh button - One shared save flow (
useSavedAction) drives every admin form — profile, links & sections, drag-to-reorder, settings tabs, theme fork/customize, migration wizard — so a successful action always refreshes the data and the preview in one uniform path
edit → autosave → preview updates in place, where you were looking💾 Autosave on the forms the preview watches
Typing now saves itself — debounced 600 ms, deduped so re-renders never re-POST identical state:
| Surface | Behavior |
|---|---|
| Theme editor | custom themes autosave (presets keep the fork/duplicate dialog) |
| Profile identity | name, bio, badge, avatar/banner URLs |
| Social links | add a chip, type a URL, remove one — all schedule the same save |
| Settings → General | page title, SEO, footer, privacy policy |
| Settings → Integration | analytics, consent text (switches still save instantly on toggle) |
| Settings → Appearance | custom CSS (theme chips still save on click) |
The dead Save buttons are gone — along with the empty footer strips that held them. What keeps an explicit button is exactly what autosave must never touch: page settings dialog, link/section dialogs, migration confirm, and anything security-sensitive. Typing in password/2FA fields fires zero requests (verified live, bcrypt hash asserted unchanged).
🖼️ Non-destructive image positioning
Fit / focus / zoom for avatar, banner, and background — computed at render, never baked into the file:
- One shared picker, auto-opens after upload
- Drag to set the focus point · wheel-to-zoom · live
X · Y · zoomreadout · Cover/Contain chips · reset - Migration
0025adds twelve nullablepagescolumns;NULLrenders pixel-identical to before, so existing installs are untouched - Video backgrounds take fit + position only
📜 Preview keeps your scroll position
Autosave reloads the iframe — which used to snap you back to the top. The pane now remembers the scroll offset and re-applies it after every reload, clamped to the page height. Editing a link at the bottom of a long page finally stays where you're looking.
🔧 Fixed
- Cleared fields no longer revert on reload — deleting the page title, SEO text, footer, privacy policy, custom CSS, analytics, or a profile URL used to silently resurrect the old value (
""was treated as "absent → leave unchanged"). True tri-state semantics now: present = write, even empty. Verified live across every affected field. - X offered twice in the social picker — "X (Twitter)" and "X" both rendered the identical logo.
twitteris now an alias of the singlexentry; old links keep working. - Media URL scheme allowlist — avatar, banner, favicon, and background URLs now accept only
https://,http://, or site-relative paths at the save boundary, sojavascript:/data:URIs can never reach asrcattribute (closes CodeQL alerts #20/#21) - vCard test escape pattern — the fixture now escapes backslash + semicolon together (closes CodeQL alert #19; production escaping was already correct)
- joi prototype pollution (Dependabot #26/#27) —
passkit-generatorpinnedjoi@17.13.4; an npm override now forces 17.13.8.npm audit: 0 vulnerabilities.
📦 Dependencies
Dependabot minor-and-patch group (#113): next 16.3.5 · zod 4.6.5 · lucide-react 1.47 · next-intl 4.14.5 · tailwind-merge 3.7 · vitest 5.0.1 · eslint-config-next 16.3.5 · @types/node 26.6
✅ Quality
857/857 unit tests · lint & typecheck clean · 21 live-browser checks (8 delete-persistence, 6 autosave, 7 picker/preview) · security negative-tests verified
Full changelog: see CHANGELOG.md
⬆️ Upgrading: pull the new image / git pull + npm ci — migration 0025 applies automatically on first boot. No breaking changes; every existing page renders exactly as before.
LinkBreeze v1.4.0
LinkBreeze 1.4.0
Added
- Two-factor authentication (TOTP) — The admin login can now require a second factor: a 6-digit code from any authenticator app. Setup lives in Settings → Security: scan the QR or paste the secret, confirm with one live code, and save your 8 single-use recovery codes. Secrets are stored encrypted (AES-256-GCM); "trust this device for 30 days" skips the code step per browser, and re-enrolling invalidates every trust.
- Popup link cards: text and location — Two new link types. A text card opens a dialog with a markdown body and an optional CTA button; a location card opens the same dialog with a keyless embedded Google map (loads only when the visitor clicks, so no third-party contact until then) and an "Open in Google Maps" CTA that counts as a click. Paste an address, a full Maps URL, or a mobile share link — it's parsed at save time.
- Divider element — A themed horizontal separator you can drag between links. Style per theme: solid/dashed/dotted/gradient fade, color, thickness, width, with a live preview. Rendered from theme tokens, zero client JS.
- Search engine visibility toggle — One switch in Settings to send
noindex, follow, drop the page from the sitemap, and pull the sitemap line from robots.txt. Flips back instantly. - Admin UI in German, Simplified Chinese, and Arabic — Full dictionaries with key parity alongside English, French, and Spanish. Arabic flips the admin to RTL; German uses the informal register; Chinese and Arabic carry proper ICU plurals.
- arm64 container images — GHCR images are now multi-arch (linux/amd64 + linux/arm64), built natively on arm runners. Runs on Raspberry Pi 3/4/5 (64-bit OS) and Apple Silicon. Same docker run/compose instructions as before.
- Theme additions — Avatar size (48–180px + Auto), a separate card font (site font and card font can differ, including uploaded fonts), and opacity sliders for card background and border colors.
- Popup analytics — Opens are tracked as a separate event type; clicks stats and CTR stay pure.
Changed
- Dependency updates — React 19.3, zod 4.6, next-intl 4.14.3, Playwright 1.63, lucide-react 1.45 (with a compat fix so every picked icon keeps rendering inline), hono/sharp/js-yaml security bumps, vitest 5.
- Popups become responsive bottom sheets on phones.
Fixed
- Live preview no longer covers the admin it docks beside
- Imported links now resolve favicons like manual saves do
- Typewriter display-name animation (caret and reveal fixes)
- Pasted Google Maps URLs now render a map; map embed no longer blocked by CSP
- Popups are pixel-centered on desktop
Full changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md
Install: curl -fsSL https://raw.githubusercontent.com/Manak-hash/LinkBreeze/main/scripts/install.sh | bash or the usual docker run from ghcr.io/manak-hash/linkbreeze:1.4.0. Demo: https://linkbreeze-demo.omnirise.dev/linkbreeze
LinkBreeze v1.3.2
LinkBreeze v1.3.2
What's new
Spanish admin UI (es)
The admin speaks a third language. A complete Spanish dictionary (700 keys, tuteo register, terminology aligned with the Spanish README) ships alongside English and French with the same compile-time key parity and drift guards — a missing key fails the build, not your users. Español joins the language picker in Settings; the choice persists per user like en/fr.
Also fixed along the way: four keys that had drifted and rendered as raw key paths (setup wizard, integrations tab, password form, QR card), the raw Enabled/Disabled labels beside the email-capture switch, and two broken English source strings ("Choose a settings.", "You call it later."). 74 dead keys pruned from en/fr. New npm run i18n:audit parity tool for deep locale checks beyond the hash gate.
Set a default page and keep it first (#90, reported by @baptleduc)
Every page switcher row now has a star: click it on any page to make that page the default, confirmed by a toast. The default page always sorts to the top of the switcher, the links page, the theme page, settings, and the dashboard, so it stops wandering as you add pages.
Per-link icons: auto, picked, or uploaded (#91, reported by @gdyys96b56-web)
Links are no longer stuck with whatever favicon a site serves (or none, for email, phone, WhatsApp, and vCard links). The link dialog has a compact new Icon section:
- Auto keeps today's behavior: fetch and cache the site's favicon when the link is saved.
- Pick opens the categorized icon gallery and stores the choice as data, not an image: the public page renders it as an inline SVG that inherits the theme's text color, so it matches every theme and card style for free.
- Upload takes PNG/JPG/GIF/WebP/AVIF/ICO/SVG up to 512 KB. Files are validated by magic bytes, not by name, and SVGs pass through a sanitizer that strips scripts, event handlers, foreign objects, and external references before storage.
Fixed
- Upload failures were silent — a rejected icon upload looked like the button ignored you; errors now surface inline in the dialog.
- Uploads never reached the server — the icon file input carried no field name, so the file was dropped from the form data.
- Long dialogs stranded their Save/Cancel buttons off-screen; the shared dialog is now capped at 90% viewport height and scrolls its body.
- Uploaded files were served under the page CSP; uploads now always ship
default-src 'none'. - Settings → Integrations crashed with INVALID_MESSAGE — a literal
<script>bracket in a rich-text hint broke next-intl's parser; reworded.
Full changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md
LinkBreeze v1.3.1
What's new
Admin UI in English and French
The whole admin panel (dashboard, links, theme customizer, settings, migration wizard, setup, onboarding, profile, dialogs, toasts, error screens) now renders fully in French. A language picker in Settings switches instantly; the choice persists per user (cookie + database). English remains the default; public link pages stay English by design so shared URLs and SEO never shift with the admin language.
- Typed TypeScript dictionaries — a missing or renamed key fails at compile time, not in production. A hash gate refuses to build when locale files drift apart.
- Locale-aware dates ("19 août"), number grouping ("1 234"), and plurals ("1 abonné", "3 abonnés").
- Server-side errors translate too: failed actions return message keys that render in the active language.
- French docs: README.fr.md plus contributor guide for the next languages (Spanish, Chinese, Hindi, Arabic, Portuguese).
Custom theme fonts (#82, reported by @einlichtvogel context: bundled set grows)
Upload your own woff/woff2 (max 2 MB) from the customizer's Typography tab and pick it for any theme. Four new bundled Google families: Montserrat, Caveat, Pacifico, Abril Fatface — the curated set is now 15.
Page and theme deletion (#84, reported by @einlichtvogel)
Non-default pages can be deleted from the page switcher, user-created themes from the theme page. Both ask for confirmation. Page deletion offers keep-links vs delete-everything; pages using a deleted theme fall back to the active one.
Fixed
- Link creation failing with "FOREIGN KEY constraint failed" once a page had sections (#86, reported by @einlichtvogel).
- Social icons editor losing focus and dropping rows while typing a URL (reported by @einlichtvogel).
- Link creation and edit flows shared the flawed schema; both fixed, with regression tests.
Dependencies
next 16.3.1, lucide-react 1.31.0, @types/node 26.2.0, and more maintenance bumps.
Full changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md
LinkBreeze v1.3.0
Highlights
Frutiger Aero theme
A glossy mid-2000s "water + air" theme — the 11th built-in preset:
- Gel bubble link style: rounded-pill shape, vertical aqua gradient, specular highlight arc, and a "gel press" hover/active interaction. Applied to link cards, social icons, and the subscribe button. Disabled under
prefers-reduced-motion. - Frosted glass everywhere — semi-transparent cloud-white card tint, backdrop blur, thin light borders, soft sky glow.
- Bubbles video background (Mixkit, free license) with an aqua gradient fallback when the video can't load.
- Nunito font joins the picker.
- Floating avatar, gradient-flow display name, blur-in reveal.
- Existing installs get the new preset automatically — no migration.
Theme customizer rework
- Live phone-shaped visualizer beside the fields, rendering with the production resolver.
- Overlay opacity fixed (slider scale vs renderer scale), noise texture implemented, Aurora follows your colors, preset save forks safely, dead controls removed, sliders replace free-text CSS, contextual fields, real-font font picker.
- Draggable focal point + cover/contain/tile picker for image/GIF/video backgrounds.
- Uploads fixed end-to-end (Server Action body limit raised to 10 MB).
QR customizer
Custom code/background colors, center logo (avatar or favicon, circular-clipped), PNG export at 256/512/1024 px, automatic High error correction when a logo is embedded.
Link sections (#78)
Group links under titled headers with optional Lucide icons.
Fixes & hardening
- Video backgrounds: CSP now allows cross-origin media (
media-src 'self' https: blob:) — hotlinked videos previously played nowhere, silently falling back to the gradient. Video themes paint their fallback gradient in the container. - Media-card radius clamp: pill themes (Frutiger Aero, Pastel Soft) no longer render rich-preview/thumbnailed cards as circles — tall cards use
min(radius, 24px). - Login dev-overlay crash ("LoginPage cannot have a negative time stamp" — Next.js 16 framework bug): pre-hydration guard added.
- Google fonts self-hosted: all 13 families ship in the repo (latin subsets). Builds work fully offline — CI and Docker no longer fetch fonts.googleapis.com, and the product makes zero third-party font requests at runtime.
- CodeQL fixes: HTML entity decoding in the OG fetcher no longer double-unescape (
&lt;stayed<). - Analytics retention defaults to 90 days (#77), with a shared resolver and matching policy text.
- Migration self-heal (#79): out-of-order migration records are repaired on startup.
Full changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md
Docker image: ghcr.io/manak-hash/linkbreeze:1.3.0 (also :latest)
LinkBreeze v1.2.7
Privacy, Security & Preview Cards
Built-in Privacy Policy Pages
Every public page now has a privacy policy at /{slug}/privacy, linked from a footer that is always visible. The policy auto-generates from the page's actual configuration (analytics, email capture, embeds, external analytics scripts, retention window) so it stays accurate without manual maintenance. Operators can write a custom policy to override the auto-generated text.
Security
- Email capture consent (#75) — Subscriber form now requires a consent checkbox. Consent timestamp and exact consent text stored in the database for audit trail. CSV export includes consent columns.
- Removed Google S2 favicon fallback (#72) — No more leaking visitor IPs to Google. Links without cached favicons show a first-letter avatar in the accent color.
- Referrer stripping in analytics (#73) — Only the origin of the Referer header is stored, not the full URL with paths and query parameters.
- CSP support for external analytics (#74) — Operators can allowlist analytics domains via
EXTRA_SCRIPT_SRCenvironment variable.
New Features
- Rich preview link cards — Per-link card style toggle (compact vs. rich preview). Rich cards fetch Open Graph data (title, description, image) and render with a full-bleed image on top, same style as thumbnail cards.
- Multi-strategy favicon fetcher — Replaced Google S2 with a 3-strategy chain: HTML parse > common paths > DuckDuckGo. No more generic globe placeholders for newer domains.
- Email subscribers panel in Data tab — Table of all subscribers with CSV export and Clear All, shown only when email capture is enabled.
- Email capture toggle in Settings UI — The feature existed but had no UI toggle. Now in Settings → General.
Improvements
- Subscribe button inherits theme border radius (was square on 5 themes)
- WCAG-based
--lb-btn-textcontrast token (fixes invisible button text on Glassmorphism and Terminal Mono) - Onboarding checklist simplified to 3 steps with auto-dismiss
- Email capture toggle persistence fixed (missing
revalidatePath)
Full Changelog: v1.2.6...v1.2.7
LinkBreeze v1.2.6
What's New
Featured Links (#48)
Links marked as "Featured" now stand out with a star badge, accent-tinted background, thicker border, bolder title, and taller card. Works across all link styles (glass, neon, pixel).
Onboarding Wizard
New multi-step setup wizard: credentials, profile, theme picker, and completion screen. A dismissible dashboard checklist helps new users get started with progress tracking.
Empty State Enhancements
Dashboard and links page now show helpful welcome messages with CTAs for fresh installs.
Deployment Examples (#42)
New examples/ directory with Docker Compose configs for Caddy, Traefik, Cloudflare Tunnel, Nginx, https-portal, backup sidecar, and Kubernetes manifests.
Demo Mode
Full demo instance support: CSP frame-ancestors for iframe embedding, auto-login middleware, and live theme preview via postMessage.
Fixes
- CSP now allows
unsafe-evalin development mode (React dev requirement) - SQLite
busy_timeout5s to prevent intermittent "database is locked" during builds - Admin layout set to
force-dynamicto fix Docker build prerender errors - Seed script properly releases DB handle after Lighthouse CI
Full Changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md
LinkBreeze v1.2.5
Fixed
- Secure cookie over HTTP (#70) — Session cookies were always flagged
Securein Docker, so self-hosters on LAN IPs could never log in. Now derived from the actual request transport viaX-Forwarded-Proto. Zero configuration required. - 500 on login/setup without SECRET_KEY — Bare
docker runwithoutSECRET_KEYcaused every login to 500. The app now auto-generates and persists a 32-byte key on first boot.
Added
- Full error system — Centralized
ActionResulttype withErrorCodeenum, structured logging,demoGuard()wrapper, and typed convenience constructors across all server actions. - Error boundaries — Three React error boundaries (admin, public, global) with recovery UI and retry buttons.
- Error UI components —
ErrorBanner(color-coded by error code) andSuccessToastfor consistent form feedback. - Accessibility regression tests — axe-core Playwright E2E tests scanning public link page, login page, and 404 for WCAG 2.2 AA violations.
- Backup/restore integration tests — Full round-trip verification: snapshot, restore, integrity check, edge case rejection.
Changed
- CSP enforcement — Promoted Content-Security-Policy from Report-Only (1.2.4) to enforced on all routes.
- Dependency bumps — next 16.3.0, better-sqlite3 13.0.3, lucide-react 1.30.0, @base-ui/react 1.7.0, shadcn 4.16.2.
Security
- npm overrides — Added
nanoid >=3.3.17andjs-yaml >=4.3.1overrides to resolve two high-severity Dependabot alerts in transitive dependencies.
Full changelog: https://github.com/Manak-hash/LinkBreeze/blob/main/CHANGELOG.md