Skip to content

Add “Building a federated blog” tutorial (Astro + Bun + SQLite)#695

Open
dahlia wants to merge 35 commits intofedify-dev:mainfrom
dahlia:tutorial/astro-blog
Open

Add “Building a federated blog” tutorial (Astro + Bun + SQLite)#695
dahlia wants to merge 35 commits intofedify-dev:mainfrom
dahlia:tutorial/astro-blog

Conversation

@dahlia
Copy link
Copy Markdown
Member

@dahlia dahlia commented Apr 18, 2026

Closes #691.

Read it at https://d180af62.fedify.pages.dev/tutorial/astro-blog.

Background

The existing microblog tutorial demonstrates building a multi-user social server with Hono and Node.js. This new tutorial covers a different workflow: layering ActivityPub federation onto a content-first blog where posts are authored as static Markdown files in Astro content collections and served via Astro's server-side rendering (SSR) mode.

The target audience is developers who already have a blog (or want one) and want to make it federable—not ActivityPub implementors building a general-purpose social server.

What's included

A new end-to-end tutorial at docs/tutorial/astro-blog.md (≈ 2,900 lines, 10 chapters) and a companion example project at https://github.com/fedify-dev/astro-blog, with each chapter corresponding to a tagged commit in the example repo.

The tutorial covers:

  • Scaffolding with fedify init and Astro's content collections
  • Implementing a Person actor with Ed25519 + RSA-PKCS1-v1.5 key pairs
  • Exposing the server through a Cloudflare tunnel and discovering the actor on ActivityPub.Academy
  • Implementing Follow/Undo(Follow) inbox handlers and a followers collection
  • Migrating from an in-memory Map to bun:sqlite for key pairs and followers
  • A startup sync that diffs Astro content collections against a posts SQLite table and fans out Create(Article), Update(Article), or Delete(Article) to all followers
  • Receiving Create(Note)/Update(Note)/Delete(Note) inbox activities as post comments and displaying them on the post page

