What is Hackers' Pub?

Hackers' Pub is a place for software engineers to share their knowledge and experience with each other. It's also an ActivityPub-enabled social network, so you can follow your favorite hackers in the fediverse and get their latest posts in your feed.

0
0
0
1

RE: techhub.social/@Cenbe/11613650

I spent hours learning Swift and Metal to build a feature that is quite popular with millenials and gen z.

You don't have to use this feature, but the fact it exists in an open source, consumer facing mobile app is quite unique.

I wasn't able to find any free/open source implementation, so I had to build one.

Let's not shame devs who build popular feature implementations, just because you don't use them.

We should be better than that.

0

I posted on lobste.rs and reddit.com about my latest blog article. It's been a very long time (3 years) since last time I did that.

Some comments are… weird… They try to summarize the content of the article, but it's all wrong. Checking the authors' profile show they are active since many months. But really, really, it smells LLM. Maybe I'm “doubtful” because LLM are everywhere today, but I don't know what to do with these comments…

Did you experience something similar recently?

0
0
0
1

I posted on lobste.rs and reddit.com about my latest blog article. It's been a very long time (3 years) since last time I did that.

Some comments are… weird… They try to summarize the content of the article, but it's all wrong. Checking the authors' profile show they are active since many months. But really, really, it smells LLM. Maybe I'm “doubtful” because LLM are everywhere today, but I don't know what to do with these comments…

Did you experience something similar recently?

0
1
8
0
0
1
0
1
0
1
0
1
1
1
0

🫧 socialcoding.. shared the below article:

Inside Indiekit: How 30+ Plugins Turn a Node.js Server into a Federated Personal Web Platform

Ricardo Mendes @rick@rmendes.net

This post is a guided tour through the architecture of the system that powers this site.

