You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by pi...@apache.org on 2026/08/24 13:23:50 UTC

(iggy-website) branch main updated: Iggy TLP blog post

This is an automated email from the ASF dual-hosted git repository.

spetz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iggy-website.git


The following commit(s) were added to refs/heads/main by this push:
     new 23364c33 Iggy TLP blog post
23364c33 is described below

commit 23364c33ca22245a9e07d07ee279506e6d55ee93
Author: spetz <pi...@gmail.com>
AuthorDate: Mon Aug 24 15:23:41 2026 +0200

    Iggy TLP blog post
---
 ...pache-iggy-top-level-project-tlp-graduation.mdx |  83 ++++++++++++++++++
 .../iggy-tlp-stats.png                             | Bin 0 -> 369263 bytes
 public/fonts/Montserrat-OFL.txt                    |  93 +++++++++++++++++++++
 public/fonts/Montserrat-Variable.ttf               | Bin 0 -> 744936 bytes
 public/fonts/OpenSans-OFL.txt                      |  92 ++++++++++++++++++++
 public/fonts/OpenSans-Variable.ttf                 | Bin 0 -> 532636 bytes
 public/img/authors/kranti.png                      | Bin 0 -> 164024 bytes
 public/img/authors/piotr.png                       | Bin 16348 -> 196111 bytes
 src/app/(site)/blogs/[...slug]/page.tsx            |  40 ++++++++-
 src/app/(site)/blogs/page.tsx                      |   8 +-
 src/lib/authors.ts                                 |  76 +++++++++++++++++
 11 files changed, 387 insertions(+), 5 deletions(-)