Supporting changes:

  • Navigation entry added to docs/.vitepress/config.mts
  • bun-types added to proper_nouns in .hongdown.toml
  • CHANGES.md entry under the unreleased section
  • .playwright-mcp/** added to fmt.exclude in deno.json (Playwright MCP auto-generates screenshots into this directory; they should not be reformatted by deno fmt)
  • .playwright-mcp/ added to .gitignore for the same reason

dahlia added 22 commits April 18, 2026 19:59
Adds the first two chapters of the new "Building a federated blog"
tutorial (docs/tutorial/astro-blog.md):

- Chapter 1 (Introduction): describes what we're building, the
  target audience, and the list of features and limitations.
- Chapter 2 (Setting up): walks through installing Bun and the
  fedify CLI, running fedify init with the Astro + Bun + in-memory
  options, and verifying the project works with fedify lookup.

Also adds the tutorial to the navigation in docs/.vitepress/config.mts
so it appears in the sidebar.

The companion example repository is at:
https://github.com/fedify-dev/astro-blog

Assisted-by: Claude Code:claude-sonnet-4-6
Adds chapter 3 "Building the blog" to docs/tutorial/astro-blog.md.
This chapter walks through:

- Defining a content collection with Astro's Content Layer API
  (defineCollection + glob() loader) and a Zod schema.
- Creating three sample Markdown posts in src/content/posts/.
- Writing a minimal Layout.astro with global CSS that readers can
  copy-paste without needing an external CSS framework.
- Implementing the blog listing page (src/pages/index.astro) using
  getCollection() and the filter/sort pattern.
- Implementing the individual post page using dynamic routing and
  the render() helper.

Also includes two screenshots of the working blog UI taken with
Playwright.

Also excludes .playwright-mcp/ from deno fmt and git tracking.

Assisted-by: Claude Code:claude-sonnet-4-6
…rial

Covers the in-memory key pair store (globalThis trick for Astro HMR),
the actor dispatcher returning a Person with RSA + Ed25519 keys, inbox
and followers stub dispatchers, content negotiation via @fedify/astro
middleware, and the HTML profile page at /users/blog.  Includes a
screenshot of the actor profile page and instructions for verifying with
fedify lookup.

Assisted-by: Claude Code:claude-sonnet-4-6
Covers using fedify tunnel to expose the local dev server publicly,
the X-Forwarded-Proto fix needed so actor URIs use https://, configuring
Vite's allowedHosts for tunnel hostnames, and verifying the actor is
discoverable from Mastodon's search interface.

Assisted-by: Claude Code:claude-sonnet-4-6
Covers Follow/Undo inbox handlers with auto-accept, storing followers in
the in-memory map, returning real follower data from the followers
dispatcher, and displaying the follower count on the home page.
Testing instructions use ActivityPub.Academy.

Assisted-by: Claude Code:claude-sonnet-4-6
Covers migrating the in-memory Map store to bun:sqlite so that key
pairs and followers survive server restarts.  Explains key serialization
(PKCS#8/SPKI via Web Crypto) and the bun-types TypeScript integration.

Assisted-by: Claude Code:claude-sonnet-4-6
Chapter 8 covers the startup post-sync mechanism: compares the Astro
content collection against a SQLite `posts` table and sends
Create(Article), Update(Article), or Delete(Article) activities to
followers.  Also explains the Article object dispatcher and how
@fedify/astro handles HTML/ActivityPub content negotiation on the same URL.

Also adds `twoslash` to all TypeScript code blocks in Chapters 4–7 and
`// @noErrors` where imports cannot be resolved in the VitePress context.

Assisted-by: Claude Code:claude-sonnet-4-6
Covers handling Create/Update/Delete(Note) inbox activities, verifying
the inReplyTo points at a local Article, and displaying stored comments
below each post page.  Includes XSS warning about rendering remote HTML.

Assisted-by: Claude Code:claude-sonnet-4-6
…rial

Chapter 10 covers: HTML sanitization for comment XSS prevention,
Update(Person) for profile changes, Delete(Article) for deleted posts,
image attachments, and Fly.io deployment notes.

Also records the new tutorial in CHANGES.md under the 2.2.0 unreleased
Docs section, referencing issue fedify-dev#691.

Assisted-by: Claude Code:claude-sonnet-4-6
The previous text directed readers to use a Mastodon instance without
specifying one, and the screenshot was taken unauthenticated (so the
searched actor didn't appear in results).  ActivityPub.Academy is a
sandbox Mastodon instance already used in Chapters 6 and 9, so using
it throughout keeps the tutorial consistent.

The screenshot (mastodon-search.png) still needs to be retaken from
ActivityPub.Academy while logged in.

Assisted-by: Claude Code:claude-sonnet-4-6
First-time readers may be confused by the unusual sign-up flow: no email
or password is required, just a checkbox and a button.  Add a brief
explanation and a screenshot of the sign-up page so readers know what to
expect.

Assisted-by: Claude Code:claude-sonnet-4-6
Show what the blog profile looks like on ActivityPub.Academy instead of
a plain-text approximation.

Assisted-by: Claude Code:claude-sonnet-4-6
…API link

Convert three bold paragraph headers (Key serialization, BLOB return type,
Synchronous followers) into a proper list with italic terms and colons.
Also link "Web Crypto API" to its MDN page.

Assisted-by: Claude Code:claude-sonnet-4-6
…Chapter 9

Assisted-by: Claude Code:claude-sonnet-4-6
Briefly explain what Fly.io and Fly Volumes are, and frame the section as
an option rather than an assumption.

Assisted-by: Claude Code:claude-sonnet-4-6
- Add X-Forwarded-Host handling to the middleware code blocks (Chapter 5
  and 8); previously only X-Forwarded-Proto was shown
- Add security.allowedDomains: [{}] to the astro.config.ts code block and
  explain why it is required for X-Forwarded-Host to be trusted
- Remove the ORIGIN env var approach; instead instruct users to open the
  tunnel URL in their browser so the first request already carries the
  correct forwarded headers
- Expand the Chapter 9 "Testing with ActivityPub.Academy" section with
  step-by-step instructions and four new screenshots:
    - activitypub-academy-timeline.png
    - activitypub-academy-reply-box.png
    - activitypub-academy-reply-typed.png
    - post-with-comment.png

Assisted-by: Claude Code:claude-sonnet-4-6
VitePress TOC and heading styles were broken because all chapter headings
were marked as h1 with ==== underlines.  Only the document title should
be h1; chapter-level headings should be h2 with --- underlines, matching
the convention used in docs/tutorial/microblog.md.

Assisted-by: Claude Code:claude-sonnet-4-6
Prevents Hongdown's sentence-case formatter from capitalizing
"bun-types" to "Bun-types" in headings.

Assisted-by: Claude Code:claude-sonnet-4-6
Cross-references like "Chapter 6" are opaque without numbered headings.
Replace each with an italicized, linked section title so readers can
navigate directly to the referenced content.

Assisted-by: Claude Code:claude-sonnet-4-6
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new, extensive Astro+Bun tutorial for a federated blog, registers it in docs navigation and CHANGES.md, adds bun-types to .hongdown.toml proper-noun rules, and excludes .playwright-mcp/ from VCS and Deno formatting.

Changes

Cohort / File(s) Summary
Ignore & formatting
/.gitignore, deno.json
Add .playwright-mcp/ to .gitignore and .playwright-mcp/** to deno.json fmt.exclude.
Documentation configuration
docs/.vitepress/config.mts, .hongdown.toml
Add "Building a federated blog" to TUTORIAL nav; add bun-types to heading.proper_nouns.
Changelog
CHANGES.md
Add Docs entry referencing the new Astro+Bun federated blog tutorial and adjust link references.
Tutorial content
docs/tutorial/astro-blog.md
Add comprehensive tutorial (~2900+ lines) covering Astro content setup, Fedify integration, ActivityPub endpoints (actor/inbox/outbox/collections), follower handling, SQLite persistence for keys/followers/comments, startup publishing sync, inbox handlers (Follow/Undo, Create/Update/Delete Note), and comment rendering/security guidance.

Sequence Diagram(s)

sequenceDiagram
    participant Startup as "Startup Sync"
    participant Astro as "Astro Server (actor/inbox/outbox)"
    participant DB as "SQLite (blog.db)"
    participant Follower as "Remote Follower Inbox"

    Startup->>Astro: Read site posts & metadata
    Astro->>DB: Load stored posts/follower list
    Startup->>DB: Compute diffs (Create/Update/Delete)
    loop For each follower
        Startup->>Follower: POST Activity (Create/Update/Delete Article)
        Follower-->>Startup: 2xx / error
        Startup->>DB: Record delivery/result
    end
Loading
sequenceDiagram
    participant Remote as "Remote Actor"
    participant Inbox as "Astro Inbox Route"
    participant DB as "SQLite (blog.db)"
    participant Renderer as "Static Post Page"

    Remote->>Inbox: POST Activity (Follow / Create(Note) / Undo / Update / Delete)
    Inbox->>DB: Validate, persist follower/comment changes
    DB-->>Inbox: OK
    Inbox->>Renderer: (on read) render comments/follower count from DB
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #692 — Docs/tutorial and CHANGES.md edits overlap with adding a tutorial entry and changelog update; this PR adds the Astro+Bun tutorial and registers it in navigation, which aligns with that objective.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically summarizes the main change: adding a comprehensive tutorial on building a federated blog with Astro, Bun, and SQLite.
Linked Issues check ✅ Passed The code changes fully implement the requirements from issue #691, including the tutorial document, navigation entry, supporting file updates, and follow/inbox handling features.
Out of Scope Changes check ✅ Passed All changes are directly related to adding the federated blog tutorial and its supporting infrastructure; no unrelated modifications were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description comprehensively describes the changeset, explaining the new tutorial, its structure, supporting changes to configuration files, and linking to the companion example repository.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@issues-auto-labeler issues-auto-labeler bot added component/actor Actor dispatcher related component/cli CLI tools related component/federation Federation object related component/inbox Inbox related component/outbox Outbox related labels Apr 18, 2026
@dahlia dahlia changed the title Add *Building a federated blog* tutorial (Astro + Bun + SQLite) Add “Building a federated blog” tutorial (Astro + Bun + SQLite) Apr 18, 2026
@issues-auto-labeler issues-auto-labeler bot added the activitypub/interop Interoperability issues label Apr 18, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 17 changed files in this pull request and generated 3 comments.

Comment thread docs/tutorial/astro-blog.md
Comment thread docs/tutorial/astro-blog.md
Comment thread docs/tutorial/astro-blog.md
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive tutorial on building a federated blog using Astro and Fedify, covering topics such as actor setup, follower management, and ActivityPub activity handling. The changes also include necessary project configuration updates, such as adding bun-types to the proper nouns list and excluding temporary directories from formatting. A review comment correctly identifies a technical discrepancy in the deployment section of the tutorial, noting that the server must be started with the bun command rather than node because the implementation relies on the Bun-specific bun:sqlite module.

Comment thread docs/tutorial/astro-blog.md Outdated
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The tutorial uses Temporal.Instant from @js-temporal/polyfill but
only relied on it being present transitively through @fedify/vocab.
Transitive dependencies are not guaranteed to stay reachable as
dependency graphs change; added an explicit bun add install step
and listed the package directly.

Assisted-by: Claude Code:claude-sonnet-4-6
@dahlia dahlia requested a review from Copilot April 19, 2026 03:36
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

/gemini review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f4f759523

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/tutorial/astro-blog.md Outdated
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive tutorial titled "Building a federated blog," which guides users through creating an ActivityPub-enabled blog using Astro and Fedify. The tutorial covers essential topics such as actor implementation, follower management, SQLite persistence, activity publishing, and comment handling. Additionally, the PR updates the project configuration and changelog to reflect these documentation improvements. I have no feedback to provide.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 17 changed files in this pull request and generated 2 comments.

Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Fedify's key validation code (sig/key.ts) throws a TypeError if the
key is not extractable, and it also needs to export the public key
to serialize it in the actor document. Setting extractable: false
breaks federation entirely after the first restart. Reverted both
importKey() calls back to extractable: true.

Assisted-by: Claude Code:claude-sonnet-4-6
@dahlia dahlia requested a review from Copilot April 19, 2026 04:09
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

/gemini review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 17 changed files in this pull request and generated 4 comments.

Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive tutorial for building a federated blog using Astro and Fedify, covering actor setup, follower management, SQLite persistence, and ActivityPub activity handling. The review feedback suggests improving the code examples by using the more idiomatic 'followers' collection identifier in sendActivity() calls, which simplifies recipient handling and ensures proper addressing via the cc field.

Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Comment thread docs/tutorial/astro-blog.md Outdated
Astro.params.slug is typed as string | undefined even after the
404 guard, so passing it directly to getCommentsByPost() produces
a TypeScript type error. post.id is the same value but typed as
string since it comes from the content collection entry.

Assisted-by: Claude Code:claude-sonnet-4-6
@dahlia dahlia requested a review from Copilot April 19, 2026 04:43
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented Apr 19, 2026

/gemini review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive tutorial on building a federated blog using Astro and Fedify, covering actor setup, follower management, SQLite persistence, and activity handling. Supporting changes include updates to the project's configuration files and changelog. The review feedback highlights two critical issues in the tutorial's code snippets: a runtime "TypeError" caused by attempting to overwrite the read-only "APIContext.request" property in Astro middleware, and a schema validation failure in the "astro.config.ts" example due to an incorrect "security.allowedDomains" configuration.

Comment thread docs/tutorial/astro-blog.md
Comment thread docs/tutorial/astro-blog.md
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 17 changed files in this pull request and generated 1 comment.

Comment thread docs/tutorial/astro-blog.md
@dahlia dahlia requested review from 2chanhaeng and sij411 April 19, 2026 05:48
@github-actions
Copy link
Copy Markdown
Contributor

Pre-release has been published for this pull request:

Packages

Package Version JSR npm
@fedify/fedify 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/cli 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/amqp 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/astro 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/cfworkers 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/create 2.2.0-pr.695.16+7a782334 npm
@fedify/debugger 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/denokv 2.2.0-pr.695.16+7a782334 JSR
@fedify/elysia 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/express 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/fastify 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/fixture 2.2.0-pr.695.16+7a782334 JSR
@fedify/fresh 2.2.0-pr.695.16+7a782334 JSR
@fedify/h3 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/hono 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/init 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/koa 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/lint 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/mysql 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/nestjs 2.2.0-pr.695.16+7a782334 npm
@fedify/next 2.2.0-pr.695.16+7a782334 npm
@fedify/nuxt 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/postgres 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/redis 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/relay 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/solidstart 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/sqlite 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/sveltekit 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/testing 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/vocab 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/vocab-runtime 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/vocab-tools 2.2.0-pr.695.16+7a782334 JSR npm
@fedify/webfinger 2.2.0-pr.695.16+7a782334 JSR npm

Documentation

The docs for this pull request have been published:

https://d180af62.fedify.pages.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples Example code related integration/astro Astro integration (@fedify/astro) type/documentation Improvements or additions to documentation

Development

Successfully merging this pull request may close these issues.

Build federated blog example and tutorial (Astro + Bun)

2 participants