It’s built on Indiekit, an open-source Node.js IndieWeb server created by Paul Robert Lloyd. I forked it because I wanted to change fundamental aspects of how it works — a new page post type for slash pages, OpenGraph card embeds in the Bluesky and Mastodon syndicators, full ActivityPub federation, content aggregation from external platforms, a social reader, a homepage builder, and more. The result is 27 @rmdes/* plugins that extend the original system far beyond its initial scope.

This level of extension — going from a handful of core IndieWeb features to a 30+ plugin personal web platform — was made possible by developing with Claude Code (Anthropic). It served as a pair-programming assistant throughout, helping me move fast without sacrificing code quality.

If you want the interactive version with diagrams, check out the Architecture Explorer.

What follows is the story of how all these pieces fit together.


1. The Core: What Indiekit Is

At its heart, Indiekit is an Express server with a plugin orchestration layer. It doesn’t do much on its own — its power comes from the plugins you load. The core provides five extension points:

  • addEndpoint() — registers HTTP routes. This is how plugins expose admin UIs, APIs, and protocol endpoints.
  • addPostType() — defines content types (articles, notes, photos, pages) with their own properties and permalink patterns.
  • addStore() — plugs in a storage backend. The default writes .md files to the filesystem, but you could write to GitHub, GitLab, or anywhere else.
  • addSyndicator() — registers targets for cross-posting. Each syndicator knows how to format and deliver content to its platform.
  • addPreset() — integrates with a static site generator. The preset converts Indiekit’s internal JF2 data format into whatever your SSG expects.

Configuration lives in indiekit.config.js, loaded via cosmiconfig. You list your plugins, set your preferences, and Indiekit wires everything together at startup.

The key design principle is that every piece of functionality is a plugin. Authentication? A plugin. Content creation via Micropub? A plugin. The admin UI for managing posts? A plugin. This means you can run a minimal IndieWeb blog with just a handful of packages, or load 30+ plugins for a full-featured personal web platform.

2. The Plugin Taxonomy

With 30+ plugins, organization matters. They break down into six categories:

Core IndieWeb Endpoints (6 plugins)

These are the essential building blocks. endpoint-auth handles IndieAuth with JWT and PKCE. endpoint-micropub processes content creation requests — it’s the main entry point for posting. endpoint-posts provides the admin UI at /posts for managing content. endpoint-syndicate triggers cross-posting on a 2-minute polling schedule. Two webmention plugins handle outbound sending and inbound receiving via webmention.io.

Several of these are forks of the upstream Indiekit packages. The Micropub fork adds type-based post discovery. The syndicate fork adds batch mode with a 2-second delay between targets to avoid rate limiting. These are the kinds of practical modifications that emerge from running the system in production.

Social & Federation (3 plugins)

This is where things get interesting. The ActivityPub plugin turns the entire site into a fediverse actor — more on this in section 4. Microsub is a social reader with adaptive feed polling that ranges from 1-minute checks for active feeds down to 17-hour intervals for dormant ones. Blogroll aggregates blogs from OPML files, Microsub subscriptions, and FeedLand, with webhooks connecting it to the Microsub reader.

Content Aggregation (6 plugins)

Six plugins pull activity from external platforms on background schedules: RSS feeds every 15 minutes, Podroll (podcasts via FreshRSS) every 15 minutes, Funkwhale listening history every 5 minutes, Last.fm scrobbles every 5 minutes, GitHub activity, and YouTube channel data. Each stores its data in MongoDB and exposes an API that the Eleventy theme fetches at build time.

Site Management (3 plugins)

Homepage Builder provides a drag-and-drop admin UI for arranging homepage sections. It discovers available sections from other plugins — CV data, GitHub repos, Funkwhale listening stats, recent blog posts, and more. CV manages a structured resume with experience, education, skills, projects, and certifications. LinkedIn OAuth handles token management for the LinkedIn syndicator.

Syndicators (4 plugins)

Four cross-posting targets: Bluesky (AT Protocol with native rich text facets and OG card embeds), Mastodon (with native favorites and reblogs), LinkedIn (REST API for articles and notes), and IndieNews (webmention-based, no API key needed). Each understands interaction types — when you like or repost something, the syndicator sends the appropriate native interaction rather than creating a new post.

Post Types & Presets (2 plugins)

post-type-page creates root-level slash pages (/about, /now, /uses). preset-eleventy is a fork that generates permalinks for all post types, not just the ones upstream Indiekit supports. It converts JF2 content to YAML frontmatter with Markdown bodies — the format Eleventy expects.

3. The Complete Data Flow

The system has two main directions: outbound (publishing your content) and inbound (aggregating external activity).

Outbound: From Micropub to Published Page

When you write a post — using a Micropub client like Quill, the Indigenous app, or the admin UI at /posts — here’s what happens:

  1. Auth check. endpoint-auth validates your IndieAuth bearer token and checks scopes (create, update, delete).
  2. Content processing. endpoint-micropub receives the JF2 content, determines the post type, and preserves any mp-syndicate-to targets for later.
  3. Format conversion. preset-eleventy converts the JF2 data into a YAML frontmatter + Markdown file, generating the appropriate permalink (/articles/2026/02/25/my-post/).
  4. Storage. store-file-system writes the .md file to /app/data/content/{type}/. Post metadata goes into the MongoDB posts collection for admin queries.
  5. Build. Eleventy’s file watcher detects the change and triggers a build. The entire site is rebuilt, a new timestamped release directory is created, and a symlink swap makes it live — zero downtime.
  6. Serving. nginx serves the static site on port 3000, proxying admin and API routes to Indiekit on port 8080.

After publishing, two background processes kick in:

  • The syndication poller (every 2 minutes) finds posts with pending mp-syndicate-to targets and triggers each syndicator with a 2-second delay between them.
  • The webmention sender (every 5 minutes) scans post content for URLs and sends webmentions to any linked page that advertises a webmention endpoint.

Inbound: External Activity Flowing In

The reverse direction is simpler. Six aggregator plugins run on background schedules, fetch data from external APIs, and store it in MongoDB. When Eleventy rebuilds, _data/*.js files call plugin API endpoints to get the latest aggregated content, and the theme renders it.

The cycle looks like: External service → background sync → MongoDB → plugin API → Eleventy _data file → Nunjucks template → static HTML.

4. ActivityPub Federation

This is the most complex part of the system. The ActivityPub plugin, built on Fedify 2.0, turns the site into a full fediverse actor. People on Mastodon, Pleroma, Misskey, or any ActivityPub-compatible platform can follow the site and see new posts in their timeline.

Outbound: Publishing to the Fediverse

When a post is created, the plugin converts JF2 content to an ActivityStreams 2.0 activity using jf2ToAS2Activity(). Fedify’s ctx.sendActivity() then delivers it to every follower’s inbox. The conversion handles all post types — articles become Article objects, notes become Note, likes become Like activities, and so on.

Inbound: Receiving from the Fediverse

Remote servers POST activities to the inbox. Fedify routes them to handlers for each activity type: Follow, Undo, Like, Announce (boost), Create (new post from a followed account), and Delete. Each handler updates the appropriate MongoDB collection.

The Reader

Following accounts on the fediverse populates a timeline. Their posts arrive as Create activities, are stored in ap_timeline, and rendered in a reader UI that supports composing, liking, boosting, and following — all from the admin interface.

The Express-Fedify Bridge

One technical challenge: Fedify has an official @fedify/express adapter, but it doesn’t work correctly with mounted sub-apps due to path resolution issues. The plugin uses a custom bridge that reconstructs req.originalUrl and POST bodies to make Express and Fedify communicate properly.

What It Looks Like

The plugin manages 13 MongoDB collections (ap_followers, ap_following, ap_activities, ap_keys, ap_kv, ap_profile, ap_featured, ap_featured_tags, ap_timeline, ap_notifications, ap_muted, ap_blocked, ap_interactions) and exposes:

Public endpoints for federation:

  • /.well-known/webfinger — actor discovery
  • /.well-known/nodeinfo — server metadata
  • /activitypub/users/* — actor profile, inbox, outbox, collections
  • Content negotiation at / — ActivityPub clients see AS2 JSON instead of HTML

Admin UI for management:

  • Dashboard, reader with timeline and compose
  • Profile editor, follower/following lists
  • Mastodon account import (migration)
  • Moderation (mute/block)

5. Inter-Plugin Relationships

Plugins don’t exist in isolation. Several have meaningful relationships with each other:

The Homepage Builder as Aggregator of Aggregators

endpoint-homepage discovers sections from other plugins at runtime. It knows about CV (5 section types), GitHub, Funkwhale, Last.fm, Blogroll, Podroll, YouTube, and Microsub. The admin UI lets you drag and drop these sections into a layout — single column, two-column with sidebar, or custom arrangements. When you save, it writes homepage.json, which triggers an Eleventy rebuild.

Microsub ↔ Blogroll

These two talk to each other. The Blogroll plugin can import feeds from Microsub channels, and when a new blog is added to the Blogroll, it notifies Microsub via webhook so the reader picks up the feed automatically.

The Publishing Chain

Content flows through a chain: Micropub (create the post) → Syndicate (trigger cross-posting) → Syndicators (deliver to platforms). The Micropub plugin preserves mp-syndicate-to targets in the post metadata. The syndicate endpoint polls for pending targets and dispatches to the appropriate syndicator. Each syndicator handles its platform’s API natively.

LinkedIn Token Sharing

The LinkedIn endpoint and syndicator are separate plugins that share state through environment variables. The endpoint handles the OAuth flow (authorization, token refresh) and stores tokens. The syndicator reads those tokens to authenticate API calls. This separation means the OAuth complexity doesn’t pollute the syndication logic.

The Three Social Outputs

When a post is published, it can reach three different networks simultaneously:

  • ActivityPub → fediverse (Mastodon, Pleroma, etc.)
  • Syndicators → social platforms (Bluesky, Mastodon API, LinkedIn)
  • Webmention sender → IndieWeb sites

Yes, Mastodon appears twice — once via ActivityPub (federation, native) and once via the syndicator (API-based, for cases where you want explicit POSSE control). They serve different purposes.

6. Deployment Architecture

Everything runs inside a single Cloudron container. Three long-running processes share the filesystem:

nginx (:3000) — The Entry Point

nginx serves the static site from /app/data/site, which is a symlink to the current Eleventy build. Media files come from /app/data/content/media/. All admin routes, Micropub endpoints, and plugin APIs are proxied to Indiekit on port 8080. It also handles legacy URL redirects (/content/ → clean URLs) and security headers (CSP, X-Frame-Options).

Indiekit (:8080) — The Application

The Express server with 30+ plugins loaded via indiekit.config.js. It handles all dynamic operations: content creation, authentication, syndication, ActivityPub federation, and all 11 background sync processes. This is the only process that talks to MongoDB.

Eleventy — The Site Builder

A file watcher on /app/data/content/. When files change, it triggers a full build. The build creates a timestamped directory in /app/data/releases/, then atomically swaps the /app/data/site symlink — zero downtime. After building, it runs Pagefind for search indexing and notifies WebSub subscribers.

The Filesystem

/app/data/                    # writable, backed up by Cloudron
├── config/                   # indiekit.config.js, env.sh, .secret
├── content/                  # user posts organized by type
│   ├── articles/
│   ├── notes/
│   ├── photos/
│   ├── likes/
│   ├── pages/
│   └── media/                # uploaded images
├── releases/                 # timestamped Eleventy builds
├── site →                    # symlink to current release
└── cache/                    # Eleventy build cache

MongoDB

Cloudron manages MongoDB. The database stores all state — 30+ collections covering posts, blogroll data, Microsub feeds, webmentions, RSS items, listening history, scrobbles, CV data, homepage configuration, LinkedIn tokens, podcast episodes, and all 13 ActivityPub collections.

7. The Eleventy Theme Layer

The theme is where data becomes a website. It’s a standalone repository used as a Git submodule in the deployment.

Data Sources

Eleventy’s _data/*.js files fetch data from three places:

Plugin APIs — endpoints like /blogrollapi/*, /funkwhale/api/*, /lastfm/api/*, /podrollapi/*, /github/api/*, /cv/data.json, and /homepage/api/*. These are the aggregator plugins exposing their MongoDB data via HTTP.

External APIs — YouTube Data API, GitHub REST API, Bluesky AT Protocol, and Mastodon API. These provide sidebar widgets with recent social activity that doesn’t go through Indiekit.

Static configsite.js (environment variables for site name, URL, author info), enabledPostTypes.js (which post types to show in navigation), homepageConfig.js (homepage layout), and cv.js (resume data).

Each data file follows the same pattern: try the Indiekit plugin API first, fall back to direct external API, return { source: "indiekit" | "api" | "error" } so templates can conditionally display content.

Templates

The template hierarchy is:

  • base.njk — the HTML shell with <head>, navigation, footer, and conditional sidebar logic
  • home.njk — the homepage with plugin-driven layout or default hero + recent posts
  • post.njk — individual posts with full microformat markup (h-entry), Bridgy syndication content, webmentions, reply context
  • page.njk — static slash pages with sidebar
  • fullwidth.njk — full-width pages for rich HTML content (like the Architecture Explorer)

Components include the homepage builder (section router, sidebar widgets), author h-card, reply context for interactions, and the webmentions display (likes, reposts, replies with avatars).

IndieWeb Compliance

Every post is marked up with Microformats2: h-entry for posts, h-card for the author, h-feed for lists, h-cite for reply context. The <head> includes rel="me" links for identity verification, authorization and token endpoints for IndieAuth, and Micropub/Microsub endpoint discovery.

For syndication via Bridgy, posts include hidden content with emoji prefixes and target URLs that Bridgy reads when cross-posting to Bluesky and Mastodon.

Frontend Stack

  • Tailwind CSS with dark mode (.dark class toggle)
  • Alpine.js for interactive components (dropdowns, tabs, compose forms)
  • Pagefind for client-side search
  • lite-youtube-embed for lazy-loaded YouTube embeds
  • is-land for island architecture — lazy-hydrated interactive widgets

8. Key Conventions

A few conventions keep the system consistent across 30+ plugins:

Dates are always ISO 8601 strings. new Date().toISOString(), never new Date(). The Nunjucks | date filter uses date-fns parseISO() which only accepts strings — passing a Date object crashes the template. Every template guards date filters with {% if value %} to handle nulls.

ESM everywhere. All plugins use "type": "module" in their package.json. No CommonJS, no build step.

Three auth layers. IndieAuth for admin routes (the user logs in), JWT for background processes (the syndication poller authenticates itself), HTTP Signatures for ActivityPub (remote servers verify identity).

Dual storage by design. MongoDB stores state and metadata — post records, aggregated content, ActivityPub data, configuration. The filesystem stores content — the actual .md files that Eleventy builds into the site. This separation means you can wipe the database and still have your content, or rebuild the database from the filesystem.

The publish lifecycle. Updating a plugin follows a strict sequence: bump version in package.json → commit and push → npm publish (manual, requires OTP) → update the Dockerfile version → cloudron build --no-cache && cloudron update. Skipping a step means the change doesn’t reach production.


The Big Picture

This is a personal web platform built from composable parts. At its core, it’s the IndieWeb stack — Micropub for publishing, webmentions for interactions, microformats for structured data. Layered on top is ActivityPub for fediverse federation, syndicators for platform cross-posting, and aggregators for pulling in external activity.

The result is a site that you fully own and control, that federates with the fediverse, cross-posts to social networks, aggregates your digital life, and serves as a static site for performance. All running inside a single container.

For the interactive version with navigable diagrams of each section, visit the Architecture Explorer.

🔗 https://rmendes.net/articles/2026/02/25/deep-dive-inside-indiekit

Read more →
0

RE: chaos.social/@netzpolitik_feed

Den Staat in Richtung einer turbokapitalistischen 1984-Version für totale hochrüsten, während die die stärkste Partei in den Umfragen ist, 50% des Staatshaushaltes zukünftig in fließen soll, ins Haus steht und alle sozialen Sicherungsnetze deinstalliert werden sollen? Was soll da schon schiefgehen, ging doch noch nie schief, oder? Dumm nur, dass uns währenddessen auch noch der Planet um die Ohren fliegt.

Wer vom Fach ist und den IT-Widerstand unterstützen will, ist sicher in unserer Branchen-Initiative IT herzlich willkommen @fau_chaosFAU @ Chaos Events

0
0

日本人だけ読めないというアルファベットフォントあった
「Electroharmonix」フォント埋め込みはダメぽいので使うならテクスチャ作成しといてになるかな
https://ja.fonts2u.com/electroharmonix-regular.%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88

RE: https://misskey.io/notes/aj6v98sh9npg0apv

1

There are a number of architecture patterns that fit like a glove together. I might summarize them to offer "Functional-reactive DDD" which allows distributed communication systems and direct focus on solution development.

As for my own interest in these technologies.. they form the underpinning to the work method that steers both my life and career. I divide my activities in a concrete and pragmatic 'work track', where I earn my living, and a long-term background 'hobby track'.

On this latter track I defined a "Grand dream" I pursue: the philosophical quest as well as the elaboration of holistic solutions for some of the wickedest problems of our time. In particular the question how we not only design an alternative to hypercapitalism, but actually introduce it into global society, such that it becomes the prevailing system.

Crazy, but fun 😅

coding.social

0
1

The natural language processing features of MacOS spotlight and the indexing are great. Did you know you can type "Pictures of dogs" and it finds images with dog in the file name but also those recognised as containing dogs? This is invisible AI helpfulness…

0
0

국회에서 유통산업 규제 완화 논의가 속도를 내는 가운데, 새벽배송 확대에 따른 탄소배출 증가 문제는 뒷전으로 밀려 있다는 우려가 제기됐습니다. 유통·식품업계가 소비자 편익과 투자 활성화를 이유로 규제 문턱을 낮춰달라고 요구하는 사이, 배송 과정에서 발생하는 환경 비용과 탄소 배출 문제는 정책 논의 뒷전으로 밀려나 있다는 지적입니다.

“새벽배송 확대, 탄소배출 증가는 뒷전” 유통법 개정안...

0
1

有關同溫層的難聽話

頗有一些右翼綠營支持者似乎沒意識到,你們口中愚昧的反川英國執政黨,實質上也是中間偏右政黨(這話不是我說的,請洽齊澤克),許多工黨面臨的困境,都跟民進黨有可比較借鑒之處,只是他們那邊的民眾黨已經快大獲全勝而已

比起台灣各政黨之間的極化,非藍白陣營內的極化還更令人憂心呢

0
1
0
1
0

I'm fascinated by ancient stories that preserve some scrap of awareness of the gradual transition from hunter-gatherer to agricultural society: there's a Chinese myth about a minotaur named Shennong ("god farmer") who could tank poison damage, and so he ate a dish of every single plant that grows in the world to figure out which ones are good to grow and which ones will kill you.

0
0
0
1
1

When I worked on Black, I was happy to see any new sign of adoption. But I'd never smear projects that looked at my tool and decided against adopting it, even if they used emotional language to motivate their decision. Ultimately it's a tool, it's your decision if you want it in your workflow or not. (2/3)

0

I'm fascinated by ancient stories that preserve some scrap of awareness of the gradual transition from hunter-gatherer to agricultural society: there's a Chinese myth about a minotaur named Shennong ("god farmer") who could tank poison damage, and so he ate a dish of every single plant that grows in the world to figure out which ones are good to grow and which ones will kill you.

0
0
1
0
0
0
1