Skip to content

Piero Bosio Social Web Site Personale Logo Fediverso

Social Forum federato con il resto del mondo. Non contano le istanze, contano le persone

As a community, we often ask ourselves how to attract more users to #XMPP.

General Discussion
31 14 4

Gli ultimi otto messaggi ricevuti dalla Federazione
  • @benjohn it's not a peer to peer protocol. It's federated - meaning you can pick a provider - like email or the Fediverse.

    read more

  • @daniel I was just checking out the Wikipedia page, thanks for the pointer. … does it work well peer to peer? Identifies seem to be tied to a domain?

    https://en.wikipedia.org/wiki/XMPP

    read more

  • @daniel@gultsch.social absolutely, the same naive expectations happen often when people think forums are easy to build :smile:

    @pixelschubsi@troet.cafe is definitely on to something about re-using an existing XMPP server in order to avoid the heavy lift. The less the maintenance burden for me, the better as far as I'm concerned.

    read more

  • @julian @pixelschubsi I understand the instinct of wanting to reuse the parts you already have. Protocol parsing, identities, profiles etc. However those will very quickly become extremely minor building blocks in the complexity of instant messaging.
    It's very easy to underestimate the scope and feature creep of IM. I've seen this happening in other places where people initially think that IM is just passing some messages around. And then users demand more features and then you reinvent XMPP.

    read more

  • @julian @daniel so in practice it would probably be the other way round: that heavy lifting you're rightfully afraid of has already been done and even the large tail of the remaining 20% (that in reality need 80% of the effort) are largely done.

    If we were to agree to go the XMPP route, we could have fully-featuered deployment-ready implementations of instant messaging on top of AP identities in weeks to months. If it's something entirely new on top of AP, it's going to take years.

    read more

  • @julian @daniel I'm looking at it from a different perspective. IMO the Mastodon server (as an example) doesn't need to implement XMPP itself (it could, but it doesn't need to). Just like it doesn't implement HTTP itself.

    It could instead rely on existing implementations. Take an existing XMPP server, reverse proxy its websocket endpoint, use the existing Mastodon auth to sign in, and embed an existing XMPP web client in the web frontend.

    read more

  • @silverpill @pixelschubsi @tris you can have a single account (or as I phrased it 'identity and login credentials') across different protocols.
    For example your Google account works across multiple protocols. And even in the federated world we have several cases where email address == xmpp address.
    So to repeat myself: using the same identity is good. Doesn't mean you are locked into ActivityPub if you want to build instant messaging.

    read more

  • To preface — I'm in agreement that ActivityPub probably isn't the best protocol to use for instant messaging. There's a lot of FUD still being spread about XMPP and I am outside of most of those discussions. NodeBB only supports AP at current.

    That said, there's interest in pursuing AP as a delivery protocol for instant messaging because integrating a separate protocol is a heavy lift for everybody involved. It's a heavy lift if you already support AP, and it's a heavy lift when you support no federating protocols at all. Imagine a site looking to federate... now they have to use AP+XMPP? AP+Delta? etc...

    Setting aside all the existing reasons why AP isn't ideal, I will say this... It clears the baseline expectations:

    Messages can get sent via AP :heavy_check_mark: Messages can be privately addressed via existing AP addressing mechanisms :heavy_check_mark:

    That's it. The rest is icing. Really important icing, but for 99% of conversations, icing.

    @daniel@gultsch.social @pixelschubsi@troet.cafe

    read more
Post suggeriti
  • 0 Votes
    3 Posts
    8 Views
    @smallcircles@social.coop That's my plan. I don't use any kind of session management, so all authenticated actions use HTTP signatures generated by a dedicated client key provided by the wasm module in the browser. I want to avoid any unique API calls as much as I can (although I do have a few right now).
  • 0 Votes
    8 Posts
    23 Views
    @OpinionatedGeek nice, I like that you display explicitly the recipients. So far everyone seems to want to paper over that whole mechanism with the "public" "followers" "mentions" model that Mastodon engendered.
  • 0 Votes
    1 Posts
    20 Views
    This tutorial will guide you through building a simple ActivityPub bot using Python. The bot will listen for mentions and, when it receives a message in a specific format, it will schedule and send a reminder back to the user after a specified delay. For example, if a user mentions the bot with a message like "@reminder@your.host.com 10m check the oven", the bot will reply 10 minutes later with a message like "🔔 Reminder for @user: check the oven". Prerequisites To follow this tutorial, you will need Python 3.10+ and the following libraries: apkit[server]: A powerful toolkit for building ActivityPub applications in Python. We use the server extra, which includes FastAPI-based components. uvicorn: An ASGI server to run our FastAPI application. cryptography: Used for generating and managing the cryptographic keys required for ActivityPub. uv: An optional but recommended fast package manager. You can install these dependencies using uv or pip. # Initialize a new project with uv uv init # Install dependencies uv add "apkit[server]" uvicorn cryptography Project Structure The project structure is minimal, consisting of a single Python file for our bot's logic. . ├── main.py └── private_key.pem main.py: Contains all the code for the bot. private_key.pem: The private key for the bot's Actor. This will be generated automatically on the first run. Code Walkthrough Our application logic can be broken down into the following steps: Imports and Configuration: Set up necessary imports and basic configuration variables. Key Generation: Prepare the cryptographic keys needed for signing activities. Actor Definition: Define the bot's identity on the Fediverse. Server Initialization: Set up the apkit ActivityPub server. Data Storage: Implement a simple in-memory store for created activities. Reminder Logic: Code the core logic for parsing reminders and sending notifications. Endpoint Definitions: Create the necessary web endpoints (/actor, /inbox, etc.). Activity Handlers: Process incoming activities from other servers. Application Startup: Run the server. Let's dive into each section of the main.py file. 1. Imports and Configuration First, we import the necessary modules and define the basic configuration for our bot. # main.py import asyncio import logging import re import uuid import os from datetime import timedelta, datetime # Imports from FastAPI, cryptography, and apkit from fastapi import Request, Response from fastapi.responses import JSONResponse from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization as crypto_serialization from apkit.config import AppConfig from apkit.server import ActivityPubServer from apkit.server.types import Context, ActorKey from apkit.server.responses import ActivityResponse from apkit.models import ( Actor, Application, CryptographicKey, Follow, Create, Note, Mention, Actor as APKitActor, OrderedCollection, ) from apkit.client import WebfingerResource, WebfingerResult, WebfingerLink from apkit.client.asyncio.client import ActivityPubClient # --- Logging Setup --- logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # --- Basic Configuration --- HOST = "your.host.com" # Replace with your domain USER_ID = "reminder" # The bot's username Make sure to replace your.host.com with the actual domain where your bot will be hosted. These values determine your bot's unique identifier (e.g., @reminder@your.host.com). 2. Key Generation and Persistence ActivityPub uses HTTP Signatures to secure communication between servers. This requires each actor to have a public/private key pair. The following code generates a private key and saves it to a file if one doesn't already exist. # main.py (continued) # --- Key Persistence --- KEY_FILE = "private_key.pem" # Load the private key if it exists, otherwise generate a new one if os.path.exists(KEY_FILE): logger.info(f"Loading existing private key from {KEY_FILE}.") with open(KEY_FILE, "rb") as f: private_key = crypto_serialization.load_pem_private_key(f.read(), password=None) else: logger.info(f"No key file found. Generating new private key and saving to {KEY_FILE}.") private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) with open(KEY_FILE, "wb") as f: f.write(private_key.private_bytes( encoding=crypto_serialization.Encoding.PEM, format=crypto_serialization.PrivateFormat.PKCS8, encryption_algorithm=crypto_serialization.NoEncryption() )) # Generate the public key from the private key public_key_pem = private_key.public_key().public_bytes( encoding=crypto_serialization.Encoding.PEM, format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo ).decode('utf-8') 3. Actor Definition Next, we define the bot's Actor. The Actor is the bot's identity in the ActivityPub network. We use the Application type, as this entity is automated. # main.py (continued) # --- Actor Definition --- actor = Application( id=f"https://{HOST}/actor", name="Reminder Bot", preferredUsername=USER_ID, summary="A bot that sends you reminders. Mention me like: @reminder 5m Check the oven", inbox=f"https://{HOST}/inbox", # Endpoint for receiving activities outbox=f"https://{HOST}/outbox", # Endpoint for sending activities publicKey=CryptographicKey( id=f"https://{HOST}/actor#main-key", owner=f"https://{HOST}/actor", publicKeyPem=public_key_pem ) ) 4. Server Initialization We initialize the ActivityPubServer from apkit, providing it with a function to retrieve our Actor's keys for signing outgoing activities. # main.py (continued) # --- Key Retrieval Function --- async def get_keys_for_actor(identifier: str) -> list[ActorKey]: """Returns the key for a given Actor ID.""" if identifier == actor.id: return [ActorKey(key_id=actor.publicKey.id, private_key=private_key)] return [] # --- Server Initialization --- app = ActivityPubServer(apkit_config=AppConfig( actor_keys=get_keys_for_actor # Register the key retrieval function )) 5. In-Memory Storage and Cache To serve created activities, we need to store them somewhere. For simplicity, this example uses a basic in-memory dictionary as a store and a cache. In a production application, you would replace this with a persistent database (like SQLite or PostgreSQL) and a proper cache (like Redis). # main.py (continued) # --- In-memory Store and Cache --- ACTIVITY_STORE = {} # A simple dict to store created activities CACHE = {} # A cache for recently accessed activities CACHE_TTL = timedelta(minutes=5) # Cache expiration time (5 minutes) 6. Reminder Parsing and Sending Logic This is the core logic of our bot. The parse_reminder function uses a regular expression to extract the delay and message from a mention, and send_reminder schedules the notification. # main.py (continued) # --- Reminder Parsing Logic --- def parse_reminder(text: str) -> tuple[timedelta | None, str | None, str | None]: """Parses reminder text like '5m do something'.""" # ... (implementation omitted for brevity) # --- Reminder Sending Function --- async def send_reminder(ctx: Context, delay: timedelta, message: str, target_actor: APKitActor, original_note: Note): """Waits for a specified delay and then sends a reminder.""" logger.info(f"Scheduling reminder for {target_actor.id} in {delay}: '{message}'") await asyncio.sleep(delay.total_seconds()) # Asynchronously wait logger.info(f"Sending reminder to {target_actor.id}") # Create the reminder Note reminder_note = Note(...) # Wrap it in a Create activity reminder_create = Create(...) # Store the created activities ACTIVITY_STORE[reminder_note.id] = reminder_note ACTIVITY_STORE[reminder_create.id] = reminder_create # Send the activity to the target actor's inbox keys = await get_keys_for_actor(f"https://{HOST}/actor") await ctx.send(keys, target_actor, reminder_create) logger.info(f"Reminder sent to {target_actor.id}") 7. Endpoint Definitions We define the required ActivityPub endpoints. Since apkit is built on FastAPI, we can use standard FastAPI decorators. The main endpoints are: Webfinger: Allows users on other servers to discover the bot using an address like @user@host. This is a crucial first step for federation. /actor: Serves the bot's Actor object, which contains its profile information and public key. /inbox: The endpoint where the bot receives activities from other servers. apkit handles this route automatically, directing activities to the handlers we'll define in the next step. /outbox: A collection of the activities created by the bot. but this returns placeholder collection. /notes/{note_id} and /creates/{create_id}: Endpoints to serve specific objects created by the bot, allowing other servers to fetch them by their unique ID. Here is the code for defining these endpoints: # main.py (continued) # The inbox endpoint is handled by apkit automatically. app.inbox("/inbox") @app.webfinger() async def webfinger_endpoint(request: Request, acct: WebfingerResource) -> Response: """Handles Webfinger requests to make the bot discoverable.""" if not acct.url: # Handle resource queries like acct:user@host if acct.username == USER_ID and acct.host == HOST: link = WebfingerLink(rel="self", type="application/activity+json", href=actor.id) wf_result = WebfingerResult(subject=acct, links=[link]) return JSONResponse(wf_result.to_json(), media_type="application/jrd+json") else: # Handle resource queries using a URL if acct.url == f"https://{HOST}/actor": link = WebfingerLink(rel="self", type="application/activity+json", href=actor.id) wf_result = WebfingerResult(subject=acct, links=[link]) return JSONResponse(wf_result.to_json(), media_type="application/jrd+json") return JSONResponse({"message": "Not Found"}, status_code=404) @app.get("/actor") async def get_actor_endpoint(): """Serves the bot's Actor object.""" return ActivityResponse(actor) @app.get("/outbox") async def get_outbox_endpoint(): """Serves a collection of the bot's sent activities.""" items = sorted(ACTIVITY_STORE.values(), key=lambda x: x.id, reverse=True) outbox_collection = OrderedCollection( id=actor.outbox, totalItems=len(items), orderedItems=items ) return ActivityResponse(outbox_collection) @app.get("/notes/{note_id}") async def get_note_endpoint(note_id: uuid.UUID): """Serves a specific Note object, with caching.""" note_uri = f"https://{HOST}/notes/{note_id}" # Check cache first if note_uri in CACHE and (datetime.now() - CACHE[note_uri]["timestamp"]) < CACHE_TTL: return ActivityResponse(CACHE[note_uri]["activity"]) # If not in cache, get from store if note_uri in ACTIVITY_STORE: activity = ACTIVITY_STORE[note_uri] # Add to cache before returning CACHE[note_uri] = {"activity": activity, "timestamp": datetime.now()} return ActivityResponse(activity) return Response(status_code=404) # Not Found @app.get("/creates/{create_id}") async def get_create_endpoint(create_id: uuid.UUID): """Serves a specific Create activity, with caching.""" create_uri = f"https://{HOST}/creates/{create_id}" if create_uri in CACHE and (datetime.now() - CACHE[create_uri]["timestamp"]) < CACHE_TTL: return ActivityResponse(CACHE[create_uri]["activity"]) if create_uri in ACTIVITY_STORE: activity = ACTIVITY_STORE[create_uri] CACHE[create_uri] = {"activity": activity, "timestamp": datetime.now()} return ActivityResponse(activity) return Response(status_code=404) 8. Activity Handlers We use the @app.on() decorator to define handlers for specific activity types posted to our inbox. on_follow_activity: Automatically accepts Follow requests. on_create_activity: Parses incoming Create activities (specifically for Note objects) to schedule reminders. # main.py (continued) # Handler for Follow activities @app.on(Follow) async def on_follow_activity(ctx: Context): """Automatically accepts follow requests.""" # ... (implementation omitted for brevity) # Handler for Create activities @app.on(Create) async def on_create_activity(ctx: Context): """Parses mentions to schedule reminders.""" activity = ctx.activity # Ignore if it's not a Note if not (isinstance(activity, Create) and isinstance(activity.object, Note)): return Response(status_code=202) note = activity.object # Check if the bot was mentioned is_mentioned = any( isinstance(tag, Mention) and tag.href == actor.id for tag in (note.tag or []) ) if not is_mentioned: return Response(status_code=202) # ... (Parse reminder text) delay, message, time_str = parse_reminder(command_text) # If parsing is successful, schedule the reminder as a background task if delay and message and sender_actor: asyncio.create_task(send_reminder(ctx, delay, message, sender_actor, note)) reply_content = f"<p>✅ OK! I will remind you in {time_str}.</p>" else: # If parsing fails, send usage instructions reply_content = "<p>🤔 Sorry, I didn\'t understand. Please use the format: `@reminder [time] [message]`.</p><p>Example: `@reminder 10m Check the oven`</p>" # ... (Create and send the reply Note) 9. Running the Application Finally, we run the application using uvicorn. # main.py (continued) if __name__ == "__main__": import uvicorn logger.info("Starting uvicorn server...") uvicorn.run(app, host="0.0.0.0", port=8000) How to Run the Bot Set the HOST and USER_ID variables in main.py to match your environment. Run the server from your terminal: uvicorn main:app --host 0.0.0.0 --port 8000 Your bot will be running at http://0.0.0.0:8000. Now you can mention your bot from anywhere in the Fediverse (e.g., @reminder@your.host.com) to set a reminder. Next Steps This tutorial covers the basics of creating a simple ActivityPub bot. Since it only uses in-memory storage, all reminders will be lost on server restart. Here are some potential improvements: Persistent Storage: Replace the in-memory ACTIVITY_STORE with a database like SQLite or PostgreSQL. Robust Task Queuing: Use a dedicated task queue like Celery with a Redis or RabbitMQ broker to ensure reminders are not lost if the server restarts. Advanced Commands: Add support for more complex commands, such as recurring reminders. We hope this guide serves as a good starting point for building your own ActivityPub applications! https://fedi-libs.github.io/apkit/ https://github.com/fedi-libs/apkit https://github.com/AmaseCocoa/activitypub-reminder-bot
  • 0 Votes
    1 Posts
    5 Views
    Offentlige virksomheter bør eie egen publiseringskanal Sosiale medier som twitter har vært en populær og lett tilgjengelig publiseringsplatform for ting som værmeldinger og trafikkmeldinger og oppdateringer fra politiet. Sosiale medier har også vært en fin måte for radioprogrammmer og TV-programmer som går live til å få reakskjoner og innspill fra lyttere/seere inn i programmet i sanntid. Sosiale medier har vært en ypperlig måte for journalister til å kontakte kilder og få tips. Sosiale meldinger har vært en måte for politikere å ha direkte kontakt med sine velgere og å snakke direkte til de samme velgerne. Men så har det dukket opp problemer med bruk av sosiale medier. De sosiale mediene kostet i utgangspunktet ingenting å bruke. Men ingenting er gratis, noen måtte betale for kostnadene med å holde serverene i drift. Det som nå betaler for de “gratis” sosiale mediene, er at personer som har lagt inn informasjon de er avhengig av og knyttet kontakter de er avhengige av, betaler indirekte med å bli et produkt til de som skreddersyr reklame. Et annet problem er at man kan bli sensurert på måter som virker helt vilkårlig, uten mulighet til å finne ut hvorfor man blir sensurert eller mulighet til gjøre noe med det. Og… så kan et sosialt medium bli kjøpt av en milliardær med en agenda som ikke passer med et liberalt demokrati og en tolking av ytringsfrihet som er at alle som er enig med ham kan si det de vil, mens de som mener noe annet enn ham blir straffet. Spørsmål som har blitt stilt er: hvorfor finnes det noe åpen kildekode-alternativ? Hvorfor er det ingen informasjonskanal som de som publiserer kan eie sjøl? Svar på første spørsmål er at det finnes ikke bare ett åpen kildekode-alternativ, det finnes mange. Svar på andre spørsmål er at det finnes en sånn informasjonskanal og at den heter “ActivityPub“. ActivityPub binder sammen tjenester som blandt annet mastodon og pixelfed og har eksistert siden 2018. Hva er ActivityPub Så: Hva er ActivityPub? ActivityPub er en nettverksprotokoll som brukes til meldingsutveksling over internett. ActivityPub er definert som et sett av standarder av W3C ActivityPub W3C Recommendation 23 January 2018Activity Streams 2.0 W3C Recommendation 23 May 2017 (beskriver formatet for meldingene som sendes over ActivityPub)Activity Vocabulary W3C Recommendation 23 May 2017 (lister et vokabular for bruk i ActivityPub/Activity streams) Rent teknisk så består ActivityPub av JSON over HTTP. JSON’en er JSON-LD og id til JSON-elementene er navigerbare HTTPS-URLer som peker på elementene, så ActivityPub danner faktisk et semantisk web (uten at jeg, eller noen andre jeg har sett så langt, vet hvordan denne egenskapen skal utnyttes til noe praktisk… men: artig å vite!). ActivityPub knytter mange tjenester sammen Jeg selv bruker 4 forskjellige tjenester som er knyttet sammen med ActivityPub mastodon, som er et sosialt medium av type “mikroblog“, som minner om twitter (eller “X” som noen insisterer på å kalle det idag)pixelfed, som er et sosialt medium lagd for å utveksle bilder (minner litt om instagram, men uten algoritmer og notifikasjoner)wordpress, som er en bloggeplatformbookwyrm, som er en åpen kildekode-tjeneste for bokanmeldelser, et alternativ til Amazons goodreads Jeg bruker mastodon som en slags hub mellom de andre tre tjenstene. En kamerat av meg karakteriserte mastodon som “USENET med bilder” og det var egentlig ganske treffende (for oss som husker USENET). Mastodon likner på twitter i utseende og oppførsel, men det er to klare forskjeller: Det er ikke bare én server for mastodon, dvs. det er ikke bare at man bytter ut twitter.com med mastodon.social. Man kan spinne opp sin egen server og starte å følge folk på andre servere og så begynner trafikk å flyte innDet er ingen algoritmer. Meldinger som kommer i feeden din kommer enten fraPostinger fra andre folk du følger (disse kan komme fra andre servere)Hashtagger du følger (her ser du bare meldinger med denne hashtaggen som havner på samme server som du er på) Merk: det at mastodon eller pixelfed ikke har algoritmer er ikke en egenskap som blir diktert av ActivityPub. Det å ikke ha algoritmer til å styre brukernes feed, er et aktivt valg gjort av utviklerene back mastodon og pixelfed. Metas Threads, som også støtter (til en viss grad) ActivityPub, omfavner algoritmer. Hvordan flyter trafikk i ActivityPub En ting jeg lenge lurte på, var: hvordan fungerer egentlig denne “federeringen“…? Hvordan er det postinger flyter rundt i fediverset? Den enkleste måten å forklare er å bruke et eksempel. Eksempelet er at noen, meg i dette tilfellet, setter opp en egen mastodon-server mastodon.bang.priv.no. Når jeg setter opp og starter mastodon.bang.priv.no så sitter serveren bare der uten noen artikler og uten noen brukere. Den sender ingenting og mottar ingenting. Så lager jeg brukeren @steinarb på serveren og det går fortsatt ingen trafikk ut og inn. Bruker @steinarb poster en artikkel. Siden @steinarb@mastodon.bang.priv.no ikke har noen følgere så kommer ikke artikkelen lengre enn mastodon.bang.priv.no. Så bestemmer @steinarb@mastodon.bang.priv.no seg for å følge @Gargron@mastodon.social (dvs. grunnleggeren av mastodon) mastodon.social.bang.priv.no gjør et WebFinger-kall til mastodon.social for å finne konto-URLen til @Gargron@mastodon.social (“self” i responsen fra WebFinger) { "links": [ { "rel": "self", "type": "application/activity+json", "href": "https://mastodon.social/users/Gargron" } ]} Merk: bruk av WebFinger går utenfor ActivityPub, å bruke WebFinger for å få tak i brukerinfo er noe mastodon har begynt å gjøre og er oppførsel kopiert av andre fediverse-tjenster. Eugen Rochkos bloggpost fra 23. juni 2018 How to implement a basic ActivityPub server beskriver bruk av WebFinger for å identifisere en følger og det er eneste beskrivelse jeg har funnet av WebFinger sammen med en ActivityPub-server Konto-URLen til @Gargron@mastodon.social returnerer JSON-LD for kontoen som inneholder bla innboks og offentlig krypteringsnøkkel { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1" ], "id": "https://mastodon.social/users/Gargron", "type": "Person", "preferredUsername": "Gargron", "name": "Eugen Rochko", "following": "https://mastodon.social/users/Gargron/following", "followers": "https://mastodon.social/users/Gargron/followers", "outbox": "https://mastodon.social/users/Gargron/outbox", "inbox": "https://mastodon.social/users/Gargron/inbox", "publicKey": { "id": "https://mastodon.social/users/Gargron#main-key", "owner": "https://mastodon.social/users/Gargron", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIB...DAQAB\n-----END PUBLIC KEY-----\n" }} Merk: Feltet id inneholder en URL som er samme URL som ble brukt til å laste JSON’en over, dvs. “self reference” (dette er et SHOULD-krav i standarden) @steinarb@mastodon.bang.priv.no gjør en HTTPS POST av ActivityPub Follow til innboksen til @Gargron@mastodon.social (dvs. https://mastodon.social/users/Gargron/inbox) { "@context": "https://www.w3.org/ns/activitystreams", "type": "Follow", "id": "https://mastodon.bang.priv.no/users/steinarb/outbox/123456789" "actor": "https://mastodon.bang.priv.no/users/steinarb", "object": "https://mastodon.social/users/Gargron"}Returverdien på HTTP POST av Follow er bare en kvittering på avlevert melding til innboksen, resten fortsetter asynkrontmastodon.social sjekker at “object” matcher id på en lokal brukermastodon.social gjør så en HTTPS GET til URLen i “actor” og forventer der å finne en profil av liknende type som resultatet fra “self” overmastodon.social sjekker at returnert JSON-LD fra “actor” URL inneholder en inboxmastodon.social sjekker at Signature-header på HTTPS POST-operasjonen som legger Follow-meldingen i https://mastodon.social/users/Gargron/inbox, matcher publicKey i returnert JSON-LD fra “actor” URL mastodon.social gjør en HTTPS POST til innboksen til @steinarb@mastodon.bang.priv.no med en Accept (kunne vært en Reject…) { "@context": "https://www.w3.org/ns/activitystreams", "type": "Accept", "actor": "https://mastodon.social/users/Gargron", "object": "https://mastodon.bang.priv.no/users/steinarb/outbox/123456789"}mastodon.bang.priv.no svarer på HTTPS POST med en kvittering av mottatt melding og fortsetter asynkrontmastodon.bang.priv.no bruker id til å slå opp profil og finner ut at dette er en profil den allerede har lastetSiden mastodon.bang.priv.no tidligere har lastet ned profil-JSON’en til @Gargron@mastodon.social har den allerede publicKey for kontoen og kan sjekke Signature-header på HTTPS POSTI tillegg kommer id til Follow-forespørslen dette er en Accept av, som kan sjekkes om samstemmer med en forespørsel som serveren tidligere har sendtDersom alt er oppfylt, så vil mastodon.bang.priv.no legge @Gargron@mastodon.social inn i lista over kontoer som @steinarb@mastodon.bang.priv.no følger Etter at denne runddansen er over så har @steinarb@mastodon.bang.priv.no blitt med i følgerlista til @Gargron@mastodon.social og @Gargron@mastodon.social har blitt med i lista over kontoer som @steinarb@mastodon.bang.priv.no følger. Når @Gargron@mastodon.social poster en melding, så går mastodon.social gjennom følgerlista til @Gargron@mastodon.social. En av følgerne er @steinarb@mastodon.bang.priv.no, så derfor tar mastodon.social kontakt med mastodon.bang.priv.no og legger meldingen inn i innboksen til @steinarb@mastodon.bang.priv.no. Serveren mastodon.bang.priv.no sjekker at det som kommer inn i innboksen til @steinarb@mastodon.bang.priv.no har en Signature-header som matcher “publicKey” i profilen til @Gargron@mastodon.social og dersom de matcher, slippe meldingen gjennom. Nå har en posting kommet inn fra utsida, så nå har det blitt trafikk inn på mastodon.bang.priv.no. I framtida vil alle postingene @Gargron@mastodon.social legger ut komme inn på mastodon.bang.priv.no. Alle postingene @Gargron@mastodon.social bestemmer seg for å booste vil også komme inn på mastodon.bang.priv.no. Postingene fra @Gargron@mastodon.social vi også bli synlige for eventuelle andre brukere på mastodon.bang.priv.no og dersom postingen inneholder en hashtag som følges av andre brukere, f.eks. #norsktut, så vil meldingen dukke opp i feeden til andre brukere på mastodon.bang.priv.no som følger hashtaggen. Hvis mastodon.bang.priv.no skulle være nede eller utilgjengelig når @Gargron@mastodon.social poster en melding, så vil meldingen aldri komme i feeden til @steinarb@mastodon.bang.priv.no. At man godtar at man kanskje mister en posting nå og da, fjerner mye kompleksitet fra serverene. Dersom @Gargron@mastodon.social ser at han blir fulgt av @steinarb@mastodon.bang.priv.no og bestemmer seg for å følge tilbake, så vil samme verifiseringen som over skje i motsatt retning og nye postinger fra @steinarb@mastodon.bang.priv.no vil havne i feeden til @Gargron@mastodon.social (og være potensielt synlig for alle andre brukere på mastodon.social). Men artikkelen @steinarb@mastodon.bang.priv.no postet før han hadde noen følgere, vil ikke være synlig for @Gargron@mastodon.social for den artikkelen kom seg aldri av mastodon.bang.priv.no. Bruken av nøkler som matcher URLene posterne sier at de kommer fra gjør at selv om man ikke vet hvem som er i hver ende, så kan man anta at de hører hjemme på de serverene de sier at de kommer fra. Dette er som regel “godt nok”. Hvordan er det med spam Det er fort å tenke at dersom alle kan sette opp sin egen server så vil det være lett for spammere å sette opp egne servere og hamre løs med spam inn i fediverset. Men det tar tid å sette opp en server og starte å federere trafikk. Du trenger at noen følger deg for at du skal kunne sende trafikk ut fra egen server. Ikke minst: de som skal se meldingene dine må følge deg. Og det tar kort tid for de store instansene å stenge for servere som driver med spamming. Mastodon-programvaren har en del innebygde mekanismer for å utelukke postere og servere. Så distribuert-biten er mindre sårbar for spam enn jeg fryktet. Jeg har ikke opplevd veldig mye spam i fediverset. Andre har opplevd mer: What’s with the spam on Mastodon? (Kevin’s blog <2025-09-17 Wed>)Why is Spam on Mastodon Such a Heated Topic? (Caleb Hart blog <2023-05-15 Mon>) Jeg har ennå ikke opplevd en eneste spam-melding direkte inn i feeden min. Kanskje fordi jeg ikke drar inn “new on server” inn i feeden min? Eller lytter på hashtagger som spammerne bruker? I forrige uke så jeg de to første spam/phishing-artiklene jeg har sett på mastodon. De lå ikke på toppnivå, de kom som kommentarer på postinger så de ble ikke vist før jeg så hele tråden under postingene. Jeg rapporterte begge og de er borte nå. Første opplevelsen av mastodon da jeg kom fra twitter var at det var mye mindre spam enn der jeg kom fra. Og ikke minst: mye mindre “lovlig” reklame (som i ingenting). Men siden har det dukket opp dodgy følgere av samme type som dukket opp mye på slutten på twitter og gjerne lagd samme dag og som følger mange andre profiler og har ingen egne postinger. Profilene har stort sett forsvunnet rett etterpå. Spam-profiler som jeg har sett har vært på de store instansene (f.eks. mastodon.social). Spam-profilene har vært av to typer: Helt nylagde brukere som følger mange og som blir fulgt av ingen egne brukere og uten egne postinger (opprettet av bot-farmer styrt av spammere eller trollfabrikker). Disse forsvinner oftest etter kort tidBrukere som ser legitime ut men som tydeligvis ble lagd på tida Musk kjøpte twitter (2022) og ble såvidt prøvd ut da og siden ikke har vært i bruk Det har blitt mindre av den første typen fordi mastodon-programvaren nå blir distribuert med of åpen registrering disablet som default. Og de store instansene har blitt bedre på å beskytte seg selv. Usikker på hva som skjer med kaprede profiler. Hvordan jeg bruker mastodon som hub for mine ActivityPub-tjenester Jeg har, som nevnt over, følgende tjenester som støtter ActivityPub: Mastodon: @steinarb@mastodon.socialPixelfed: @steinarb@pixelfed.socialWordPress: @steinar.bang.priv.no@steinar.bang.priv.noBookwyrm: @steinarb@bookwyrm.social Jeg har latt @steinarb@mastodon.social følge kontoene @steinarb@pixelfed.social, @steinar.bang.priv.no@steinar.bang.priv.no og @steinarb@bookwyrm.social. Jeg lar @steinarb@mastodon.social booste alle postinger som kommer fra de andre kontoene, noe som betyr at all som følger @steinarb@mastodon.social også får postinger fra de andre, mer spesialiserte, kontoene mine i feeden sin. Hvordan blir innhold fra andre tjenester vist i mastodon Mastodon er en mikrobloggetjeneste som minner om twitter (som det var før det ble ødelagt av trollbots og reklame). Mastodon tillater flere tegn pr post enn twitter gjorde (mastodon tillater som default 500 tegn, mens twitter tillater 280 tegn), tillater opp til 4 bilder (eller annet medieinnhold) pr post og dersom man legger på en URL så vil mastodon se etter OpenGraph-informasjon på URLen og bruke OpenGraph-informasjonen (tittel, beskrivelse, bilde) til å lage et preview av URLen i posten. Pixelfed er en bilde- og videodelingstjeneste ala Instagram. Jeg har aldri postet en eneste video der, men jeg poster enkelt-bilder og slideshow. Slideshow’ene kan inneholde opp til 12 bilder. Jeg følger pixelfed-kontoen min fra mastodon-kontoen min. Det betyr at alt jeg poster på pixelfed-kontoen dukker opp i feeden til mastodon-kontoen min. Figure 1: Et slideshow i pixelfed (til venstre) og samme slideshow vist som en mastodon-posting (til høyre) Enkeltbilder vises omtrent på samme måte som på pixelfed, men slideshow blir nedgradert til de 4 første bildene. I tillegg er det en lenke tilbake til web-versjonen av pixelfed-postingen. Pixelfed har mulighet til å kommentere på postinger og like postinger og fremheve postinger, på linje med det man kan gjøre i mastodon. Men mulighetene er begrensede i forhold til det man kan gjøre i mastodon. Pixelfed er først og fremst et sted du kan publisere bilder og der du kan se andres bilder. WordPress er en blogg-platform. Med ActivityPub plugin aktivert på bloggen min (wordpress.com info om ActivityPub plugin) så ble bloggen synlig som en activitypub-profil (dvs. brukerkonto), som jeg så kunne følge fra mastodon-kontoen min. I mastodon-feeden blir poster vist som full lengde tekst og med opp til 4 av eventuelle bilder på bloggen. Dvs. mastodon viser mer (av og til betydelig mer) enn de 500 tegnene mastodon selv tillater. Det er også mer formatering i bloggteksten fra wordpress, enn det mastodon legger opp til på sine egne postinger. Dersom noen som får bloggposten i mastodon-feeden sin, svarer på posten, så kommer svaret som en kommentar inn i wordpress-bloggen. Dersom noen som får bloggposten i mastodon-feeden sin merker den som favoritt, så kommer det tilbake til wordpress-bloggen som en “like”. Figure 2: En wordpress bloggposting vist i wordpress (til venstre) og samme bloggpost vist i mastodon (til venstre) Den siste tjenesten jeg bruker som støtter ActivityPub, er bookwyrm. Bookwyrm er en åpen kildekode-erstatning for Amazons goodreads. Dvs. et sted der man kan finne og lage anmeldelser på bøker. Koblingen til ActivityPub er at man kan spore endringer man gjør i forhold til bøker, som postinger på ActivityPub. Eksempel på endringer som kan publiseres på ActivityPub Wants to read: blir postet når man legger bøker inn i bokhylla (jeg bruker å poste en sånn når jeg kjøper ei ny bok)Starting to read: blir postet når man registrerer at man starter å lese ei bokFinished reading blir postet når man er ferdig med ei bok. Her pleier jeg å legge en liten anmeldelse av boka Akkurat som pixelfed så har bookwyrm en feed der man kan lese kommentarer på egne kommentarer og følge postinger fra andre. Akkurat som pixelfed, så er bookwyrms muligheter for å følge og respondere postinger begrensede i forhold til det som man kan gjøre i mastodon. Bookwyrm er først og fremst et sted til å finne og lage informasjon om bøker (anmeldelser og kommentarer og bibliografisk informasjon). Figure 3: Starte lesing av ei bok vist i bookwyrm (til venstre) og samme starting av lesing vist som en mastodon-posting (til høyre)Hvor stort er egentlig fediverset? Her er det jeg har klart å oppdrive av statistikk på fediverset: https://fediverse.observer/stats lister9.4 millioner brukerkontoer på mastodon17.3 millioner brukerkontoer totalt i fediverset pr. november 20252.3 milliober brukerkontoer som har vært aktive i løpet av siste halvår før november 2025923 tusen brukerkontoer som har vært aktive i løpet av siste måned før november 2025https://fediverse.party/en/fediverse/ lister13.7 millioner brukerkontoer2.2 millioner aktive brukerkontoer Begge stedene inneholder estimater. Stedet de to ser ut til å være enige om estimatene er på aktive brukere (brukere som har vært aktive i løpet av siste halvår) og det er på ca 2.2 millioner. Hvis vi sammenlikner med twitter, Threads og bluesky: TjenesteMånedlig aktive brukereDaglig aktive brukereKildetwitter (X)557 millioner DemandSageThreads400 millioner115 millionerDemandSageBlueSky 3.5 millionerbacklinko Så de “gamle” tjenestene er 100-gangen større enn hele fediverset. Men spesielt twitter er belemret med spambots og sockpuppets sånn at det er usikkert av hvor mange ekte mennesker som er bak kontoene. Threads er ikke så gammel men den fikk masse brukere “gratis” fra andre Meta-tjenester i oppstarten. Det som kommer nærmest i størrelse er BlueSky. Egentlig er jeg litt forbauset over at BlueSky og fediverset tilsynelatende er så nærme i størrelse for jeg har sett at en del kjente folk som jeg har fulgt på mastodon annonserte at de dro over til BlueSky for et halvårs tid sia. Hva med Threads og fediverset? Threads støtter faktisk (til en viss grad) ActivityPub-protokollen. Jeg følger mange personer jeg tidligere fulgte på twitter ved å følge threads-kontoene deres på mastodon. Men: Threads-kontoer blir ikke automatisk tilgjengelige på ActivityPubDet er ganske vanskelig å finne ut hvordan man skal slå på ActivityPub på en Threads-kontoKontoer i EU-området er ekskludert fra ActivityPub (Meta påberoper seg GDPR som årsak, noe som virker sutrete og passivt-aggresivt i mine øyne)Jeg har aldri fått noen respons tilbake fra et svar på posting til en threads-konto eller posting jeg har gjort til en bruker på en threads-konto. Men jeg vet ikke om det skyldes at postingene mine ikke flyter over til threads, eller om det bare er at jeg forsvinner i støyenThreads tillater kun fediverse-trafikk fra mastodon og kun fra utvalgte servereHva med Bluesky? Er ikke Bluesky også distribuert? Bluesky påstår at det er et distribuert system og sjefsutvikleren på Bluesky har en lang utledning om hvorfor ActivityPub ikke duger som protokoll og at BlueSky derfor har sin egen protokoll. Det finnes broer mellom bluesky og fediverset. Jeg følger flere brukere fra bluesky på min mastodon-konto og får postinger fra dem inn i feeden min. Men i likhet med Threads så aner jeg ikke om svarene og likes’ene mine kommer tilbake til bluesky. Jeg har aldri fått noen respons derfra, men vet ikke om det er fordi mine svar aldri kommer dit eller om de bare forsvinner i støyene. Min mening: dersom man er på jakt etter en erstatning for twitter som ikke er eid og kontrollert av storkapitalen så er ikke BlueSky stedet å gå. For bluesky er ikke der ennå, men det er definitivt dit de ønsker seg. Hvorfor er ikke mastodon like populært som Threads og Bluesky? Jeg tror mastodons manglende popularitet kan oppsummeres i tre ting: Manglende kritisk masse (den er forsåvidt ikke så mye mindre enn BlueSky): dvs ingen herIngen algoritmer som “krydrer” feeden din med ting som gjør deg opprørt: dvs. kjedeligHyggeligere og høfligere brukere : dvs. kjedelig De to siste har jeg ikke lyst til å gjøre noe med. Men det hadde ikke skadet om antall aktive brukere økte. Jeg tror også at da folk var på jakt etter et alternativ til twitter tilbake i 2022 så var alt snakket om “federering” og “mange instanser” og “du kan sette opp en egen server” mer til forvirring enn hjelp for de fleste. En del folk kom seg forbi den bøygen og lagde seg en bruker på mastodon.social eller andre populære instanser tilbake i 2022 som kikket seg rundt og konkluderte med at “her var det for stille” (se over) og dro igjen. Det kunne vært litt interessant å finne ut hvor mange av de 13 til 17 millionene med brukerkontoer som finnes i fediverset ble lagd i 2022 og siden ikke har blitt rørt? Jeg tror det er en del. Hvordan kan norske offentlige etater bidra til å gjøre mastodon og resten av fediverset mer populært? Dersom norske offentlige etater som Politiet og Vegvesenet og Meteorologisk Institutt tilbyr samme tjenester som de tidligere tilbød på twitter via ActivityPub så vil mange flere komme seg på mastodon (eller en annen fedivers-tjeneste) for å følge PolitiOps eller Vegmeldinger. Dersom almenkringkasteren NRK lar folk som idag må laste ned og bruke appen deres, istedenfor får samme mulighet til å delta via ActivityPub så vil det bli mulig å sende inn kommentarer og bilder til TV- og radioprogrammer som man tidligere gjorde med twitter og instagram. Igjen så er det en ting som vil få folk til å skaffe seg en mastodon- eller pixelfed-konto og kommunisere via en nettside eller en app på mobil. Hvilke muligheter finnes for å ta ibruk activitypub Enkleste mulighet er for etaten å spinne opp sin egen mastodon og/eller pixelfed-instans. Mastodon og pixelfed finnes som ferdige docker-imager, eller man kan bruke docker-compose i kildekoden for å lage egne docker-image’r. Mastodon og pixelfed finnes som nedlastbar og installerbar software for de som fortsatt har fysiske servere. “Mastodon as a service” og “pixelfed as a service” finnes fra flere tilbydere (masto.host og mastodon-utviklerne for mastodon, eliesto for pixelfed) Mastodon og pixelfed finnes også som kildekode som kan lastes ned og bygges. Men dersom man, som Politiet og NRK, allerede har en egen app, som man har lyst til å fortsette med, så kan man bruke ett av mange programvare-bibliotek for å la app’ens backend også kommunisere via ActivityPub. Her er noen få av de tilgjengelige bibliotekene: Bibliotekspråk/platformlisensFedifyTypeScriptMITGo-ActivityPubGoMITBigBoneJavaMITKort oppsummert Det er mange gode grunner for å ta ActivtyPub i bruk: Kostnadene er laveMan eier sin egen infrastruktur for publiseringMan kan nå 2.2 millioner brukere over hele verden (potensielt 115 millioner brukere på Threads dersom krav fra brukerene skulle framtvinge at de åpner ActivityPub skikkelig)Nyttig informasjon fra Vegvesen, Meterologisk institutt, NRK og Politiet vil drive aktivitet opp i fediverset og sende journalister og politikere inn ditForskjell fra RSS som mange fortsatt har er at ActivityPub tillater interaksjon med leserene: leserene kan like og svare på og dele postingene med andre som også kan like og svare på meldingene Jeg kommer ikke på noen gode grunner til å la være. #activitypub #allheimen #fodiverset #fediverse #mastodon #norsktut #pixelfed #socialmedia #socialnetwork #threads #wordpress