diff --git a/.github/release-drafter-gateway.yml b/.github/release-drafter-gateway.yml
index 44db9dbb3..9de5c1d26 100644
--- a/.github/release-drafter-gateway.yml
+++ b/.github/release-drafter-gateway.yml
@@ -52,4 +52,5 @@ exclude-labels:
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
template: |
- CHANGE_ME
+ Please see our [changelog](https://www.firezone.dev/changelog?utm_source=github-releases)
+ for more details.
diff --git a/.github/release-drafter-gui-client.yml b/.github/release-drafter-gui-client.yml
index 4192c3ff5..8be0c18a9 100644
--- a/.github/release-drafter-gui-client.yml
+++ b/.github/release-drafter-gui-client.yml
@@ -52,4 +52,5 @@ exclude-labels:
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
template: |
- CHANGE_ME
+ Please see our [changelog](https://www.firezone.dev/changelog?utm_source=github-releases)
+ for more details.
diff --git a/.github/release-drafter-headless-client.yml b/.github/release-drafter-headless-client.yml
index 8a8bed7af..cfc928418 100644
--- a/.github/release-drafter-headless-client.yml
+++ b/.github/release-drafter-headless-client.yml
@@ -52,4 +52,5 @@ exclude-labels:
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
template: |
- CHANGE_ME
+ Please see our [changelog](https://www.firezone.dev/changelog?utm_source=github-releases)
+ for more details.
diff --git a/website/src/app/changelog/layout.tsx b/website/src/app/changelog/layout.tsx
new file mode 100644
index 000000000..b69bf2b33
--- /dev/null
+++ b/website/src/app/changelog/layout.tsx
@@ -0,0 +1,31 @@
+import Image from "next/image";
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+ Changelog
+
+
+ A list of the most recent updates to Firezone, organized by
+ component.
+
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/website/src/app/changelog/page.tsx b/website/src/app/changelog/page.tsx
new file mode 100644
index 000000000..113f090d7
--- /dev/null
+++ b/website/src/app/changelog/page.tsx
@@ -0,0 +1,11 @@
+import { Metadata } from "next";
+import Changelog from "@/components/Changelog";
+
+export const metadata: Metadata = {
+ title: "Changelog • Firezone",
+ description: "A list of the most recent updates to Firezone.",
+};
+
+export default function Page() {
+ return ;
+}
diff --git a/website/src/app/docs/administer/backup/readme.mdx b/website/src/app/docs/administer/backup/readme.mdx
index fd7eba237..f7507290b 100644
--- a/website/src/app/docs/administer/backup/readme.mdx
+++ b/website/src/app/docs/administer/backup/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
import SupportOptions from "@/components/SupportOptions";
diff --git a/website/src/app/docs/administer/regen-keys/readme.mdx b/website/src/app/docs/administer/regen-keys/readme.mdx
index b5f160efc..3e000c43e 100644
--- a/website/src/app/docs/administer/regen-keys/readme.mdx
+++ b/website/src/app/docs/administer/regen-keys/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Regenerate Secret Keys
diff --git a/website/src/app/docs/administer/troubleshoot/readme.mdx b/website/src/app/docs/administer/troubleshoot/readme.mdx
index c25f7c217..5acef21a7 100644
--- a/website/src/app/docs/administer/troubleshoot/readme.mdx
+++ b/website/src/app/docs/administer/troubleshoot/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import SupportOptions from "@/components/SupportOptions";
# Troubleshooting Guide
diff --git a/website/src/app/docs/administer/uninstall/readme.mdx b/website/src/app/docs/administer/uninstall/readme.mdx
index 965114f61..4ff84f901 100644
--- a/website/src/app/docs/administer/uninstall/readme.mdx
+++ b/website/src/app/docs/administer/uninstall/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Uninstall Firezone
diff --git a/website/src/app/docs/administer/upgrade/readme.mdx b/website/src/app/docs/administer/upgrade/readme.mdx
index 81b2db075..aa073996a 100644
--- a/website/src/app/docs/administer/upgrade/readme.mdx
+++ b/website/src/app/docs/administer/upgrade/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
import Image from "next/image";
diff --git a/website/src/app/docs/authenticate/saml/readme.mdx b/website/src/app/docs/authenticate/saml/readme.mdx
index 544a10e19..0a724b8b9 100644
--- a/website/src/app/docs/authenticate/saml/readme.mdx
+++ b/website/src/app/docs/authenticate/saml/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
# Integrate your identity provider using SAML 2.0
diff --git a/website/src/app/docs/deploy/advanced/external-database/readme.mdx b/website/src/app/docs/deploy/advanced/external-database/readme.mdx
index 8436c29d4..e5aa1581d 100644
--- a/website/src/app/docs/deploy/advanced/external-database/readme.mdx
+++ b/website/src/app/docs/deploy/advanced/external-database/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Custom External Database
diff --git a/website/src/app/docs/deploy/configure/readme.mdx b/website/src/app/docs/deploy/configure/readme.mdx
index c8198623e..a06c4204a 100644
--- a/website/src/app/docs/deploy/configure/readme.mdx
+++ b/website/src/app/docs/deploy/configure/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
# Configure Firezone
diff --git a/website/src/app/docs/deploy/readme.mdx b/website/src/app/docs/deploy/readme.mdx
index 1db068715..8c1b7b0f7 100644
--- a/website/src/app/docs/deploy/readme.mdx
+++ b/website/src/app/docs/deploy/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Deploy Firezone
diff --git a/website/src/app/docs/deploy/security-considerations/readme.mdx b/website/src/app/docs/deploy/security-considerations/readme.mdx
index 67dbe7d24..843f21de9 100644
--- a/website/src/app/docs/deploy/security-considerations/readme.mdx
+++ b/website/src/app/docs/deploy/security-considerations/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Security considerations
diff --git a/website/src/app/docs/reference/file-and-directory-locations/readme.mdx b/website/src/app/docs/reference/file-and-directory-locations/readme.mdx
index bd61d145f..deef809c3 100644
--- a/website/src/app/docs/reference/file-and-directory-locations/readme.mdx
+++ b/website/src/app/docs/reference/file-and-directory-locations/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
# File and Directory Locations
diff --git a/website/src/app/docs/reference/telemetry/readme.mdx b/website/src/app/docs/reference/telemetry/readme.mdx
index bc760df55..701504f7c 100644
--- a/website/src/app/docs/reference/telemetry/readme.mdx
+++ b/website/src/app/docs/reference/telemetry/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Telemetry
diff --git a/website/src/app/kb/administer/logs/readme.mdx b/website/src/app/kb/administer/logs/readme.mdx
index 0159cf565..5373027f1 100644
--- a/website/src/app/kb/administer/logs/readme.mdx
+++ b/website/src/app/kb/administer/logs/readme.mdx
@@ -1,4 +1,4 @@
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
# Viewing logs
diff --git a/website/src/app/kb/administer/troubleshooting/readme.mdx b/website/src/app/kb/administer/troubleshooting/readme.mdx
index 7389868ab..787381b22 100644
--- a/website/src/app/kb/administer/troubleshooting/readme.mdx
+++ b/website/src/app/kb/administer/troubleshooting/readme.mdx
@@ -1,5 +1,5 @@
import SupportOptions from "@/components/SupportOptions";
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Image from "next/image";
# Troubleshooting Guide
diff --git a/website/src/app/kb/administer/uninstall/readme.mdx b/website/src/app/kb/administer/uninstall/readme.mdx
index 3876e6a7e..35eed8459 100644
--- a/website/src/app/kb/administer/uninstall/readme.mdx
+++ b/website/src/app/kb/administer/uninstall/readme.mdx
@@ -1,5 +1,5 @@
import SupportOptions from "@/components/SupportOptions";
-import { TabsGroup, TabsItem } from "@/components/DocsTabs";
+import { TabsGroup, TabsItem } from "@/components/Tabs";
import Alert from "@/components/DocsAlert";
# Uninstall Firezone Gateway
diff --git a/website/src/app/kb/administer/upgrading/readme.mdx b/website/src/app/kb/administer/upgrading/readme.mdx
index de8e71c9b..997fbf79b 100644
--- a/website/src/app/kb/administer/upgrading/readme.mdx
+++ b/website/src/app/kb/administer/upgrading/readme.mdx
@@ -1,6 +1,6 @@
import { HiCheck } from "react-icons/hi2";
import Link from "next/link";
-import { TabsItem, TabsGroup } from "@/components/DocsTabs";
+import { TabsItem, TabsGroup } from "@/components/Tabs";
import Image from "next/image";
import Alert from "@/components/DocsAlert";
import SupportOptions from "@/components/SupportOptions";
diff --git a/website/src/app/pricing/_page.tsx b/website/src/app/pricing/_page.tsx
index 2596276bf..45ca67be5 100644
--- a/website/src/app/pricing/_page.tsx
+++ b/website/src/app/pricing/_page.tsx
@@ -232,7 +232,7 @@ export default function _Page() {
- Directory sync for Google, Entra ID, and Okta
+ Directory sync for Google, Entra ID, Okta, and JumpCloud
diff --git a/website/src/components/Changelog/Android.tsx b/website/src/components/Changelog/Android.tsx
new file mode 100644
index 000000000..c31617e04
--- /dev/null
+++ b/website/src/components/Changelog/Android.tsx
@@ -0,0 +1,28 @@
+import Entries from "./Entries";
+import Entry from "./Entry";
+
+export default function Android() {
+ return (
+
+
+ This release fixes a bug where the incorrect Client version could be
+ reported to the admin portal.
+
+
+ This release contains connectivity bugfixes and performance
+ improvements.
+
+
+ This release reverts a change that could cause connectivity issues in
+ some cases.
+
+
+ This release contains reliability and performance fixes and is
+ recommended for all users.
+
+
+ Initial release.
+
+
+ );
+}
diff --git a/website/src/components/Changelog/Apple.tsx b/website/src/components/Changelog/Apple.tsx
new file mode 100644
index 000000000..289b0f16c
--- /dev/null
+++ b/website/src/components/Changelog/Apple.tsx
@@ -0,0 +1,28 @@
+import Entry from "./Entry";
+import Entries from "./Entries";
+
+export default function Apple() {
+ return (
+
+
+ This release introduces new Resource status updates in the Resource
+ list.
+
+
+ Bug fixes.
+
+
+ Bug fixes.
+
+
+ Bug fixes.
+
+
+ Bug fixes.
+
+
+ Initial release.
+
+
+ );
+}
diff --git a/website/src/components/Changelog/Entries.tsx b/website/src/components/Changelog/Entries.tsx
new file mode 100644
index 000000000..4ff39d4ac
--- /dev/null
+++ b/website/src/components/Changelog/Entries.tsx
@@ -0,0 +1,109 @@
+import React from "react";
+import Entry from "./Entry";
+
+function Latest({
+ title,
+ version,
+ date,
+ children,
+}: {
+ title: string;
+ version: string;
+ date: Date;
+ children: React.ReactNode;
+}) {
+ const options: Intl.DateTimeFormatOptions = {
+ timeZone: "UTC",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ };
+ const utcDateString = date.toLocaleDateString("en-US", options);
+ return (
+ <>
+
+ Latest {title} version
+
+
+
+ Version: {version}
+
+
+ Released:{" "}
+
+ {utcDateString}
+
+
+ {children}
+
+ >
+ );
+}
+
+function Previous({
+ title,
+ children,
+}: {
+ title: string;
+ children: React.ReactNode;
+}) {
+ return (
+ <>
+
+ Previous {title} versions
+
+
+
+
+
+ Version
+
+
+ Date
+
+
+ Description
+
+
+
+ {children}
+
+ >
+ );
+}
+
+export default function Entries({
+ title,
+ children,
+}: {
+ title: string;
+ children: React.ReactNode;
+}) {
+ const childrenArray = React.Children.toArray(children);
+ const firstEntry = childrenArray[0];
+ const previousEntries = childrenArray.slice(1);
+
+ if (!React.isValidElement(firstEntry)) {
+ throw new Error("First child is not a valid React element");
+ }
+
+ const { version, date, children: firstEntryChildren } = firstEntry.props;
+
+ return (
+
+
+ {firstEntryChildren}
+
+
{previousEntries}
+
+ );
+}
diff --git a/website/src/components/Changelog/Entry.tsx b/website/src/components/Changelog/Entry.tsx
new file mode 100644
index 000000000..3d3a1b17d
--- /dev/null
+++ b/website/src/components/Changelog/Entry.tsx
@@ -0,0 +1,30 @@
+export default function Entry({
+ version,
+ date = new Date(),
+ children,
+}: {
+ version: string;
+ date: Date;
+ children: React.ReactNode;
+}) {
+ const options: Intl.DateTimeFormatOptions = {
+ timeZone: "UTC",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ };
+ const utcDateString = date.toLocaleDateString("en-US", options);
+ return (
+
+
+ {version}
+
+
+ {utcDateString}
+
+
+ {children}
+
+
+ );
+}
diff --git a/website/src/components/Changelog/GUI.tsx b/website/src/components/Changelog/GUI.tsx
new file mode 100644
index 000000000..73329c7f1
--- /dev/null
+++ b/website/src/components/Changelog/GUI.tsx
@@ -0,0 +1,46 @@
+import Entry from "./Entry";
+import Entries from "./Entries";
+
+export default function GUI({ title }: { title: string }) {
+ return (
+
+
+ This release simplifies the Resource connected state icons in the menu
+ to prevent issues with certain Linux distributions.
+
+
+ Fixes an issue in Windows that could cause the Wintun Adapter to fail to
+ be created under certain conditions.
+
+
+ This release fixes a bug where the incorrect Client version was reported
+ to the admin portal.
+
+
+ This release contains connectivity fixes and performance improvements
+ and is recommended for all users.
+
+
+ This release adds an IPC service for Windows to allow for better process
+ isolation.
+
+
+ This release fixes a bug on Windows where system DNS could break after
+ the Firezone Client was closed.
+
+
+ Maintenance release.
+
+
+ This release reverts a change that could cause connectivity issues seen
+ by some users.
+
+
+ Update the upgrade URLs used to check for new versions.
+
+
+ Initial release.
+
+
+ );
+}
diff --git a/website/src/components/Changelog/Gateway.tsx b/website/src/components/Changelog/Gateway.tsx
new file mode 100644
index 000000000..ba113df49
--- /dev/null
+++ b/website/src/components/Changelog/Gateway.tsx
@@ -0,0 +1,68 @@
+import Entry from "./Entry";
+import Entries from "./Entries";
+import Link from "next/link";
+
+export default function Gateway() {
+ return (
+
+
+
+ This release introduces a new method of resolving and routing DNS
+ Resources that is more reliable on some poorly-behaved networks. To
+ use this new method, Client versions 1.1.0 or later are required.
+ Client versions 1.0.x will continue to work with Gateway 1.1.x, but
+ will not benefit from the new DNS resolution method.
+
+
+ Read more about this change in the announcement post{" "}
+
+ here
+
+ .
+
+
+
+ This is a maintenance release with no major user-facing changes.
+
+
+ This release fixes a bug where the incorrect Gateway version could be
+ reported to the admin portal.
+
+
+ This release contains connectivity fixes and performance improvements
+ and is recommended for all users.
+
+
+ Minor maintenance fixes.
+
+
+ Fixes an issue detecting the correct architecture during installation
+ and upgrades.
+
+
+ Adds support for{" "}
+
+ traffic restrictions
+
+ .
+
+
+ Fixes a big that caused invalid connections from being cleaned up
+ properly.
+
+
+ Fixes a bug that could prevent the auto-upgrade script from working
+ properly.
+
+
+ Initial release.
+
+
+ );
+}
diff --git a/website/src/components/Changelog/Headless.tsx b/website/src/components/Changelog/Headless.tsx
new file mode 100644
index 000000000..93c0575bc
--- /dev/null
+++ b/website/src/components/Changelog/Headless.tsx
@@ -0,0 +1,39 @@
+import Entry from "./Entry";
+import Entries from "./Entries";
+
+export default function Headless() {
+ return (
+
+
+ This is a maintenance release with no major user-facing changes.
+
+
+ This release fixes a bug where the incorrect Client version was reported
+ to the admin portal.
+
+
+ This release contains connectivity fixes and performance improvements
+ and is recommended for all users.
+
+
+ This is a maintenance release with no major user-facing changes.
+
+
+ This is a maintenance release with no major user-facing changes.
+
+
+ Maintenance release.
+
+
+ This release reverts a change that could cause connectivity issues seen
+ by some users.
+
+
+ Update the upgrade URLs used to check for new versions.
+
+
+ Initial release.
+
+
+ );
+}
diff --git a/website/src/components/Changelog/index.tsx b/website/src/components/Changelog/index.tsx
new file mode 100644
index 000000000..ca0253d80
--- /dev/null
+++ b/website/src/components/Changelog/index.tsx
@@ -0,0 +1,37 @@
+"use client";
+
+import { TabsGroup, TabsItem } from "@/components/Tabs";
+import Android from "./Android";
+import Apple from "./Apple";
+import Gateway from "./Gateway";
+import GUI from "./GUI";
+import Headless from "./Headless";
+import { HiServerStack } from "react-icons/hi2";
+import { FaApple, FaAndroid, FaWindows, FaLinux } from "react-icons/fa";
+
+export default function Changelog() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/components/Footer/index.tsx b/website/src/components/Footer/index.tsx
index b8538f254..6d00f18e7 100644
--- a/website/src/components/Footer/index.tsx
+++ b/website/src/components/Footer/index.tsx
@@ -47,21 +47,29 @@ export default function Footer() {
Company
-
-
- About
-
-
Home
+
+
+ About
+
+
Pricing
+
+
+ Roadmap
+
+
Blog
@@ -92,18 +100,26 @@ export default function Footer() {
- Trust Center
+ Support
- Roadmap
+ Changelog
+
+
+
+
+ Trust Center
diff --git a/website/src/components/RootLayout/index.tsx b/website/src/components/RootLayout/index.tsx
index 86a57cdf0..04d1ed3e0 100644
--- a/website/src/components/RootLayout/index.tsx
+++ b/website/src/components/RootLayout/index.tsx
@@ -33,7 +33,7 @@ export default function RootLayout({
src="https://app.termly.io/resource-blocker/c4df1a31-22d9-4000-82e6-a86cbec0bba0?autoBlock=on"
/>
-
+
Firezone 1.0 is here!{" "}
diff --git a/website/src/components/DocsTabs/index.tsx b/website/src/components/Tabs/index.tsx
similarity index 83%
rename from website/src/components/DocsTabs/index.tsx
rename to website/src/components/Tabs/index.tsx
index 6514577d8..c79249e9b 100644
--- a/website/src/components/DocsTabs/index.tsx
+++ b/website/src/components/Tabs/index.tsx
@@ -79,16 +79,34 @@ function TabsGroup({ children }: { children: React.ReactNode }) {
function TabsItem({
children,
title,
+ icon,
...props
}: {
children: React.ReactNode;
title: string;
+ icon?: FlowbiteIcon;
}) {
return (
-
+
{children}
);
}
export { TabsGroup, TabsItem };
+
+// Nastiness needed because of Flowbite Typescript
+// See https://github.com/themesberg/flowbite-react/issues/1359
+export type IconSVGProps = React.PropsWithoutRef<
+ React.SVGProps
+> &
+ React.RefAttributes;
+export type FlowbiteIconProps = IconSVGProps & {
+ title?: string;
+ titleId?: string;
+};
+
+export type FlowbiteIcon = React.FC<
+ Omit, "ref">
+> &
+ FlowbiteIconProps;