Skip to content
  • 0 Votes
    1 Posts
    5 Views
    rick@rmendes.netR
    The fediverse and Bluesky are often discussed as competing visions of decentralized social media. ActivityPub is a W3C standard implemented by Mastodon, Misskey, Lemmy, and hundreds of other projects. AT Protocol is Bluesky’s foundation — a different architecture built around personal data servers, DIDs, and a global firehose. Most projects pick one. A few try to bridge them externally. Wafrn does something more ambitious: it implements both natively, in the same codebase, with a unified user model that lets a single account exist on both networks simultaneously. This post is a deep dive into how that works — from the service architecture down to the queue ordering that determines which protocol sees your post first. The focus is on what Wafrn’s approach means for self-hosting: if you deploy a Wafrn instance, you’re running a fully sovereign ActivityPub server and your own Bluesky PDS, with your users’ identities living on your infrastructure in both networks. I’m writing this as an outsider reading the source code, not as a contributor. All code references are from the public repository. 1. What Wafrn Is Wafrn is a federated social media platform inspired by Tumblr. It’s a TypeScript monorepo with an Angular frontend and an Express backend, backed by PostgreSQL and Redis. The project is AGPL-3.0-or-later licensed and maintained primarily by Gabriel Amador García. At its core, Wafrn is a microblog engine — users create posts with rich text, media, content warnings, polls, and threaded replies. Posts can be reblogged (“rewooted”), quoted, liked, and emoji-reacted to. There’s a notification system, user blocking/muting, custom emoji support, and a forum mode. What makes Wafrn architecturally interesting is that this microblog engine doesn’t just connect to the fediverse and Bluesky — it is both. It speaks ActivityPub natively as a first-class federation protocol, and it runs a real Bluesky PDS alongside its backend, bridging the two through a shared database and a carefully ordered message queue pipeline. 2. The Service Architecture A production Wafrn deployment (using the “advanced” Docker Compose variant) defines eleven services. The logical architecture looks like this: Internet │ Frontend/Caddy container (Angular static files + Caddy reverse proxy + TLS) │ ├── Backend ×3 replicas (Express API, ActivityPub endpoints) │ ├── Cache ×1 (dedicated backend replica for cache API routes) │ ├── WebSocket ×1 (real-time updates) │ ├── Workers ×3 (BullMQ job processors) │ ├── PDS Worker ×1 (ATProto Jetstream listener) │ ├── Migration (one-shot init container for DB migrations) │ ├── PDS (official Bluesky ghcr.io/bluesky-social/pds:0.4) │ ├── PostgreSQL (shared state) │ ├── Redis (caching, queues, sessions) │ └── Adminer (database admin UI) A notable detail: Caddy isn’t a standalone service. The frontend Dockerfile builds the Angular app into static files, then copies them into a caddy:2 base image. The Caddyfile, TLS termination, and reverse proxy logic all live inside the same container that serves the frontend. This is a clean packaging choice — one container handles both static asset serving and all domain routing. The key insight is that the Backend, Workers, Cache, WebSocket, and PDS Worker services all share the same Docker image — they’re the same Express backend started with different entry points and configuration flags. The USE_WORKERS environment variable determines whether a given replica processes BullMQ jobs or just serves HTTP requests. The PDS is the only service that isn’t Wafrn’s own code. It’s the stock Bluesky PDS binary, running as a sidecar. 3. Native ActivityPub: Not a Bridge, a Server Wafrn doesn’t use Bridgy Fed or any external bridge for ActivityPub. It implements the protocol directly. When another fediverse server sends a Follow request, the HTTP request hits Wafrn’s own Express routes. The Protocol Surface The ActivityPub implementation lives in two route files and a directory of processors: routes/activitypub/well-known.ts — WebFinger, NodeInfo, host-meta, and Mastodon-compatible instance API (/api/v1/instance). The NodeInfo response identifies itself as software "wafrn" with protocol ["activitypub"]. routes/activitypub/activitypub.ts — Actor endpoints (/fediverse/blog/:url), inbox (both per-user and shared), outbox, following/followers collections, featured collection, emoji objects, and post replies. utils/activitypub/processors/ — Sixteen separate processors handling every activity type: Accept, Add, Announce (boost), Bite, Block, Create, Delete, EmojiReact, Flag, Follow, Like, Move, Reject, Remove, Undo, and Update. How Incoming Activities Flow When a Mastodon server sends a Create activity to Wafrn’s shared inbox: Caddy receives the HTTPS request on the main domain and reverse-proxies it to one of the three backend replicas. The /fediverse/sharedInbox route validates the HTTP Signature via checkFediverseSignature. The activity body is added to a BullMQ queue called inbox with 3 retry attempts and exponential backoff. A Worker process picks up the job and dispatches it to the appropriate processor based on the activity type. This queue-based design means Wafrn never blocks on processing — it returns HTTP 200 immediately and processes asynchronously. This is important for federation reliability, since remote servers will timeout and retry if your inbox is slow. User Representation When a remote fediverse server queries a Wafrn user via WebFinger or fetches their actor document, Wafrn responds with standard JSON-LD. The userToJSONLD function constructs the full ActivityPub actor object including: Standard fields: inbox, outbox, followers, following, featured, publicKey alsoKnownAs — and here’s where it gets interesting: if the user has a bskyDid, Wafrn includes at://{did} in the alsoKnownAs array. This is how the dual identity is advertised to the fediverse: the user’s ActivityPub actor document explicitly links to their AT Protocol DID. if (user.bskyDid) { alsoKnownAs.push(`at://${user.bskyDid}`) } This cross-referencing is bidirectional — Wafrn stores the fediverse remoteId on users discovered via Bluesky, and the bskyDid on users discovered via ActivityPub. 4. The Bluesky PDS: A Sidecar, Not a Reimplementation Wafrn doesn’t implement the AT Protocol data server from scratch. It runs the official Bluesky PDS Docker image (ghcr.io/bluesky-social/pds:0.4) as a sidecar service, activated via Docker Compose profiles: pds: image: ghcr.io/bluesky-social/pds:0.4 profiles: - bluesky environment: PDS_HOSTNAME: ${PDS_DOMAIN_NAME} PDS_JWT_SECRET: ${PDS_JWT_SECRET} PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD} PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ${PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX} PDS_DATA_DIRECTORY: /pds PDS_BLOBSTORE_DISK_LOCATION: /pds/blocks PDS_BLOB_UPLOAD_LIMIT: 157286400 PDS_DID_PLC_URL: "https://plc.directory" PDS_BSKY_APP_VIEW_URL: ${PDS_APPVIEW_URL:-https://api.bsky.app} PDS_BSKY_APP_VIEW_DID: ${PDS_APPVIEW_DID:-did:web:api.bsky.app} PDS_CRAWLERS: ${PDS_CRAWLERS:-https://bsky.network, https://atproto.africa} PDS_SERVICE_HANDLE_DOMAINS: ${PDS_HANDLE_DOMAINS:-.${PDS_DOMAIN_NAME}} # also configured: PDS_REPORT_SERVICE_URL/DID (moderation), # PDS_EMAIL_SMTP_URL (email), LOG_ENABLED This is a full-fat PDS. It manages repositories, handles blob storage, registers DIDs with the PLC directory, and gets crawled by the Bluesky relay network. Your users’ AT Protocol data lives on your server’s disk, in your PDS’s data directory. Domain Routing The Caddy reverse proxy handles the multi-domain routing that makes this work: # Main domain → Wafrn backend (API, ActivityPub, frontend) ${DOMAIN_NAME} { @api path /api* /fediverse* /contexts* /post* /blog* /.well-known* handle @api { reverse_proxy ${BACKEND_HOST:-backend:9000} } handle { root * /var/www/html/frontend/ file_server } } # PDS domain + wildcard subdomains → Bluesky PDS ${PDS_DOMAIN_NAME} *.${PDS_DOMAIN_NAME} { tls { on_demand } reverse_proxy ${PDS_HOST:-pds:3000} } The wildcard *.${PDS_DOMAIN_NAME} with on-demand TLS is crucial — in AT Protocol, each user’s handle resolves to a subdomain of the PDS. When someone’s handle is alice.bsky.example.com, the AT Protocol infrastructure expects to reach the PDS at that exact domain. Caddy’s on-demand TLS automatically provisions certificates for each user’s subdomain as they’re created. The PDS also exposes a /tls-check endpoint that Caddy queries before provisioning certificates, preventing abuse by ensuring only legitimate handles get TLS certificates. What the Self-Hosted PDS Gives You Running your own PDS means: Your users’ AT Protocol identities are sovereign. Their DIDs are registered in the PLC directory pointing to your PDS. Even if Bluesky’s main relay goes down, the identity data lives on your infrastructure. Repository data is local. Posts, likes, follows — the actual signed data repositories — are stored in your PDS’s /pds/blocks directory. Handle resolution is yours. Users get handles under your PDS domain (e.g., alice.bsky.example.com), or you can configure custom handle domains. You control the crawlers. The PDS announces itself to the Bluesky relay network (bsky.network) and optionally to alternative relays (atproto.africa), but you could point it wherever you want. 5. The Unified User Model The bridge between protocols happens at the data layer. Wafrn’s User model is designed from the ground up to represent identities across both networks. A single database row can carry: Field Purpose email Present only for local users — if null, the user is remote remoteId ActivityPub actor URL (e.g., https://mastodon.social/users/alice) remoteMentionUrl Fediverse mention URL remoteInbox Fediverse inbox endpoint federatedHostId FK to the remote server record publicKey / privateKey RSA key pair for HTTP Signatures bskyDid AT Protocol DID (e.g., did:plc:abc123...) bskyAppPassword Per-user AT Protocol app password for session auth enableBsky Whether this user has opted into Bluesky cross-posting isBskyPrimary Whether the user’s primary identity is Bluesky alternateUrl The secondary protocol’s handle The model also has computed properties that distinguish user types at runtime: get isBlueskyUser() { return !!(this.url.split("@").length == 2 && this.bskyDid); } get isFediverseUser() { return !!(this.url.split("@").length == 3 && this.remoteId); } The naming convention encodes the protocol: Bluesky handles have one @ segment (@alice.bsky.social), while fediverse handles have two (@alice@mastodon.social). Local users have neither prefix. Four User States This model creates four distinct states: Local-only: Has email, no remoteId, no bskyDid. A user who only exists on the Wafrn instance. Local + Bluesky: Has email, has bskyDid and enableBsky: true. Posts cross-post to Bluesky via the PDS. Remote fediverse: Has remoteId, no email. Discovered through ActivityPub federation. Remote Bluesky: Has bskyDid, no email, no remoteId. Discovered through the Jetstream firehose. And then there’s the fifth, most interesting state: Merged dual-protocol: Has both remoteId and bskyDid. This happens when Wafrn detects that a user exists on both networks — for example, a fediverse user who also has a Bluesky account via Bridgy Fed. The merge logic in getAtprotoUser is worth examining: // simplified for clarity — actual code collects the match first, then merges const bskyPds = doc?.service?.find( x => x.id === '#atproto_pds' || x.type === 'AtprotoPersonalDataServer' ) if (bskyPds && bskyPds.serviceEndpoint.toString() .replace(/\/$/, '').endsWith('brid.gy')) { // bridgy user. find the alsoKnownAs user const allHttpsAlsoKnownAs = doc?.alsoKnownAs ?.filter(x => x.startsWith('http')) ?? [] let user: User | undefined = undefined for (const fediUser of allHttpsAlsoKnownAs) { const tempUser = await getRemoteActor(fediUser, userFound, true) if (tempUser) { user = tempUser break } } if (user) { // found remote fedi user, now merge await mergeUsersQueue.add("mergeUsers", { primaryUserId: user.id, userToMergeId: userFound.id }); return user } } When Wafrn encounters a Bluesky DID whose PDS is atproto.brid.gy (Bridgy Fed), it looks up the alsoKnownAs in the DID document, resolves the HTTPS URLs via ActivityPub’s getRemoteActor, and merges the two user records into one. The fediverse identity becomes primary, and the Bluesky identity is preserved as alternate. This prevents duplicate users from appearing when the same person is visible from both networks. 6. The Jetstream Worker: Ingesting the Bluesky Firehose While ActivityPub works on a push model (remote servers POST activities to your inbox), AT Protocol uses a pull model. Wafrn connects to the Bluesky network’s Jetstream — a WebSocket-based event stream that provides real-time access to all public commits across the network. The Jetstream worker (atproto.ts) runs as a separate process: const jetstream = new Jetstream({ endpoint: completeEnvironment.bskyJetstreamUrl, wantedCollections: [ "net.wafrn.feed.bite", "app.bsky.feed.like", "app.bsky.feed.post", "app.bsky.feed.repost", "app.bsky.graph.follow", "app.bsky.graph.block", "app.bsky.feed.threadgate", ], cursor: cursor, }); Note net.wafrn.feed.bite — Wafrn has defined a custom AT Protocol lexicon for its “bite” feature (a playful social interaction), and it listens for those events alongside the standard Bluesky collections. The Filtering Layer The firehose produces every public event on the Bluesky network. Wafrn doesn’t process all of them — it filters to only relevant events using a cached set of DIDs: jetstream.on("commit", async (event) => { if ( cacheData.followedDids.has(event.did) || checkCommitMentions(event.did, commit, cacheData) || commit.collection === "net.wafrn.feed.bite" ) { await firehoseQueue.add("processFirehoseQueue", data); } }); The filter cache (getCacheAtDids) builds three sets: followedDids: All Bluesky DIDs that any local Wafrn user follows localUserDids: DIDs of local users who have Bluesky enabled followedHashtags: Hashtags that any local user follows An event is processed if: The author DID is in the followed set (someone your users follow posted something), or The event mentions a DID that’s in the local or followed set, or It’s a bite (always processed as a custom lexicon). Everything else is dropped. This is how Wafrn stays efficient despite connecting to a firehose that produces millions of events per day. From Firehose to Database When an event passes the filter, the processFirehoseWorker translates it into Wafrn’s internal model. It handles creates (new posts, likes, reposts, follows, blocks), deletes (unfollows, unlike, post deletions), and updates. For posts specifically, the processSinglePost function does heavy lifting: Resolves the author’s DID to a Wafrn User record (creating one if needed). Fetches the post record directly from the author’s PDS using com.atproto.repo.getRecord. Checks if the post has a fediverseId or bridgyOriginalUrl — if so, it’s a cross-posted fediverse post, and Wafrn resolves the original via ActivityPub instead of creating a duplicate. Parses rich text facets (mentions, links, hashtags) into HTML. Processes embeds (images, videos, link cards, quotes). Creates the post in PostgreSQL with bskyUri and bskyCid fields set, linking it to the AT Protocol record. The deduplication in step 3 is particularly sophisticated. When a post has a fediverseId field (a custom record field that Wafrn sets when cross-posting), the worker verifies it by fetching the fediverse post and confirming that the blueskyUri and blueskyCid match bidirectionally. This prevents spoofing — you can’t claim a fediverse post is yours on Bluesky unless the fediverse post also claims you. 7. The Post Pipeline: Bluesky First, Then ActivityPub When a local Wafrn user creates a post, the federation order is deliberate: Bluesky first, ActivityPub second. This is visible in the triggerPostFederation function: async function triggerPostFederation(post: Post, user: User) { const jobData = { postId: post.id, petitionBy: post.userId } if (post.privacy === Privacy.Public && user.enableBsky && completeEnvironment.enableBsky && user.bskyDid) { await sendPostBskyQueue.add('sendPostBsky', jobData, { delay: 500 }) } else { await prepareSendPostQueue.add('prepareSendPost', jobData, { jobId: post.id, delay: 1500 }) } } If the user has Bluesky enabled, the post goes to the sendPostBsky queue. ActivityPub federation doesn’t happen yet. Inside sendPostBsky, after the post is successfully created on Bluesky and the bskyUri/bskyCid are saved to the database, then the ActivityPub federation is triggered: // we send the post to fedi once we get the bsky data await prepareSendPostQueue.add('prepareSendPost', job.data, { jobId: post.id, delay: 5000 }) This ordering exists for a practical reason: when the post is federated via ActivityPub, the JSON-LD representation can include the blueskyUri and blueskyCid. This enables the deduplication logic described in section 6 — when a fediverse post arrives via the Bluesky firehose, Wafrn can verify the cross-reference. If ActivityPub federation happened before the Bluesky post was created, that cross-reference wouldn’t exist yet. The ATProto Post Construction The postToAtproto function handles the translation from Wafrn’s internal post format to an AT Protocol record. This isn’t trivial: Rich text: Wafrn uses @atcute/bluesky-richtext-builder to construct faceted text. It tokenizes the post, detects mentions, links, and hashtags, and builds the facet array that Bluesky clients use for rendering. Character limit: Bluesky has a 300-grapheme limit. Wafrn approximates this by comparing against UTF-8 byte length (postMax = 300 checked via TextEncoder.encode().byteLength), which may truncate non-ASCII posts slightly earlier than strictly necessary. If a post exceeds the limit and has 1–4 media attachments, Wafrn truncates and appends a “see complete post” link back to the Wafrn instance. Otherwise (no media, or more than 4 attachments), it truncates with [...]. Media: Images larger than 1,000,000 bytes (Bluesky’s hard limit) are resized via the optimizeMedia utility (which uses sharp internally), with two attempts — first at 2000px on the long side, then at 768px if still too large. Multi-frame images (animated WebP, APNG, GIF) are handled in a two-step pipeline: first converted to GIF via sharp, then from GIF to MP4 via ffmpeg, before being uploaded as video. Content warnings: Mapped to Bluesky’s self-label system (graphic-media). Quotes: Translated to app.bsky.embed.record or app.bsky.embed.recordWithMedia depending on whether the quoting post also has media. Reply threading: Wafrn resolves the parent and root post bskyUri/bskyCid to construct the AT Protocol reply reference. Interaction controls: Wafrn’s reply control settings are translated to AT Protocol threadgates and postgates, mapping settings like “followers only” or “mentioned users only” to the appropriate $type rules. The function also embeds a fediverseId field in the AT Protocol record pointing back to the Wafrn/ActivityPub URL of the post. This is the outbound side of the deduplication handshake. 8. Session Management and Per-User Authentication Each Wafrn user authenticates independently against the PDS. The getAtProtoSession function manages this: const agent = new AtpAgent({ service: serviceUrl, persistSession: async (evt, session) => { if (session && user) { await redisCache.set( 'bskySession:' + user.id, JSON.stringify(session), 'EX', 3600 ) } } }) Sessions are cached in Redis with a 1-hour TTL. The login uses the user’s bskyDid and bskyAppPassword — each user provides their own app password for their PDS account. If login fails, Wafrn makes up to three total attempts with increasing waits (1 second, then 2.5 seconds between attempts). If all three fail, handleAgentLoginFail disables the user’s Bluesky integration to prevent account lockouts from repeated failed auth. A login error within any single attempt can also trigger the disable for non-admin users. The admin user gets a separate session via getAdminAtprotoSession, used for server-level operations like fetching public posts from the PDS. 9. What This Architecture Means for Self-Hosting If you deploy a Wafrn instance, here’s what you’re actually running in terms of protocol sovereignty: ActivityPub Identity Your users are full ActivityPub actors at https://yourdomain.com/fediverse/blog/username You hold the private keys for HTTP Signatures WebFinger, NodeInfo, and all discovery protocols resolve to your server Incoming activities hit your inbox endpoints directly You control federation — you can block servers, review registrations, moderate content This is no different from running Mastodon or Misskey. Your ActivityPub identity is as sovereign as any other fediverse implementation. AT Protocol Identity Your PDS runs at bsky.yourdomain.com User handles resolve as username.bsky.yourdomain.com DIDs are registered with the PLC directory pointing to your PDS The PLC rotation key (PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX) is the master key to your AT Protocol identities. Whoever holds this key can update the DID documents. Keep it safe. Repository data (the actual signed commits containing posts, follows, etc.) is stored locally in the PDS’s data directory Your PDS announces to crawlers, but you choose which crawlers to announce to The PDS rotation key deserves emphasis: in AT Protocol, identity is controlled by whoever holds the rotation key for the DID. By self-hosting the PDS and controlling this key, you retain the ability to move your users’ identities to a different PDS if needed, or to rotate keys if compromised. This is the AT Protocol equivalent of “owning your keys” in ActivityPub. The Combined Result A single Wafrn user with both protocols enabled has: An ActivityPub actor discoverable via WebFinger at @username@yourdomain.com An AT Protocol identity with a handle at username.bsky.yourdomain.com Cross-references between the two: alsoKnownAs in the ActivityPub actor linking to the DID, and the PDS serving the AT Protocol identity Posts that federate to both networks, with deduplication ensuring the same post isn’t seen twice by users on either network Interactions (likes, follows, boosts/reposts) flowing in from both networks into a single unified notification stream 10. Trade-offs and Observations Reading through the codebase, a few design trade-offs stand out: The PDS is a black box. Wafrn doesn’t modify the Bluesky PDS — it runs the stock image. This means Wafrn can’t deeply integrate with the PDS’s internals (like custom lexicon validation at the PDS level), but it also means PDS upgrades are trivial: bump the Docker image version. The tight coupling happens through the Jetstream firehose and the AT Protocol HTTP API, both of which are stable, documented interfaces. Jetstream, not the full firehose. Wafrn uses @skyware/jetstream rather than connecting to the full relay firehose. Jetstream is a lighter-weight WebSocket stream that provides the same events in a more manageable format. The trade-off is dependency on a Jetstream endpoint, but the code allows configuring which endpoint to use. Queue-driven everything. Both protocols use BullMQ queues for processing. ActivityPub inbox processing, post federation to both networks, user resolution, media processing — it’s all async. This makes the system resilient to spikes but adds complexity in failure handling. Failed jobs use exponential backoff and have configurable retry counts. Bridgy Fed awareness. Wafrn explicitly handles the case where a user exists on both networks via Bridgy Fed, detecting the brid.gy PDS endpoint and merging identities. This is pragmatic — as more fediverse users opt into AT Protocol via Bridgy Fed, this deduplication becomes increasingly important to avoid phantom duplicate accounts. The “Bluesky first” pipeline is a design choice, not a technical requirement. It could be inverted or parallelized. The current ordering optimizes for cross-reference integrity at the cost of slightly higher latency to fediverse followers (the additional 5-second delay for the prepareSendPost job queued after the Bluesky post completes). For users without Bluesky enabled, posts go directly to the ActivityPub pipeline with a 1.5-second delay and no Bluesky-related overhead. 11. Closing Thoughts Wafrn’s approach to dual-protocol federation is notable because it doesn’t treat either protocol as a second-class citizen. ActivityPub isn’t “bridged through” Bluesky, and Bluesky isn’t “bridged through” ActivityPub. Both are implemented at the application level, sharing a database and a user model designed for dual identity from the start. For the self-hosting community, this is significant. The question “should I join the fediverse or Bluesky?” becomes “why not both?” — with a single deployment that gives your users presence on both networks while keeping all the data on your infrastructure. The ActivityPub keys, the AT Protocol rotation keys, the repository data, the user database — it all lives where you put it. The broader implication is that protocol convergence doesn’t have to mean protocol compromise. Wafrn demonstrates that a relatively small codebase (the backend is a few dozen TypeScript files) can implement both protocols without either one feeling like an afterthought. The unified user model, the deliberate post-ordering pipeline, and the Bridgy Fed deduplication logic show thoughtful integration rather than bolted-on bridging. Whether this dual-protocol model becomes common remains to be seen. But Wafrn proves it’s architecturally viable — and for self-hosters who want to participate in both networks without running two separate platforms, it’s a compelling option. https://rmendes.net/articles/2026/02/26/deep-dive-wafrn-dual-protocol-federation
  • 0 Votes
    1 Posts
    0 Views
    rick@rmendes.netR
    What if I could deploy my own Bluesky PDS and selfhost my Bluesky data in the same way I turned my Indiekit powered blog into an ActivityPub instance overnight with Fedify ? https://rmendes.net/notes/2026/02/26/153ec
  • 0 Votes
    3 Posts
    0 Views
    smallcircles@social.coopS
    @liaizon @Ric There is also a #Matrix bot thas some bridge functionality to Mastodon and Peertube, which you might consider 'bridging'. Perhaps not useful to you, but mentioning just in case.https://delightful.coding.social/delightful-fediverse-experience/#matrix-bridges
  • 0 Votes
    1 Posts
    0 Views
    beckermatic@pleroma.arielbecker.comB
    ️ #Fundraising for a project ️ Friends of the Fediverse! I am raising funds to finance the development of the Fediverse integration of my blogging engine Attoblog, which you can see in action here. My idea is to integrate #ActivityPub so that blog posts can be viewed directly here in the #Fediverse and interacted with through likes, retweets, and comments. Please remember that I am a disabled developer, and working on this gives me back my dignity, as I am unemployed and do not receive any kind of pension. You can send funds via: Paypal: https://paypal.me/beckermatic Ko-Fi: https://ko-fi.com/beckermatic Argentine pesos via alias CBU: beckermatic ETH via this addy #Crowdfunding #FOSS #MutualAid #MutualHelp #Fedihelp #Fedihire #Disability #SmallWeb
  • 0 Votes
    1 Posts
    0 Views
    toddsundsted@epiktistes.comT
    The latest release of Ktistec addresses the shortcomings of the previous release that became apparent after using quote posts in production for a few days. So far, there have been no major bugs, but there was room for improvement.Here's the full changelog.AddedFederation documentation (FEDERATION.md).Visibility (private or direct) icon in object summary.Object social activity details include dislikes."quotes-me" theming class for objects.Notification for quote posts.MCP integration for quote posts.ChangedRenamed NodeInfo siteName to more standard nodeName.Increased hard-coded limits for actor attachments and pinned collections.FixedDisplaying quoted posts in draft view.Visual indication of nested quotes in object view.I added a FEDERATION.md document to the project. This is documentation required by FEP-67ff on "information necessary for achieving interoperability with a federated service". The document describes, at a high level, what federation protocols and standards Ktistec currently supports.#ktistec #crystallang #activitypub #fediverse
  • 0 Votes
    1 Posts
    6 Views
    rick@rmendes.netR
    I created this page to describe the Indiekit architecture powering this blog & AP server #fedify #indiekit #indieweb https://rmendes.net/notes/2026/02/26/d9488
  • 0 Votes
    1 Posts
    0 Views
    smallcircles@social.coopS
    Taking notes on the observed general communication preferences within the #ActivityPub developer community...1. Microblog, microblog, microblog2. Seek one-to-comms, mentions-only3. Don't care about spread of the msg4. Don't boost, don't use hashtags5. Tooted today, gone tomorrowConvenient? Pragmatic? Desirable? idk.
  • 0 Votes
    3 Posts
    1 Views
    julian@activitypub.spaceJ
    @hongminhee@hollo.social the w3id pages point to the rendered resource afaik? The codeberg page is nicer, but the existing pages are okay too e.g. https://w3id.org/fep/1b12
  • 0 Votes
    1 Posts
    3 Views
    heathenstorm@mastodon.socialH
    Another deep dig into #mastodon / #pixelfed / etc. as I prep my #yunohost #homelabSince last autumn, Mastodon's webfinger has been returning rel:self in the format "[mastodonsite]/ap/users/[userid]"However, the YunoHost versions of PixelFed and BookWyrm link back to Mastodon accounts using the rel:self URL, which returns 404 when accessed in a browser.I notice rel:self is in the old format on mastodon.social, so I wonder if it's just a server setting somewhere?#activitypub
  • 0 Votes
    7 Posts
    0 Views
    strypey@mastodon.nzoss.nzS
    @wjmaggos> not sure I see what you're referring to on the quick skim nowSorry, it's the second one that does the deep dive into why decentralisation matters, mainly in the section on "Decentralization and federation terminology". Please excuse my patchy memory.
  • 0 Votes
    1 Posts
    1 Views
    firesidefedi@btfree.socialF
    Fireside Fedi 77 - Shade of Creativity: Lorenzo’s Fediverse Art Talk Special thank you to @lrnz@feed.lrnz.it ** **! **Watch the full VOD:** tubefree.org/w/nMnNvXvyTgTPU... **Explore more episodes:** tubefree.org/a/ozoned/video-channels Subscribe to the RSS feed so you never miss a future deep‑dive. **Subscribe via RSS:** tubefree.org/feeds/videos.xml?videoChannelId=5934 **Podcast feed:** tubefree.org/feeds/podcast/videos.xml?videoChannelId=5934 <br/> #FiresideFedi #FiresideChat #Podcast #VideoPodcast #Fediverse #Fedi #ActivityPub #BSKY #DecentralizedSocial #OpenSocialWeb #Community #TechCulture #FutureOfSocial #TechTalk #DevCommunity #CodingFun #art #comics #ghost #machine
  • 0 Votes
    2 Posts
    1 Views
    surf@flipboard.socialS
    Sign up for the Surf waitlist here using the referral code SurfShares:https://waitlist.surf.social/#Flipboard #SurfFeeds #OpenSocial #SurfApp
  • 0 Votes
    1 Posts
    8 Views
    rick@rmendes.netR
    You can easily fork this repo, customize the tests to your usecase and then run the test suite. git clone git@github.com:rmdes/activitypub-tests.git — Discovery — WebFinger resolution ✓ PASS WebFinger subscribe template ✓ PASS WebFinger error handling ✓ PASS NodeInfo endpoint ✓ PASS NodeInfo well-known chain ✓ PASS NodeInfo version format ✓ PASS NodeInfo content types ✓ PASS — Actor — Actor lookup (fedify) ✓ PASS Actor required fields ✓ PASS Actor JSON structure ✓ PASS Actor attachments (PropertyValue) ✓ PASS Actor multi-key (RSA + Ed25519) ✓ PASS Actor summary / bio ✓ PASS Actor alsoKnownAs ✓ PASS Actor manuallyApprovesFollowers ✓ PASS Actor icon and image ✓ PASS Actor not found (404) ✓ PASS Actor ld+json Accept header ✓ PASS — Collections — Outbox collection ✓ PASS Followers collection ✓ PASS Following collection ✓ PASS Liked collection ✓ PASS Featured (pinned) collection ✓ PASS Featured tags collection ✓ PASS Featured tags structure ✓ PASS Collection URI resolution ✓ PASS Collection pagination ✓ PASS Outbox traversal (first page) ✓ PASS Outbox actor attribution ✓ PASS — Content Negotiation — Post returns AS2 JSON ✓ PASS HTML requests don’t get AS2 JSON ✓ PASS Root URL redirects to actor ✓ PASS Object dispatcher (dereference) ✓ PASS — Inbox — GET inbox returns 405 ✓ PASS GET shared inbox returns 405 ✓ PASS Inbox 405 headers (Allow, Content-Type) ✓ PASS Unsigned POST to inbox rejected ✓ PASS — Instance Actor & Aliases — Instance actor (Application) ✓ PASS WebFinger alias resolution ✓ PASS — HTTP Protocol — HTTP headers compliance ✓ PASS Vary and CORS headers ✓ PASS — Endpoints — Authorize interaction ✓ PASS Public profile page ✓ PASS Quick replies 404 ✓ PASS ============================================ Results: 44 passed, 0 failed, 0 skipped https://rmendes.net/articles/2026/02/25/activitypub-federation-tests-fedify-2
  • 0 Votes
    5 Posts
    7 Views
    smallcircles@social.coopS
    @xavsworld @alex @pierrenick More worried that this requires a high-speed 9600 baud modem.
  • 0 Votes
    1 Posts
    0 Views
    randomdolgok@mastodon.socialR
    Azért az #ActivityPub megdolgoztatja a szervert meg kell hagyni.Vagyis... Azért annyira nem pörgött, de nem is volt idle állapotban utána.Nem baj. A #Mastodon egy jó dolog!Marad a saját szerveren is. Csak még ki kell találnom, hogy miként legyen állandóra.#fediverse #magyar #hungarian
  • 0 Votes
    1 Posts
    1 Views
    wedistribute.org@bsky.brid.gyW
    Huge progress has been made on implementing End-To-End Encryption in #ActivityPub, thanks to efforts by The Social Web Foundation, Bonfire, and Emissary.Federated End-to-End Encrypted...
  • 0 Votes
    1 Posts
    2 Views
    hello@social.wedistribute.orgH
    Huge progress has been made on implementing End-To-End Encryption in #ActivityPub, thanks to efforts by The Social Web Foundation, Bonfire, and Emissary.https://wedistribute.org/2026/02/fediverse-e2ee-coming/
  • 0 Votes
    1 Posts
    1 Views
    admin@mastodon.raddemo.hostA
    How to Deploy #Funkwhale on #Debian #VPS This article provides a guide demonstrating how to deploy Funkwhale on Debian VPS.What is Funkwhale?Funkwhale is a self-hosted, federated audio platform for streaming and sharing music & podcasts. It uses #ActivityPub so libraries can follow/feature content across instances (like Mastodon), and it offers a web UI plus ...Continued https://blog.radwebhosting.com/deploy-funkwhale-on-debian-vps/?utm_source=mastodon&utm_medium=social&utm_campaign=mastodon.raddemo.host #opensource #postgresql #selfhosting #redis #letsencrypt #selfhosted #python #certbot
  • 0 Votes
    1 Posts
    1 Views
    radwebhosting@mastodon.socialR
    How to Deploy #Funkwhale on #Debian #VPS This article provides a guide demonstrating how to deploy Funkwhale on Debian VPS.What is Funkwhale?Funkwhale is a self-hosted, federated audio platform for streaming and sharing music & podcasts. It uses #ActivityPub so libraries can follow/feature content across instances (like Mastodon), and it offers a web UI plus ...Continued https://blog.radwebhosting.com/deploy-funkwhale-on-debian-vps/?utm_source=mastodon&utm_medium=social&utm_campaign=mastodon.social #redis #selfhosted #opensource #letsencrypt #postgresql #selfhosting #python #certbot
  • #datocurioso

    General Discussion datocurioso writeas fediverso activitypub blog
    3
    1
    0 Votes
    3 Posts
    1 Views
    damny@mastodon.nudecri.unicamp.brD
    @MDT @mdtblogmx que genial!!