diff --git a/content/blog/apache-iggy-top-level-project-tlp-graduation.mdx b/content/blog/apache-iggy-top-level-project-tlp-graduation.mdx
new file mode 100644
index 00000000..fa9f30bc
--- /dev/null
+++ b/content/blog/apache-iggy-top-level-project-tlp-graduation.mdx
@@ -0,0 +1,83 @@
+---
+title: Apache Iggy™ Graduates to a Top-Level Project
+author: piotr, kranti
+tags: ["tlp", "top level project", "graduation", "announcement"]
+date: 2026-08-24
+---
+## Apache Iggy™
+
+We've got some amazing news to share: as of August 19th, 2026, Apache Iggy has officially graduated after a unanimously positive [vote](https://lists.apache.org/thread/dsnzqldshkg104n0t2g2q1czbz7tcbz6) from the Apache Incubator and is now an **Apache Software Foundation Top-Level Project (TLP)**. This is a special milestone for us.
+
+What started more than three years ago as a small experiment to learn Rust and explore the internals of message streaming has grown into an independent Apache project with contributors and users from around the world. And somehow, the journey from joining the Apache Incubator to becoming a TLP took only about **a year and a half**.
+
+## The origins
+
+It all began in March 2023 out of **Piotr Gankiewicz’s** pure curiosity. There was no corporate mandate or missing tool driving it - just an engineering desire to learn messaging internals and finally pick up Rust for real. Sure, the market was already flooded with message streaming tools like Apache Kafka, but that didn't matter. Engineers still build new database engines all the time, driven by an obsession with latency, performance, efficiency and operational simplicity - and a simple [...]
+
+The initial plan was simple: build a basic append-only log server, get comfortable with Rust, and call it a day. But it didn't stay simple. Performance is a deep rabbit hole - developers quickly find themselves constantly optimizing, modularizing, and chasing efficiency. Fast-forward to today, and Iggy is a persistent streaming platform built on a **thread-per-core** design with **io_uring** for disk/network I/O, **[VSR](https://github.com/apache/iggy/blob/master/assets/vsr.pdf)** (Views [...]
+
+But technology is only part of the story. **The bigger change happened when other people started contributing.**
+
+The moment someone you’ve never met spends their own time submitting a pull request, reporting an issue, improving documentation, testing something or simply participating in a technical discussion, a personal experiment starts becoming something else. It becomes a **community**.
+
+Contributors came and went. Some submitted a single PR. Others stayed and eventually became committers and members of the project. Creating our [Discord](https://discord.gg/apache-iggy) community made it easier for people to ask questions, propose ideas and interact with developers. GitHub remained the place where technical decisions and project work could happen openly and remain searchable.
+
+As described in our [2023 post about building message streaming in Rust](https://iggy.apache.org/blogs/2023/12/29/building-message-streaming-in-rust/), the project had already transformed from a “solo side project to complete in three weeks” into something much bigger.
+
+## Joining the Apache Incubator
+
+As the community grew, we faced a more important question: **How do we make sure Iggy remains truly open source for the long term?**
+
+We wanted contributors and users to know that the project’s future wouldn’t depend on a single individual or company, and that its license couldn’t suddenly change because someone’s commercial strategy changed. The Apache Software Foundation was a natural home.
+
+In early 2025, we [proposed](https://cwiki.apache.org/confluence/spaces/INCUBATOR/pages/340036264/Iggy+Proposal) Apache Iggy to the Apache Incubator, and LaserData Inc. transferred ownership of the Iggy source code to the Apache Software Foundation through the ASF Software Grant process. That transition changed much more than the project’s name. 
+
+There was no longer an “owner” of Iggy making unilateral decisions. The project would operate through Apache’s community governance: public discussions, votes, releases, committers, PPMC members and consensus. Learning to operate this way was an important part of incubation.
+
+**Kranti Parisa** led much of Iggy’s journey into the Apache Software Foundation, from helping drive the original Incubator proposal and onboarding process through the project’s graduation. With graduation, Kranti has been appointed Chair of the Apache Iggy Project Management Committee (Iggy PMC). The Chair is not the leader of an Apache project (the PMC collectively governs it), but serves as the project’s liaison with the ASF Board and helps ensure the health and governance of the project.
+
+We also owe a special thank you to **Yonik Seeley**, our Apache Champion and Mentor, who helped us navigate the path into the ASF and supported the community throughout incubation. And to our mentors **Hulk Lin**, **Zili Chen**, and **Hao Ding**, whose guidance around Apache governance, releases and community building helped us reach this milestone.
+
+## From a podling to a community
+
+During roughly 18 months in the Apache Incubator, Iggy grew to:
+
+* more than **4,500 GitHub stars**
+* more than **120 contributors**
+* nearly **3,000 pull requests**
+* more than **700 members** in the broader Discord community
+* more than **500K cumulative downloads**
+
+Those numbers are exciting, but graduation isn’t really about GitHub stars or download counts. It’s about whether a project has developed a healthy, diverse and self-governing community capable of sustaining itself.
+
+![Apache Iggy graduates to a Top Level Project - 4500+ GitHub stars, 120+ contributors, 2700+ pull requests, 700+ Discord members and 500K+ cumulative downloads](/apache-iggy-top-level-project-tlp-graduation/iggy-tlp-stats.png)
+
+Our PPMC members became PMC members. Contributors became committers. New people joined discussions, reviewed code, proposed features, helped with releases, tested the software and challenged technical decisions. The complete Apache Iggy community, including PMC members, mentors and committers, is listed on our [Team](https://iggy.apache.org/community/team/) page.
+
+And there are many more contributors and users, across the world, beyond that list who have helped through code, documentation, issues, discussions, testing, integrations and community support.
+
+**Apache Iggy exists because of all of them.**
+
+Less than three and a half years after the first line of Iggy was written, and about a year and a half after entering the Apache Incubator, **we’re now an Apache Top-Level Project**.
+
+## What's next
+
+Graduation isn’t the finish line. If anything, it’s the beginning of Iggy’s next chapter.
+
+There are several important technical areas we’re working on and discussing with the community:
+* [Release](https://github.com/apache/iggy/discussions/3885) the next version of Iggy with one of our most requested capabilities: **clustering** based on [VSR](https://github.com/apache/iggy/blob/master/assets/vsr.pdf), bringing Iggy closer to highly available production deployments.
+* Complete the initial **[Kafka protocol gateway](https://github.com/apache/iggy/discussions/3253)**, making it easier for existing applications and ecosystems to work with Iggy.
+* Explore multi-leader replication per partition, tiered storage, message registry and embedded KV capabilities.
+* Continue expanding the connector runtime, including additional sources, sinks and optimizations.
+* And, perhaps most importantly, keep experimenting with how far we can push modern hardware, operating systems and Rust to build faster and more efficient streaming infrastructure.
+
+The roadmap will continue to evolve through community discussion - as it should.
+
+## Thank you
+Apache Iggy becoming a Top-Level Project is an achievement that belongs to everyone who helped get it here. Thank you and congratulations to the entire Apache Iggy community.
+
+Let's enter the **[Third Wave](https://www.streamingdata.tech/i/203596806/apache-iggy)** of message streaming technology together. It is an incredible time to build, especially as real-time data becomes the foundation for a new generation of AI and systems of intelligence.
+
+-\
+Piotr Gankiewicz & Kranti Parisa\
+Apache Iggy PMC.
diff --git a/public/apache-iggy-top-level-project-tlp-graduation/iggy-tlp-stats.png b/public/apache-iggy-top-level-project-tlp-graduation/iggy-tlp-stats.png
new file mode 100644
index 00000000..c2fc1917
Binary files /dev/null and b/public/apache-iggy-top-level-project-tlp-graduation/iggy-tlp-stats.png differ
diff --git a/public/fonts/Montserrat-OFL.txt b/public/fonts/Montserrat-OFL.txt
new file mode 100644
index 00000000..fe576a2e
--- /dev/null
+++ b/public/fonts/Montserrat-OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2024 The Montserrat.Git Project Authors (https://github.com/JulietaUla/Montserrat.git)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/public/fonts/Montserrat-Variable.ttf b/public/fonts/Montserrat-Variable.ttf
new file mode 100644
index 00000000..c97aca18
Binary files /dev/null and b/public/fonts/Montserrat-Variable.ttf differ
diff --git a/public/fonts/OpenSans-OFL.txt b/public/fonts/OpenSans-OFL.txt
new file mode 100644
index 00000000..d762c3c4
--- /dev/null
+++ b/public/fonts/OpenSans-OFL.txt
@@ -0,0 +1,92 @@
+Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/OpenSans-Variable.ttf b/public/fonts/OpenSans-Variable.ttf
new file mode 100644
index 00000000..9db85693
Binary files /dev/null and b/public/fonts/OpenSans-Variable.ttf differ
diff --git a/public/img/authors/kranti.png b/public/img/authors/kranti.png
new file mode 100644
index 00000000..bfe50d8e
Binary files /dev/null and b/public/img/authors/kranti.png differ
diff --git a/public/img/authors/piotr.png b/public/img/authors/piotr.png
index 9e5df282..546d1f48 100644
Binary files a/public/img/authors/piotr.png and b/public/img/authors/piotr.png differ
diff --git a/src/app/(site)/blogs/[...slug]/page.tsx b/src/app/(site)/blogs/[...slug]/page.tsx
index ca1e3ebe..70c9699a 100644
--- a/src/app/(site)/blogs/[...slug]/page.tsx
+++ b/src/app/(site)/blogs/[...slug]/page.tsx
@@ -18,6 +18,7 @@
  */
 
 import { blogPosts } from "@/lib/source";
+import { resolveAuthors } from "@/lib/authors";
 import { notFound } from "next/navigation";
 import { getMDXComponents } from "@/mdx-components";
 import Link from "next/link";
@@ -58,6 +59,7 @@ export default async function BlogPost(props: {
 
   const MDX = page.body;
   const date = new Date(page.date);
+  const postAuthors = resolveAuthors(page.author);
 
   return (
     <main className="min-h-screen px-6 py-16 md:px-12">
@@ -73,10 +75,40 @@ export default async function BlogPost(props: {
           <h1 className="mt-3 text-3xl font-extrabold tracking-tight text-fd-foreground md:text-5xl">
             {page.title}
           </h1>
-          {page.author && (
-            <p className="mt-4 text-base text-fd-muted-foreground">
-              By {page.author}
-            </p>
+          {postAuthors.length > 0 && (
+            <div className="mt-6 flex flex-wrap gap-x-10 gap-y-4">
+              {postAuthors.map((author) => (
+                <div key={author.name} className="flex items-center gap-3">
+                  {/* eslint-disable-next-line @next/next/no-img-element */}
+                  <img
+                    src={author.image}
+                    alt={author.name}
+                    className="h-11 w-11 rounded-full border border-fd-border object-cover"
+                  />
+                  <div>
+                    {author.url ? (
+                      <a
+                        href={author.url}
+                        target="_blank"
+                        rel="noreferrer"
+                        className="text-sm font-semibold text-fd-foreground transition-colors hover:text-fd-primary"
+                      >
+                        {author.name}
+                      </a>
+                    ) : (
+                      <span className="text-sm font-semibold text-fd-foreground">
+                        {author.name}
+                      </span>
+                    )}
+                    {author.title && (
+                      <p className="text-xs text-fd-muted-foreground">
+                        {author.title}
+                      </p>
+                    )}
+                  </div>
+                </div>
+              ))}
+            </div>
           )}
           <div className="mt-8 h-px bg-fd-border" />
         </header>
diff --git a/src/app/(site)/blogs/page.tsx b/src/app/(site)/blogs/page.tsx
index 50bcc066..078bc797 100644
--- a/src/app/(site)/blogs/page.tsx
+++ b/src/app/(site)/blogs/page.tsx
@@ -19,6 +19,7 @@
 
 import Link from "next/link";
 import { blogPosts } from "@/lib/source";
+import { resolveAuthors } from "@/lib/authors";
 import type { Metadata } from "next";
 
 export const metadata: Metadata = {
@@ -70,7 +71,12 @@ export default function BlogIndex() {
                       day: "numeric",
                     })}
                     {post.author && (
-                      <span className="ml-2 opacity-70">by {post.author}</span>
+                      <span className="ml-2 opacity-70">
+                        by{" "}
+                        {resolveAuthors(post.author)
+                          .map((author) => author.name)
+                          .join(" & ")}
+                      </span>
                     )}
                   </time>
                   <h2 className="mt-2 text-2xl font-bold tracking-tight text-fd-foreground transition-colors group-hover:text-fd-primary md:text-3xl">
diff --git a/src/lib/authors.ts b/src/lib/authors.ts
new file mode 100644
index 00000000..06b21d0f
--- /dev/null
+++ b/src/lib/authors.ts
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface Author {
+  name: string;
+  title?: string;
+  url?: string;
+  image: string;
+}
+
+const FALLBACK_IMAGE = "/img/Avatar-Placeholder.svg";
+
+export const authors: Record<string, Author> = {
+  piotr: {
+    name: "Piotr Gankiewicz",
+    title: "Apache Iggy Founder & PMC Member",
+    url: "https://github.com/spetz",
+    image: "/img/authors/piotr.png",
+  },
+  kranti: {
+    name: "Kranti Parisa",
+    title: "Apache Iggy PMC Chair",
+    url: "https://github.com/kparisa",
+    image: "/img/authors/kranti.png",
+  },
+  grzegorz: {
+    name: "Grzegorz Koszyk",
+    title: "Apache Iggy PMC Member",
+    url: "https://github.com/numinnex",
+    image: "/img/authors/grzegorz.png",
+  },
+  hubert: {
+    name: "Hubert Gruszecki",
+    title: "Apache Iggy PMC Member",
+    url: "https://github.com/hubcio",
+    image: "/img/authors/hubert.png",
+  },
+  krishna: {
+    name: "Krishna Vishal Vemula",
+    title: "Apache Iggy Committer",
+    url: "https://github.com/krishvishal",
+    image: "/img/authors/krishna.png",
+  },
+};
+
+/**
+ * Resolves a frontmatter `author` value (a single key or a comma-separated
+ * list, e.g. "piotr, kranti") into full author entries. Unknown keys fall
+ * back to a plain entry so legacy values like "Apache Iggy" still render.
+ */
+export function resolveAuthors(author: string | undefined): Author[] {
+  if (!author) return [];
+  return author
+    .split(",")
+    .map((key) => key.trim())
+    .filter(Boolean)
+    .map(
+      (key) => authors[key] ?? { name: key, image: FALLBACK_IMAGE },
+    );
+}