<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kristof Zerbe</title>
    <description>The latest articles on DEV Community by Kristof Zerbe (@kristofzerbe).</description>
    <link>https://dev.to/kristofzerbe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F778859%2Faebe795d-0606-4ef5-a964-67e040cd2e23.jpg</url>
      <title>DEV Community: Kristof Zerbe</title>
      <link>https://dev.to/kristofzerbe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kristofzerbe"/>
    <language>en</language>
    <item>
      <title>Mentions United, GitHub &amp; GraphQL</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Sun, 09 Nov 2025 16:04:05 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/mentions-united-github-graphql-1k4h</link>
      <guid>https://dev.to/kristofzerbe/mentions-united-github-graphql-1k4h</guid>
      <description>&lt;p&gt;A few months ago, in my post &lt;a href="https://dev.to/post/Using-GitHub-as-Commenting-Platform-2025-Edition"&gt;Using GitHub as Commenting Platform, 2025 Edition&lt;/a&gt;, I explained how GitHub Issues can be used quite easily as a commenting platform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub issue for each post that contains at least the post URL&lt;/li&gt;
&lt;li&gt;Add a syndication link to the issue on the post page&lt;/li&gt;
&lt;li&gt;Let Brid.gy and Webmention.io do their job&lt;/li&gt;
&lt;li&gt;Integrate Mentions United into the page to display comments via the &lt;a href="https://github.com/kristofzerbe/Mentions-United?tab=readme-ov-file#provider-webmentions" rel="noopener noreferrer"&gt;Provider Plugin Webmentions&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I've made it a habit to syndicate every new post in a GitHub issue. This saves me from having to set up my own spam-proof comment system, which is difficult or even impossible to implement on &lt;a href="https://en.wikipedia.org/wiki/Static_site_generator" rel="noopener noreferrer"&gt;SSG&lt;/a&gt; sites anyway.&lt;/p&gt;

&lt;p&gt;One might argue that the data chain &lt;strong&gt;Site&lt;/strong&gt; → &lt;strong&gt;GitHub&lt;/strong&gt; → &lt;strong&gt;brid.gy&lt;/strong&gt; → &lt;strong&gt;webmention.io&lt;/strong&gt; → &lt;strong&gt;Site&lt;/strong&gt; is quite long and offers a lot of room for errors, but all I can say to that is: Yes, you're right, because that's exactly the case at the moment!&lt;/p&gt;

&lt;p&gt;A blog like this, with two or three posts a month (if there's time), isn't exactly flooded with comments, so some time passed before I noticed that brid.gy is still polling my GitHub account, but unfortunately isn't getting any results and therefore isn't passing along any web mentions for GitHub issue comments. The chain has been broken!&lt;br&gt;
I will certainly inform the creator &lt;a href="https://snarfed.org/about" rel="noopener noreferrer"&gt;Ryan Barrett&lt;/a&gt; about this, but that doesn't help me at the moment, so I decided to build a &lt;a href="https://github.com/kristofzerbe/Mentions-United?#provider-github" rel="noopener noreferrer"&gt;native GitHub provider plugin for Mentions United&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiko.io/post/Mentions-United-GitHub-GraphQL/" rel="noopener noreferrer"&gt;Continue Reading ...&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mentionsunited</category>
    </item>
    <item>
      <title>Mentions United: Peertube Provider</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Sat, 03 May 2025 16:52:12 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/mentions-united-peertube-provider-179a</link>
      <guid>https://dev.to/kristofzerbe/mentions-united-peertube-provider-179a</guid>
      <description>&lt;p&gt;I don't often work with videos, especially not on social media. But every now and then I record one with my smartphone, for example at a concert or a soccer match, and want to blog about the event later. I then embed the MP4 files as an asset in the corresponding Markdown. Over the years, a few MB have accumulated and at some point I had to think about where to put them in order to keep the size of the blog under 1GB. Outsourcing to YouTube? I can, but that would be the wrong direction for me. Something on Fediverse or the social web? Of course ... &lt;strong&gt;Peertube&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;On the German instance &lt;a href="https://clip.place/c/kiko_io/videos" rel="noopener noreferrer"&gt;&lt;strong&gt;clip.place&lt;/strong&gt;&lt;/a&gt; operated by adminForge, a channel for the videos was quickly created and uploaded. Basically I only had to change the URL's in the &lt;code&gt;video&lt;/code&gt; and &lt;code&gt;iframe&lt;/code&gt; tags to the new ones.&lt;/p&gt;

&lt;p&gt;I don't expect a lot of comments on my videos on clip.place, as I currently use it more as a repository or syndication target, but you never know ... and since I have so far added a &lt;a href="https://github.com/kristofzerbe/Mentions-United?tab=readme-ov-file#provider-plugins" rel="noopener noreferrer"&gt;Mentions United Provider Plugin&lt;/a&gt; to all of my connected platforms that have a free API, one is now also available for &lt;a href="https://github.com/kristofzerbe/Mentions-United?tab=readme-ov-file#provider-peertube" rel="noopener noreferrer"&gt;&lt;strong&gt;Peertube&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd25qtn8az8flndi8yh8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd25qtn8az8flndi8yh8x.png" alt="Provider Peertube" width="733" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always, the data for the plugin, i.e. the video URLs, are in the frontmatter of my post. Here is an example of my concert post &lt;a href="https://dev.topost/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17"&gt;WIRTZ DNA-Tour 2024, Leipzig @ 2024-02-17&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;syndication&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Peertube&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://clip.place/w/quf3PWpmKJjRwm89axBLY2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the EJS template for my posts, it is checked whether there is at least one Peertube syndication for the current post and if so, the Peertube provider plugin is integrated and initialized after the main Mention United script in the JS code. Finally, the &lt;code&gt;retrieve&lt;/code&gt; method contained in the plugin is called via the general Mentions United &lt;code&gt;load&lt;/code&gt; and the comments are displayed on the page, if there are any.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let synPeertube = syndications?.filter(s =&amp;gt; s.host.toLowerCase() === "peertube");

&amp;lt;% if (synPeertube?.length &amp;gt; 0) { %&amp;gt;
&amp;lt;%- js('js/mentions-united-provider_peertube.js'); %&amp;gt;
&amp;lt;% } %&amp;gt;

&amp;lt;script&amp;gt;
  ...
  const mentionsUnited = new MentionsUnited({ ... });

  &amp;lt;% synPeertube?.forEach(syn =&amp;gt; { %&amp;gt;
  mentionsUnited.register(new MentionsUnitedProvider_Peertube({ 
    syndicationUrl: "&amp;lt;%- syn.url %&amp;gt;",
    syndicationTitle: "&amp;lt;%- syn.title %&amp;gt;"
  }));
  &amp;lt;% }); %&amp;gt;

  ...
  mentionsUnited.load()
    .then(() =&amp;gt; { 
      return mentionsUnited.show(); 
    })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually, everything is the same as always, but the &lt;a href="https://docs.joinpeertube.org/api/rest-getting-started" rel="noopener noreferrer"&gt;Peertube API&lt;/a&gt; still has a special feature: although there is an open RESTful API, there are no fixed auth tokens that could be generated in the account UI, for example. Instead, you first have to retrieve the client tokens via the API in order to generate an auth token valid for 24 hours, also via a REST endpoint. A little cumbersome, but you can also retrieve the comments of a video via a token-free &lt;a href="https://docs.joinpeertube.org/api-rest-reference.html#tag/Video-Feeds" rel="noopener noreferrer"&gt;Video Feed API&lt;/a&gt; JSON format and limit the amount of data with a parameter for the video ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/feeds/video-comments.json?videoId={{ID}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, this procedure has a small catch: In contrast to the REST API, the feed does not provide the complete author data of the comment, but only the display name and the profile URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kristof Zerbe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://clip.place/accounts/kiko"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thankfully, the REST endpoint &lt;code&gt;/api/v1/accounts&lt;/code&gt; does not require an auth token and so I collect all unique authors in the Peertube Provider plugin, cut the &lt;code&gt;name&lt;/code&gt; from the profile URL and use it to retrieve the complete data separately. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://clip.place/accounts/kiko"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kiko"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clip.place"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"avatars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/lazy-static/avatars/e0891432-0f45-48c5-86c0-594a395f3d91.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fileUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://clip.place/lazy-static/avatars/e0891432-0f45-48c5-86c0-594a395f3d91.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-04-01T06:16:21.893Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-04-01T06:16:21.893Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17957&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hostRedundancyAllowed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"followingCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"followersCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-03-31T07:15:05.857Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kristof Zerbe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://kiko.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-04-01T06:10:52.234Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;213&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mainly focused on the avatar URL, because a small picture of the commenter is a standard for Mentions United interactions and I don't want to deviate from this.&lt;/p&gt;

&lt;p&gt;Of course, the number of additional requests for many comments increases significantly with this approach, but I have already opened an &lt;a href="https://github.com/issues/created?issue=Chocobozzz%7CPeerTube%7C6998" rel="noopener noreferrer"&gt;Issue (#6998)&lt;/a&gt; for this in the GitHub project. Let's see when I can remove the account retrieval again.&lt;/p&gt;

&lt;p&gt;Here's an example I used to test the new Peertube Provider plugin: &lt;a href="https://kiko.io/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/#interactions" rel="noopener noreferrer"&gt;https://kiko.io/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/#interactions&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Original post on kiko.io: &lt;a href="https://kiko.io/post/Mentions-United-Peertube-Provider/" rel="noopener noreferrer"&gt;https://kiko.io/post/Mentions-United-Peertube-Provider/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fediverse</category>
      <category>mentionsunited</category>
      <category>peertube</category>
    </item>
    <item>
      <title>Reusable API proxy in just a few lines of JavaScript</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Sun, 27 Apr 2025 11:31:54 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/reusable-api-proxy-in-just-a-few-lines-of-javascript-2044</link>
      <guid>https://dev.to/kristofzerbe/reusable-api-proxy-in-just-a-few-lines-of-javascript-2044</guid>
      <description>&lt;p&gt;For my project &lt;a href="https://dev.to/projects/mentions-united"&gt;Mentions United&lt;/a&gt;, which collects interactions from various platforms via API to display them on the own page, at some point I needed a way to hide the authentication tokens required for some APIs from being used by unauthorized parties. You simply don't leave your security keys lying around in the code, especially not if it's publicly visible on GitHub. This may still work somehow for server languages, but JavaScript (and my solution is based on this) is downloaded in the browser and can then be viewed freely. Obfuscators are useless here, as they have become obsolete in the age of AI anyway.&lt;/p&gt;

&lt;p&gt;The only way to do this is to build a translator that takes a request without a token and sends one with a token to the remote site and simply passes the result along. A proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuxpnkwluurruabeo0ob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuxpnkwluurruabeo0ob.png" alt="Token vs. Proxy Token" width="576" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The aim of the solution was basically to replace the URL of an API call with that of the proxy and to leave the other components of the URL as they are, so that, for example, call &lt;/p&gt;

&lt;p&gt;https://&lt;strong&gt;myproxy.domain&lt;/strong&gt;/&lt;em&gt;&lt;strong&gt;example&lt;/strong&gt;&lt;/em&gt;/api/v1/statuses/12345&lt;/p&gt;

&lt;p&gt;becomes&lt;/p&gt;

&lt;p&gt;https://&lt;strong&gt;example.com&lt;/strong&gt;/api/v1/statuses/12345&lt;/p&gt;

&lt;p&gt;The basis of my small API proxy is the NPM package &lt;a href="https://www.npmjs.com/package/http-proxy-middleware" rel="noopener noreferrer"&gt;&lt;strong&gt;http-proxy-middleware&lt;/strong&gt;&lt;/a&gt; from &lt;a href="https://github.com/chimurai" rel="noopener noreferrer"&gt;Steven Chim&lt;/a&gt;, which I utilized to build a system that can be used via configuration for various endpoints and that runs on a server under the &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; framework &lt;a href="https://expressjs.com" rel="noopener noreferrer"&gt;Express&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was also important to me that not just anyone can use the proxy, but only call sources authorized by me, which was quite easy to achieve using the &lt;a href=""&gt;CORS middleware&lt;/a&gt; from Express.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;The data required for the proxy is stored completely in environment variables. &lt;strong&gt;&lt;code&gt;PORT&lt;/code&gt;&lt;/strong&gt; (under which port number is the server accessible) and &lt;strong&gt;&lt;code&gt;LOG&lt;/code&gt;&lt;/strong&gt; (console output of a proxy process: True/False) are optional and have default values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CORS_URLS&lt;/code&gt;&lt;/strong&gt; is a comma-separated list of all authorized sources of a request, such as &lt;code&gt;https://kiko.io,http://localhost:4000&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;PROVIDERS&lt;/code&gt;&lt;/strong&gt; is also a comma-separated list with the names of the supported API endpoints, such as &lt;code&gt;pixelfed,mastodon,peertube&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;For each of these providers, two further environment variables are then required for the base URL and the token value and which begins with the provider name. Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MASTODON_API_BASEURL&lt;/code&gt;&lt;/strong&gt;=&lt;code&gt;https://indieweb.social&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MASTODON_API_TOKEN&lt;/code&gt;&lt;/strong&gt;=&lt;code&gt;ABC123DEF456&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Here is the content of &lt;strong&gt;&lt;code&gt;app.js&lt;/code&gt;&lt;/strong&gt;, in the order in which it will be called:&lt;/p&gt;

&lt;p&gt;Import of the required libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createProxyMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http-proxy-middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initializing the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; library for reading the environment variables and setting defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initializing the Express Server and the CORS middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CORS_URLS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;optionsSuccessStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creation of a proxy as a reusable function that does the actual work by rewriting the URL of the incoming request using &lt;code&gt;pathRewrite&lt;/code&gt; and forwarding it to the &lt;code&gt;target&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createProxyMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_API_BASEURL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;changeOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;pathRewrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Proxying &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_API_BASEURL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_API_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iterating over all providers and adding the respective proxy to the routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVIDERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting the fully configured Express Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API Proxy listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVIDERS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is it. That's all the code you need for the universal API proxy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hosting
&lt;/h2&gt;

&lt;p&gt;The app can be hosted wherever Node.JS apps are supported. I opted for &lt;a href="https://railway.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Railway&lt;/strong&gt;&lt;/a&gt; because it allows you to integrate and control a project completely via GitHub. All you have to do is define a source repository and Railway takes care of the build and deployment itself. This reduces the deployment of the proxy server to checking in code changes on GitHub or changing an environment variable via the Railway interface. However, you can also use &lt;a href="https://blog.railway.com/p/github-actions" rel="noopener noreferrer"&gt;Deploy GitHub Actions to Railway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofabc740kv4sjp18np9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofabc740kv4sjp18np9t.png" alt="Railway Dashboard" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My current railway plan is the &lt;a href="https://railway.com/workspace/plans" rel="noopener noreferrer"&gt;Hobby Plan&lt;/a&gt;, which is free up to 5$ consumption of resources in terms of memory, CPU and egress (outgoing traffic). At the moment I barely get to 1$ in a month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kristofzerbe/Mentions-United-API-Proxy" rel="noopener noreferrer"&gt;GitHub - kristofzerbe/Mentions-United-API-Proxy&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;See the original post on kiko.io: &lt;a href="https://kiko.io/post/Reusable-API-proxy-in-just-a-few-lines-of-JavaScript/" rel="noopener noreferrer"&gt;https://kiko.io/post/Reusable-API-proxy-in-just-a-few-lines-of-JavaScript/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>proxy</category>
    </item>
    <item>
      <title>Mentions United ... 3, 2, 1, Go</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Fri, 04 Oct 2024 10:53:22 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/mentions-united-3-2-1-go-213f</link>
      <guid>https://dev.to/kristofzerbe/mentions-united-3-2-1-go-213f</guid>
      <description>&lt;p&gt;Blogging means putting your own opinion on public display, and of course you hope for reactions to it, otherwise you would rather write your thoughts in a small notebook and hide it in your bedside table. Blog posts are therefore always only the first half of a conversation with fellow human beings, readers, and web users. The other half is made up of reactions or, better yet, interactions. Likes, comments, or links from other bloggers who take up the opinion in their own blog post in order to spread it further or discuss it. A story emerges from these two halves.&lt;/p&gt;

&lt;p&gt;It seems that blogging is becoming more popular again. After many years of being fenced in by large “opinion corporations”, which made it easy to share information, many people have realized that this simplicity comes at a price, because the opinion itself or even the person was never the focus of interest. The content at the center of the closed networks is monetized or used to manipulate others. Today, blogging is also an expression of the desire for freedom and independence and emancipation from the large social media platforms.&lt;/p&gt;

&lt;p&gt;However, this freedom also means that you have to have a fair amount of technical understanding and that linking content is not as easy as on the big platforms, where you just have to be able to use a keyboard. Blogging platforms like Wordpress are certainly a help at this point, but at the end of the day you just have a blog in which you can write down and publish your thoughts in the form of posts, but not automatically a conversation. The most important building block for a real conversation via a blog is the hyperlink, the central element of the World Wide Web. And in contrast to the 2000s, the early days of blogging, today there are a number of ways to communicate via a link and thus to engage in an interaction that goes beyond simply commenting on a blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactions
&lt;/h2&gt;

&lt;p&gt;An example: Alice writes a post on her blog...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bob reads it and writes about the topic in his blog, mentioning Alice's post via its URL.&lt;/li&gt;
&lt;li&gt;Alice posts the URL to her post including the headline on a social web platform like Mastodon. Chris reads the post and puts a Like&lt;/li&gt;
&lt;li&gt;Daniel replies to and reposts the Mastodon post at the same time&lt;/li&gt;
&lt;li&gt;Alice syndicates the complete post, including the URL to the original, on a developer platform such as DEV and Eric comments on this post&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of the above interactions originate on Alice's blog, but they take place outside of her system and are not reflected in the original post. Bob would certainly find Eric's comment or Daniel's reply interesting, but he is just as unaware of them as Alice is of Bob's mention on his blog. A list of all interactions from the web is missing at the original post.&lt;/p&gt;

&lt;p&gt;The techniques to accomplish all this already exist. They just need to be put into practice:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;on 1:&lt;/em&gt; Bob sends a &lt;a href="https://en.wikipedia.org/wiki/Webmention" rel="noopener noreferrer"&gt;webmention&lt;/a&gt; to the endpoint linked by Alice on the post page (e.g. &lt;a href="https://webmention.io/" rel="noopener noreferrer"&gt;webmention.io&lt;/a&gt;) and Alice retrieves it there via the API&lt;br&gt;
&lt;em&gt;on 2 and 3:&lt;/em&gt; Alice has previously set up &lt;a href="https://brid.gy" rel="noopener noreferrer"&gt;brid.gy&lt;/a&gt; to also deliver Mastodon interactions to the Webmention endpoint&lt;br&gt;
&lt;em&gt;on 4:&lt;/em&gt; Alice retrieves the DEV comments via the dedicated API&lt;/p&gt;

&lt;p&gt;The interactions from the social web aka &lt;a href="https://en.wikipedia.org/wiki/Fediverse" rel="noopener noreferrer"&gt;Fediverse&lt;/a&gt; can already be seen on various blog posts today. They are usually pulled directly from Mastodon's API onto the page. However, this usually neglects one important aspect: the &lt;strong&gt;linking of the self-performed syndication on the original post&lt;/strong&gt;, in order to give readers the opportunity to land on an interaction option with just one click!&lt;/p&gt;

&lt;p&gt;But how does Alice get the interactions on her page without having to familiarize herself with the respective API's?&lt;/p&gt;

&lt;p&gt;She simply uses the client scripts of the &lt;strong&gt;&lt;a href="https://github.com/kristofzerbe/Mentions-United" rel="noopener noreferrer"&gt;Mentions United&lt;/a&gt;&lt;/strong&gt; project ...&lt;/p&gt;

&lt;h2&gt;
  
  
  Impetus
&lt;/h2&gt;

&lt;p&gt;Three years ago, I not only introduced Webmentions on this blog and wrote about them &lt;a href="https://kiko.io/post/Hexo-and-the-IndieWeb-Receiving-Webmentions/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://kiko.io/post/Hexo-and-the-IndieWeb-Sending-Webmentions/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but I also started systematically recording my manual syndications on other platforms and displaying them under the post.&lt;br&gt;
I syndicate text posts on Mastodon and, if the content is suitable, on DEV and photos on Pixelfed, Flickr and a few other (unfortunately) closed platforms. I collected all Webmentions and interactions from Mastodon and Flickr using a combination of Aaron Parecki's &lt;a href="https://webmention.io/" rel="noopener noreferrer"&gt;webmention.io&lt;/a&gt; and Ryan Barrett's &lt;a href="https://brid.gy" rel="noopener noreferrer"&gt;brid.gy&lt;/a&gt; and brought them to the post page using a client-side JavaScript.&lt;/p&gt;

&lt;p&gt;But what was always missing were interactions from other platforms. brid.gy could, in principle, collect likes and comments from Pixelfed, the largest photo platform on the social web, but a few &lt;a href="https://github.com/snarfed/bridgy/issues/927" rel="noopener noreferrer"&gt;bugs&lt;/a&gt; that have been known about for four years on the platform prevent this.&lt;/p&gt;

&lt;p&gt;Now, I'm not the most patient person, and since there was no response even after repeated inquiries in the Pixelfed issue, and it would only have solved one problem for me anyway, I thought to myself: then I'll just build something myself that is able to pick up interactions from all possible APIs and insert them into my post pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project «Mentions United»
&lt;/h2&gt;

&lt;p&gt;As Robb Knight said about his &lt;a href="https://echofeed.app/" rel="noopener noreferrer"&gt;EchoFeed&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Naming things is hard, leave me alone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The functionality and structure of the solution were easier to define. On the one hand, it was to be a &lt;strong&gt;pure client JavaScript application&lt;/strong&gt; that runs in the browser on the page to ensure that the data is always up to date and that withdrawn interactions are not taken into account. On the other hand, a &lt;strong&gt;plugin system&lt;/strong&gt; should only ever execute the scripts that are needed.&lt;/p&gt;

&lt;p&gt;The project therefore consists of a main script of only 7 KB and two types of plug-in scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provider plugins&lt;/strong&gt; to retrieve person-related interaction data and put it into a common form&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Renderer plugins&lt;/strong&gt; to transform the collected data into HTML and insert it into the page&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;main script&lt;/strong&gt; (&lt;code&gt;mentions-united.js&lt;/code&gt;) implements following relevant methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;register(plugin)&lt;/strong&gt; - Registers a plugin script for execution &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;load()&lt;/strong&gt; - Executes the &lt;code&gt;retrieve()&lt;/code&gt; method in all registered provider plugins, which collects the data from the respective APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;show()&lt;/strong&gt; - Executes the &lt;code&gt;render(interactions)&lt;/code&gt; method in all registered renderer plugins, which converts the merged data into HTML and inserts it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Further explanations of how the solution is structured can be found in the README of the &lt;a href="https://github.com/kristofzerbe/Mentions-United" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available plugins
&lt;/h3&gt;

&lt;p&gt;The initially developed plugins naturally cover my needs for this blog, but it would be great if over time more from the developer community would be added. I will contribute native provider plugins for Mastodon and Flickr in the next few weeks to reduce the current dependence on brid.gy, where it is no longer necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Provider-Plugin &lt;strong&gt;Webmentions&lt;/strong&gt; (&lt;code&gt;mentions-united-provider_webmentions.js&lt;/code&gt;)&lt;br&gt;
Get all interactions from the webmention.io API via the URL of the blog post (target), be they real webmentions or interactions from the platforms Mastodon, Bluesky, GitHub, Flickr and others that have been integrated via brid.gy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provider-Plugin &lt;strong&gt;Pixelfed&lt;/strong&gt; (&lt;code&gt;mentions-united-provider_pixelfed.js&lt;/code&gt;)&lt;br&gt;
Get all interactions from the API of a Pixelfed instance via the syndication URL (source). In the passed options, you can also specify an API proxy URL to prevent the key necessary for the retrieval from being publicly available.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provider-Plugin &lt;strong&gt;DEV.to&lt;/strong&gt; (&lt;code&gt;mentions-united-provider_devto.js&lt;/code&gt;)&lt;br&gt;
Get all comments from the DEV API via the syndication URL&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijwfzxtyi4mli40fwglx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijwfzxtyi4mli40fwglx.png" alt="Provider Overview" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once all the interactions have been collected, the renderer plugins can integrate them into the page in different ways. It is important to note that the solution outputs &lt;strong&gt;pure HTML without any styles&lt;/strong&gt;, because these are very individual after all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Renderer-Plugin &lt;strong&gt;List&lt;/strong&gt; (&lt;code&gt;mentions-united-renderer_list.js&lt;/code&gt;)&lt;br&gt;
Generates a descending sorted list of all interactions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Renderer-Plugin &lt;strong&gt;Avatars by Type&lt;/strong&gt; (&lt;code&gt;mentions-united-renderer_avatars-by-type.js&lt;/code&gt;)&lt;br&gt;
Generates an inline list of avatars for all interactions of a given type, for example Likes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Renderer-Plugin &lt;strong&gt;Total Number&lt;/strong&gt; (&lt;code&gt;mentions-united-renderer_total-number.js&lt;/code&gt;)&lt;br&gt;
Creates an anchor with the number of interactions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional information about the plugins, the options and many more details can be found on the &lt;a href="https://github.com/kristofzerbe/Mentions-United" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for the project.&lt;/p&gt;




&lt;p&gt;On the original post on kiko.io I give an insight how my implementation looks like under the SSG Hexo ... &lt;a href="https://kiko.io/post/Mentions-United-3-2-1-go/" rel="noopener noreferrer"&gt;https://kiko.io/post/Mentions-United-3-2-1-go/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>indieweb</category>
      <category>fediverse</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Exploit the feedback potential with syndication links</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Thu, 15 Aug 2024 11:39:24 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/exploit-the-feedback-potential-with-syndication-links-42oh</link>
      <guid>https://dev.to/kristofzerbe/exploit-the-feedback-potential-with-syndication-links-42oh</guid>
      <description>&lt;p&gt;With the introduction of my &lt;a href="https://kiko.io/blogroll/" rel="noopener noreferrer"&gt;blogroll&lt;/a&gt;, my morning routine has changed without me having planned it. By displaying the latest blogger posts there by fetsching them while the site is build once a day, I start reading there instead of in my standard feed reader &lt;a href="https://feedly.com/" rel="noopener noreferrer"&gt;feedly&lt;/a&gt;. I no longer subscribe to the 40 blogs currently on my list, but use &lt;a href="https://github.com/Ashinch/ReadYou" rel="noopener noreferrer"&gt;Read You&lt;/a&gt; as a blogroll reader in case I miss something. &lt;/p&gt;

&lt;p&gt;But there's one thing that drives me crazy when I read the posts of my favourite bloggers: &lt;strong&gt;As good and entertaining as they write, it's sometimes difficult to give them feedback on their articles&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Some have a comment form, others rely on webmentions, including the display of reactions from Mastodon and other platforms, for example, and still others offer no interaction options with their posts at all, but are limited to links to their social media channels or newsletter subscription forms.&lt;/p&gt;

&lt;p&gt;I am absolutely not a fan of comment forms anymore, since the &lt;a href="https://en.wikipedia.org/wiki/Fediverse" rel="noopener noreferrer"&gt;Fediverse&lt;/a&gt; and the &lt;a href="https://indieweb.org/" rel="noopener noreferrer"&gt;IndieWeb&lt;/a&gt; have taken off in recent years and allow every website operator to provide feedback channels ... Keyword Webmentions, Mastodon-Replies, etc. Who knows where form data such as name, email address and website, which is often mandatory, ends up? It doesn't matter whether the operator has built the comment form themselves or uses Disqus or similar. Having to provide such information is simply no longer necessary today and I therefore avoid it. &lt;/p&gt;

&lt;p&gt;I have now analysed the 40 blogs in my blogroll with regard to the interaction possibilities with the posts, whereby multiple answers are possible:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;No&lt;/th&gt;
&lt;th&gt;Interaction possibility under the article&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Comment Form&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Contact Me Link&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Like Button&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Webmention Form&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Display Webmentions / Reactions&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Social Media Sharing&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Follow Me Links&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Syndication Links&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the other hand, many of these bloggers have syndicated their posts (at least most of the time) on Mastodon, to draw attention to their article:&lt;/p&gt;

&lt;p&gt;&lt;span&gt;36&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;90% of bloggers therefore theoretically offer the opportunity to react to the article via the syndicated post on Mastodon and just around 22% even provide corresponding reactions such as likes or replies (mostly as &lt;a href="https://indieweb.org/Webmention" rel="noopener noreferrer"&gt;webmentions&lt;/a&gt;) under the post [4]. &lt;strong&gt;But only 5% link from their article to the syndicated post [8]&lt;/strong&gt; ... and I don't understand why!&lt;/p&gt;

&lt;p&gt;Even if you don't know the &lt;a href="https://indieweb.org/POSSE" rel="noopener noreferrer"&gt;POSSE&lt;/a&gt; principle (&lt;em&gt;&lt;strong&gt;P&lt;/strong&gt;ublish on your &lt;strong&gt;O&lt;/strong&gt;wn &lt;strong&gt;S&lt;/strong&gt;ite, &lt;strong&gt;S&lt;/strong&gt;yndicate &lt;strong&gt;E&lt;/strong&gt;lsewhere&lt;/em&gt;), propagated by the IndieWeb movement, it still makes sense to set a &lt;strong&gt;reference to your own work on another platform by linking on the original&lt;/strong&gt; ... after all, we're talking about the web here and the link is still its most powerful tool. After all, not everyone uses Mastodon as a gateway for browsing and reading, but often it is the website via which articles are accessed initially. Keyword: RSS feeds!&lt;/p&gt;

&lt;h3&gt;
  
  
  Best-Case scenario
&lt;/h3&gt;

&lt;p&gt;I read about a new post via a provided website feed and I open the page to read it. I find below the text a list of reactions already received and a link to the syndicated Mastodon post that people have reacted to. As a Mastodon user, I click on it, am redirected to the post and also respond with a like or a reply. This reaction is also displayed under the post shortly afterwards.&lt;/p&gt;

&lt;h3&gt;
  
  
  My current scenario with 85% of my blogroll
&lt;/h3&gt;

&lt;p&gt;I find out about a new post via my blogroll, open the page and read. I open the blogger's profile via the Mastodon link and start scrolling until I find the post in which they have promoted the article and respond with a like or a reply. &lt;br&gt;
If there are a few days in between and the blogger is an avid Mastodon user, it can take a while ... and often I just give up and don't give any feedback. Wasted potential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raising potential
&lt;/h3&gt;

&lt;p&gt;The ones who make it easiest for me personally and fulfil the best-case scenario are &lt;a href="https://rknight.me/" rel="noopener noreferrer"&gt;Robb Knight&lt;/a&gt; and &lt;a href="https://adactio.com/" rel="noopener noreferrer"&gt;Jeremy Keith&lt;/a&gt;, at least with his notes. Somewhat out of the ordinary is &lt;a href="https://snarfed.org/" rel="noopener noreferrer"&gt;Ryan Barrett&lt;/a&gt; whose website does not need syndication links because it is itself part of the Fediverse. &lt;a href="https://notiz.blog" rel="noopener noreferrer"&gt;Matthias Pfefferle&lt;/a&gt; at least shows a link called &lt;em&gt;Reply on the Fediverse&lt;/em&gt; in his list of existing reactions, which leads to the reaction, which unfortunately does not lead any further with contributions without reactions. &lt;/p&gt;

&lt;p&gt;I myself place the links to the syndications under every article, every note and every photo, even if this means a bit more work after publication because I have to write the links manually in the frontmatter of the post. But for my readers, a reaction is usually just a click away, because I see a post on my website as the starting point for communication. I don't just write for myself, but also for you and it's nice to see when a post elicits a positive reaction or even criticism and perhaps a conversation develops from it, which helps me to make progress. That's why I consider the reactions as part of the post and, like some of my favourite bloggers, display them below the text. Tools for this are &lt;a href="https://brid.gy/" rel="noopener noreferrer"&gt;bridgy&lt;/a&gt; and &lt;a href="https://webmention.io/" rel="noopener noreferrer"&gt;webmention.io&lt;/a&gt;, at least for the Mastodon syndications. Other platforms do not yet offer Webmention support or even an interface to retrieve the reactions, but in the future I would like to connect at least those that do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I would absolutely welcome it if all website operators started to think in circles and it became irrelevant from which direction I came across a blogger's written thoughts. Everything is linked and referenced to each other and thus enables barrier-free communication.&lt;/p&gt;




&lt;p&gt;See the original post on kiko.io: &lt;a href="https://kiko.io/post/Exploit-the-feedback-potential-with-syndication-links/" rel="noopener noreferrer"&gt;Exploit the feedback potential with syndication links&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webmention</category>
    </item>
    <item>
      <title>Push Over Webmentions</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Fri, 02 Aug 2024 07:02:38 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/push-over-webmentions-n6n</link>
      <guid>https://dev.to/kristofzerbe/push-over-webmentions-n6n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Get notified on your smartphone about an incoming webmention for your site&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you offer &lt;a href="https://indieweb.org/Webmention" rel="noopener noreferrer"&gt;Webmentions&lt;/a&gt; on your website, i.e. the option of being notified via a link on another website, it is advisable to keep an eye on the incoming mentions. Otherwise, it is all too easy for a page to become a spam sling if you &lt;a href="https://kiko.io/post/Hexo-and-the-IndieWeb-Receiving-Webmentions/" rel="noopener noreferrer"&gt;automatically display&lt;/a&gt; the incoming messages under an article, as I do using JavaScript.&lt;/p&gt;

&lt;p&gt;Such a moderation is not quite simple with static pages, because you cannot receive the mentions yourself, due to the lack of a reactive component on the web server, but need a service such as &lt;strong&gt;&lt;a href="https://webmention.io" rel="noopener noreferrer"&gt;webmention.io&lt;/a&gt;&lt;/strong&gt; to do this. There, on the dashboard, you can view the list of mentions, delete unwelcome ones and also block entire domains for the future.&lt;/p&gt;

&lt;p&gt;The only problem is: How do you get &lt;strong&gt;notified promptly&lt;/strong&gt; that a mention has been received? Checking the dashboard every few days or going through your own posts regularly is not really an option. But we all carry our &lt;strong&gt;smartphone&lt;/strong&gt; with us all the time and it constantly notifies us of new emails, signal messages and the like. Perfect ...&lt;/p&gt;




&lt;h2&gt;
  
  
  The Trigger
&lt;/h2&gt;

&lt;p&gt;When developing webmention.io, &lt;a href="https://aaronparecki.com/" rel="noopener noreferrer"&gt;Aaron Parecki&lt;/a&gt; kindly thought of implementing a &lt;strong&gt;&lt;a href="https://en.m.wikipedia.org/wiki/Webhook" rel="noopener noreferrer"&gt;Webhook&lt;/a&gt;&lt;/strong&gt; that posts every incoming mention as JSON to a configurable URL for the site. Here is his example JSON, grabbed from &lt;a href="https://webmention.io/settings/webhooks:" rel="noopener noreferrer"&gt;https://webmention.io/settings/webhooks:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234abcd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://rhiaro.co.uk/2015/11/1446953889"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://aaronparecki.com/notes/2015/11/07/4/indiewebcamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"post"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"entry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amy Guy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"photo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://webmention.io/avatar/rhiaro.co.uk/829d3f6e7083d7ee8bd7b20363da84d88ce5b4ce094f78fd1b27d8d3dc42560e.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://rhiaro.co.uk/about#me"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://rhiaro.co.uk/2015/11/1446953889"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2015-11-08T03:38:09+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repost of http://aaronparecki.com/notes/2015/11/07/4/indiewebcamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"repost-of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://aaronparecki.com/notes/2015/11/07/4/indiewebcamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"wm-property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repost-of"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this webhook, all you need is a permanently available recipient who transforms the JSON of the mention into a message for the smartphone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Receiver
&lt;/h2&gt;

&lt;p&gt;Classic webhook targets are tools such as IFTTT or Zapier, in which the received data can be converted into any other action as a trigger. However, I have been using a different solution for many years, partly because the former have become too complex and expensive for me ... &lt;a href="https://pushover.net" rel="noopener noreferrer"&gt;&lt;strong&gt;Pushover&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, this app for Android and iOS is designed solely for sending &lt;strong&gt;push notifications&lt;/strong&gt; to smartphones. You can connect the above-mentioned services (the &lt;a href="https://pushover.net/apps" rel="noopener noreferrer"&gt;list of integrations&lt;/a&gt; is quite long), use the built-in mail service or create something yourself using the API, which in the case of the webmention.io hook is both necessary and fairly simple.&lt;/p&gt;

&lt;p&gt;Pushover requires registration and a fee for the app, but this is only a one-time charge of $5 USD per device class (e.g. Android) and is well invested. The standard plan allows you to send 10,000 messages per month, which should be sufficient in most cases.&lt;/p&gt;

&lt;p&gt;After registering, you can create applications in the dashboard for which you can obtain the necessary API key and which are independent of the devices on which you want to receive the push messages afterwards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05kxc71tpf4d7ugqnvlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05kxc71tpf4d7ugqnvlk.png" alt="Pushover Application" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Transformer
&lt;/h2&gt;

&lt;p&gt;The aim of the custom development is to build a small &lt;strong&gt;Web Service&lt;/strong&gt; that listens for a webhook (POST request) on a dedicated URL, transforms the incoming data into a Pushover message and sends it to the Pushover API in order to trigger a notification on the smartphone.&lt;/p&gt;

&lt;p&gt;The choice of the environment was easy: &lt;strong&gt;Node JS&lt;/strong&gt;. I'm a JavaScript aficionado. The basic framework of the web service is pretty straightforward and can be found in many &lt;a href="https://reintech.io/blog/how-to-use-node-js-to-create-a-webhook-receiver" rel="noopener noreferrer"&gt;beginner's tutorial&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app.js:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;//transform payload into JSON&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;//do something with data in req.body&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//confirm receipt&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Webhook receiver listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;webhook&lt;/code&gt; endpoint listens for incoming POST requests, processes the JSON data and sends a confirmation back to the sender. I will skip the complete implementation here, as I have made it &lt;strong&gt;available as a project on GitHub&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kristofzerbe/webmention-pushover" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku7ej6cz8jtmkkxss5yc.png" alt="webmention-pushover" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/kristofzerbe/webmention-pushover" rel="noopener noreferrer"&gt;https://github.com/kristofzerbe/webmention-pushover&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following I describe how you can easily get the service up and running from GitHub, so it is recommended to &lt;strong&gt;fork&lt;/strong&gt; the project, because you will need to make a few adjustments to make it work for your website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjustments
&lt;/h3&gt;

&lt;p&gt;All user-defined parameters are stored in an environment file called &lt;code&gt;.env&lt;/code&gt;. This includes the &lt;strong&gt;webhook secret&lt;/strong&gt;, an arbitrary character string that must be entered in webmention.io and is cross-checked in the code, and the &lt;strong&gt;Pushover User Key&lt;/strong&gt; and the &lt;strong&gt;Pushover API Key&lt;/strong&gt; (Token), so that the service knows who is currently sending a message for which registered application and that you get from Pushover:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;.env:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=1234
WEBHOOK_SECRET=xxx
PUSHOVER_USER=yyy
PUSHOVER_TOKEN=zzz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;xxx&lt;/code&gt;, &lt;code&gt;yyy&lt;/code&gt; and &lt;code&gt;zzz&lt;/code&gt; must of course be replaced by your values. You can also specify a different port here under which the service can later be reached internally on the server, but this does not really matter in live operating mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;

&lt;p&gt;The service is started via the command &lt;code&gt;node app&lt;/code&gt; or &lt;code&gt;node app.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As it is an API web service that speaks JSON, basically any REST-capable client such as Postman is suitable for testing. I use the plugin &lt;a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client" rel="noopener noreferrer"&gt;REST Client&lt;/a&gt; from Huachao Mao in my favourite IDE &lt;strong&gt;Visual Studio Code&lt;/strong&gt;. I just need a suitable &lt;code&gt;.http&lt;/code&gt; file and can start the test against the running server within the project with a hotkey. Here is the one I use to test the webhook of a deleted mention and whose payload again comes from Aaron's example:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;test/deleted.http:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST http://localhost:1234/webhook
content-type: application/json

{
  "secret": "8eXANPkiUw4OpVqZRMkSeOn4hipPa7",
  "source": "http://rhiaro.co.uk/2015/11/1446953889",
  "target": "http://aaronparecki.com/notes/2015/11/07/4/indiewebcamp",
  "deleted": true
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the output in the command line... :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5colwc84960bocvmk16z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5colwc84960bocvmk16z.png" alt="REST Client test" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... and the result on the smartphone via Pushover app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnjuhy70u4o69xcyrzc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnjuhy70u4o69xcyrzc0.png" alt="Result Pushover Android" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hosting
&lt;/h2&gt;

&lt;p&gt;The 72 lines of NodeJS code of the webmention-pushover service now need to be installed and hosted in a central location, accessible from anywhere on the Internet. The simplest and cheapest, because it is free, option is &lt;strong&gt;&lt;a href="https://render.com" rel="noopener noreferrer"&gt;Render&lt;/a&gt;&lt;/strong&gt;. Installation and deployment is foolproof, to say the least, because you simply link your GitHub project, configure the service and Render does the rest, such as automatically deploying new versions. Commit and forget ... &lt;/p&gt;

&lt;p&gt;Yogesh Chavan has written a really helpful guide on freecodecamp.org: &lt;a href="https://www.freecodecamp.org/news/how-to-deploy-nodejs-application-with-render/" rel="noopener noreferrer"&gt;How to Deploy Your Node.js Application for Free with Render&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, only two values need to be adjusted in the settings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Build Command&lt;/td&gt;
&lt;td&gt;npm install&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start Command&lt;/td&gt;
&lt;td&gt;node app&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When the web service is installed, it is automatically assigned a URL by Render. In my case, this is &lt;code&gt;https://webmention-pushover.onrender.com&lt;/code&gt;. If you like, you can also redirect this to your own domain via &lt;em&gt;Custom Domains&lt;/em&gt; in the settings.&lt;/p&gt;

&lt;p&gt;The logs in the dashboard are quite helpful to see what the service is doing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzjnuygcar43pkt8qeph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzjnuygcar43pkt8qeph.png" alt="Render Logs" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free services are spinned off by Render after a certain period of inactivity and with the next incoming request it takes a few seconds until it is back, but this is no problem for webmention.io.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Linking
&lt;/h2&gt;

&lt;p&gt;The last step is now to link the running webhook transformation service to webmention.io. &lt;/p&gt;

&lt;p&gt;All you have to do is to enter the new service URL, &lt;strong&gt;including the endpoint &lt;code&gt;/webhook&lt;/code&gt;&lt;/strong&gt;, and the previously created WEBHOOK_SECRET into the Web Hook settings of webmention.io:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46o20a0kllzn4n5hmbu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46o20a0kllzn4n5hmbu9.png" alt="webmention.io Web Hooks" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The process is very simple in general ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3vroobw51mulku0jcig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3vroobw51mulku0jcig.png" alt="Process" width="661" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All you have to do afterwards is remove the one or other webmention that you are notified about and do not want on your site via the webmention.io dashboard and, if necessary, block an entire domain.&lt;/p&gt;

&lt;p&gt;If you have any questions, please do not hesitate to ask me. Happy mentioning ;)&lt;/p&gt;

</description>
      <category>indieweb</category>
      <category>webmentions</category>
      <category>pushover</category>
      <category>node</category>
    </item>
    <item>
      <title>Ensure Accessibility on Icon Links</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Thu, 25 Jul 2024 16:58:43 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/ensure-accessibility-on-icon-links-4j7g</link>
      <guid>https://dev.to/kristofzerbe/ensure-accessibility-on-icon-links-4j7g</guid>
      <description>&lt;p&gt;&lt;strong&gt;... automatically with a small client-side script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am gradually saying goodbye to icon fonts in this blog in favor of SVG files, which I prefer to integrate using &lt;code&gt;background-image&lt;/code&gt; in order to remain flexible. &lt;/p&gt;

&lt;p&gt;During the modification, I noticed that although I have sometimes provided icon-only links with a &lt;code&gt;title&lt;/code&gt;, these do not play any role in terms of &lt;a href="https://www.a11yproject.com/posts/creating-valid-and-accessible-links/" rel="noopener noreferrer"&gt;accessibility&lt;/a&gt;. People who are dependent on a screen reader have not yet been able to recognize what these links are.&lt;/p&gt;

&lt;p&gt;Where were two ways to change this: &lt;code&gt;aria-label&lt;/code&gt; or add text and make it invisible. The former is basically just a crutch that is &lt;a href="https://www.w3.org/WAI/ARIA/1.0/CR/implementation-report" rel="noopener noreferrer"&gt;not even fully supported&lt;/a&gt; by all browsers and so only the invisible text remained. I found a suitable and very well-working solution on &lt;a href="https://stackoverflow.com/questions/62703524/how-to-make-an-html-link-displayed-as-an-icon-accessible" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt; by &lt;a href="https://dev.to/grahamthedev"&gt;GrahamTheDev&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-icon-link"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"My Link"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"visually-hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Link&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.my-icon-link&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(/images/icons/my-icon.svg)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.visually-hidden&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */&lt;/span&gt;
  &lt;span class="nl"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/*maybe deprecated but we need to support legacy browsers */&lt;/span&gt;
  &lt;span class="nl"&gt;clip-path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/*modern browsers, clip-path works inwards from each corner*/&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My task was now to extend all textless icon links in the code with the SPAN ... or to find an &lt;strong&gt;automatism&lt;/strong&gt; for this, because all these links already have a title and it is exactly what needs to be transferred to the link text.  Since accessibility is not impaired when text is injected via JavaScript, I have found the following &lt;strong&gt;client-side&lt;/strong&gt; solution, which is embedded in the footer of each page via &lt;code&gt;script&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ensureIconLinkText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;linksWithoutText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a[href^='http']:empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;linksWithoutText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;eText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;eText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;eText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visually-hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Link without Text and Title: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;ensureIconLinkText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code in text form:&lt;br&gt;
&lt;em&gt;Find all A tags without text, run through them and if the element was not intentionally hidden and if a title is defined, create a new SPAN tag with its text value und insert this into the link, otherwise output an error in the console&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;With this approach, I can leave the links as they are and can see in the console whether I have forgotten a title somewhere.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>javascript</category>
      <category>ui</category>
    </item>
    <item>
      <title>My-well-known-feeds-and-thoughts-beyond</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Wed, 24 Jul 2024 09:20:29 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/my-well-known-feeds-and-thoughts-beyond-22kp</link>
      <guid>https://dev.to/kristofzerbe/my-well-known-feeds-and-thoughts-beyond-22kp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Every idea has to start somewhere&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Triggered by &lt;a href="https://danq.me/2023/08/23/well-known-feeds/" rel="noopener noreferrer"&gt;Dan Q.'s post '.well-known/feeds'&lt;/a&gt;, in which he suggests using a consistent per-site file along the lines of the resource identifier pattern &lt;a href="https://en.wikipedia.org/wiki/Well-known_URI" rel="noopener noreferrer"&gt;.well-known&lt;/a&gt; for providing feeds, instead of placing links on all individual pages, I have summarised my feeds accordingly today, simply because this solution feels more correct to me.&lt;/p&gt;

&lt;p&gt;His approach is actually quite smart, because he simply combines two existing techniques: &lt;a href="https://en.wikipedia.org/wiki/OPML" rel="noopener noreferrer"&gt;OPML (Outline Processor Markup Language)&lt;/a&gt; and those &lt;a href="https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml" rel="noopener noreferrer"&gt;well-known URI's&lt;/a&gt;. It was therefore not particularly difficult for me to create such a file. I do exactly the same with my &lt;a href="https://kiko.io/blogroll" rel="noopener noreferrer"&gt;blogroll&lt;/a&gt;, only now the &lt;a href="https://kiko.io/feeds" rel="noopener noreferrer"&gt;feeds&lt;/a&gt; are my own, in different formats and variants.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every idea has to start somewhere&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;... as Colin Walker said in his comment.&lt;/p&gt;

&lt;p&gt;Here's my &lt;a href="///.well-known/feeds"&gt;.well-known/feeds&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;opml&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;kiko.io&lt;span class="ni"&gt;&amp;amp;#x27;&lt;/span&gt;s Feeds&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dateCreated&amp;gt;&lt;/span&gt;2023-08-30T12:47:33.000Z&lt;span class="nt"&gt;&amp;lt;/dateCreated&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerName&amp;gt;&lt;/span&gt;Kristof Zerbe&lt;span class="nt"&gt;&amp;lt;/ownerName&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerEmail&amp;gt;&lt;/span&gt;kristof.zerbe@gmail.com&lt;span class="nt"&gt;&amp;lt;/ownerEmail&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerId&amp;gt;&lt;/span&gt;https://kiko.io&lt;span class="nt"&gt;&amp;lt;/ownerId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;docs&amp;gt;&lt;/span&gt;https://kiko.io/feeds&lt;span class="nt"&gt;&amp;lt;/docs&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Atom Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"ATOM1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/atom.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as Atom feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Atom Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"ATOM1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/atom-excerpt.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as Atom feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"RSS Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"RSS2"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/rss.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as RSS feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"RSS Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"RSS2"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/rss-excerpt.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as RSS feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"JSON Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"JSON1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feed.json"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as JSON feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"JSON Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"JSON1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feed-excerpt.json"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as JSON feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"HTML Microformats Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"MF2HTML"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feeds/index.html"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 arcticles with excerpt only as HTML Microformats feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/opml&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The OPML 'type' problem
&lt;/h2&gt;

&lt;p&gt;Dan lists his different feeds (among other links) in his file, which differ in content, but are always in the same RSS format. In contrast, I actually only have two different content types, full and excerpt, but in 4 different formats: RSS, Atom, JSON and MF2/HTML. Depending on taste.&lt;/p&gt;

&lt;p&gt;There is a problem that &lt;a href="https://rubenerd.com/interpreting-the-opml-type-attribute/" rel="noopener noreferrer"&gt;Ruben Schade already noticed&lt;/a&gt; in 2021: OPML requires the value &lt;code&gt;rss&lt;/code&gt; as type, i.e. other types such as &lt;code&gt;atom&lt;/code&gt; or the like are not provided. Dave Winer writes quite succinctly in the spec:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each sub-element of the body of the OPML document is a node of type rss or an outline element that contains nodes of type rss.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only space that opens up here is the use of the &lt;code&gt;version&lt;/code&gt; attribute, but that is just speculation. Although Dave uses it by himself in one of his OPML examples (&lt;code&gt;RSS1&lt;/code&gt;), the spec says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are no known values for Atom feeds, but they certainly could be provided.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hmmm, take it or leave it? But how else am I supposed to distinguish between the 4 different formats in the OPML? Like Ruben, I have decided to simply put the values that make the most sense to me in &lt;code&gt;version&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ATOM1&lt;/li&gt;
&lt;li&gt;RSS2&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;MF2HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see what happens ... especially as Dan's OPML approach is &lt;a href="https://github.com/Dan-Q/well-known-feeds" rel="noopener noreferrer"&gt;still a proposal&lt;/a&gt;. The one is only partly related to the other, but if .well-kown/feeds becomes more popular, maybe there will be an update to OPML where you can use the type what it seemed to be intended for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thoughts beyond ...
&lt;/h2&gt;

&lt;p&gt;In my post yesterday, I reported on the &lt;a href="https://kiko.io/post/Head-Care/" rel="noopener noreferrer"&gt;refactoring of my blog's HEAD&lt;/a&gt;, which of course also mentioned the ALTERNATE links for the feeds, which the new file could replace.&lt;/p&gt;

&lt;p&gt;If you now take a closer look at the META information, not only does the latter apply to the entire site, but also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;meta name="generator"&lt;/li&gt;
&lt;li&gt;link rel="me"&lt;/li&gt;
&lt;li&gt;link rel="webmention | pingback"&lt;/li&gt;
&lt;li&gt;link rel="blogroll"&lt;/li&gt;
&lt;li&gt;link rel="search"&lt;/li&gt;
&lt;li&gt;link rel="manifest"&lt;/li&gt;
&lt;li&gt;meta name="theme-color"&lt;/li&gt;
&lt;li&gt;meta name="color-scheme"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and of course a large part of the JSON-LD that does not refer to the individual page, such as the @type's &lt;code&gt;WebSite&lt;/code&gt;, &lt;code&gt;Organisation&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All of this information also applies on a site-per-site basis and why not make it available to the user agent centrally and save some bytes when delivering the pages as a nice additional effect? (Not that web developers sometimes leave no stone unturned in order to save characters and thus bandwidth, but are forced to use dozens of lines in the header).&lt;/p&gt;

&lt;p&gt;Of course there is some META information that can differ from page to page, such as &lt;code&gt;meta name="author"&lt;/code&gt;, but with personal blogs there is only ever one person who can be considered as the author. This could therefore also be moved to a kind of &lt;code&gt;.well-known/site-meta&lt;/code&gt;. (Not to be confused with the existing &lt;a href="https://datatracker.ietf.org/doc/html/rfc6415" rel="noopener noreferrer"&gt;host-meta&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Interestingly, Dan takes up the topic indirectly, because in his OPML there are two blocks: &lt;code&gt;Blog&lt;/code&gt; (with the feeds) and &lt;code&gt;Elsewhere&lt;/code&gt;, in which only the first URL is actually of the &lt;code&gt;rss&lt;/code&gt; type and the others are simply profile links to GitHub, YouTube and others. At least &lt;code&gt;link rel="me"&lt;/code&gt; has already been mapped this way, which I would not have expected from a feed resource.&lt;/p&gt;

&lt;p&gt;From my point of view, something like this could make sense, but is not yet fully thought out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;opml&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;kiko.io&lt;span class="ni"&gt;&amp;amp;#x27;&lt;/span&gt;s Site Meta&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dateCreated&amp;gt;&lt;/span&gt;2023-08-30T12:47:33.000Z&lt;span class="nt"&gt;&amp;lt;/dateCreated&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerName&amp;gt;&lt;/span&gt;Kristof Zerbe&lt;span class="nt"&gt;&amp;lt;/ownerName&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerEmail&amp;gt;&lt;/span&gt;kristof.zerbe@gmail.com&lt;span class="nt"&gt;&amp;lt;/ownerEmail&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ownerId&amp;gt;&lt;/span&gt;https://kiko.io&lt;span class="nt"&gt;&amp;lt;/ownerId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Site"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"site"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Url"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Author"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Kristof Zerbe"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Fediverse Creator"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"fediverse:creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"@kiko@indieweb.social"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Locale"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"locale"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"en_US"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Type"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Blog"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Generators"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"generator"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Hexo 7.2.0"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://hexo.io"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Licenses"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"license"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"CC BY-SA 4.0"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://creativecommons.org/licenses/by-sa/4.0/"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"CC BY-SA 4.0 (RDF)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"xml"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"RDF"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://creativecommons.org/licenses/by-sa/4.0/rdf"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Profiles"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"me"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Mastodon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://indieweb.social/@kiko"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"GitHub"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/kristofzerbe"&lt;/span&gt; &lt;span class="na"&gt;auth=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Mail"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"mailto:kristof.zerbe@gmail.com"&lt;/span&gt; &lt;span class="na"&gt;auth=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Webmention"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"webmention"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://webmention.io/kiko.io/webmention"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Pingback"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"pingback"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://webmention.io/kiko.io/xmlrpc"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Blogroll"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"blogroll"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"xml"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"opml"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/blogroll.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"xml"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"opensearchdescription"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/opensearch.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Icon"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt; &lt;span class="na"&gt;url=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/favicon.ico"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Web Manifest"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt; &lt;span class="na"&gt;jsonUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Theme Color"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#444"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Color Scheme"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Feeds"&lt;/span&gt; &lt;span class="na"&gt;purpose=&lt;/span&gt;&lt;span class="s"&gt;"feeds"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Atom Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"atom"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"ATOM1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/atom.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as Atom feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Atom Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"atom"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"ATOM1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/atom-excerpt.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as Atom feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"RSS Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"RSS2"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/rss.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as RSS feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"RSS Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rss"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"RSS2"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/rss-excerpt.xml"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as RSS feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"JSON Feed (Full Content)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"JSON1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feed.json"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts as JSON feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"JSON Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"JSON1"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feed-excerpt.json"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 posts with excerpt only as JSON feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outline&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"HTML Microformats Feed (Excerpt)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"mf2html"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"MF2HTML"&lt;/span&gt; &lt;span class="na"&gt;xmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feeds/index.html"&lt;/span&gt; &lt;span class="na"&gt;htmlUrl=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io"&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Last 20 arcticles with excerpt only as HTML Microformats feed"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/outline&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/opml&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could well imagine taking some of the weight off my pages, because this information should actually be centralised.&lt;/p&gt;

&lt;p&gt;Your thoughts?&lt;/p&gt;

</description>
      <category>wellknown</category>
      <category>feeds</category>
    </item>
    <item>
      <title>Head Care - Cleaning up META and LINK tags</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Thu, 18 Jul 2024 19:07:47 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/head-care-cleaning-up-meta-and-link-tags-1a93</link>
      <guid>https://dev.to/kristofzerbe/head-care-cleaning-up-meta-and-link-tags-1a93</guid>
      <description>&lt;p&gt;Actually, I just wanted to add the &lt;a href="https://opml.org/blogroll.opml" rel="noopener noreferrer"&gt;blogroll link tag&lt;/a&gt; to the header of my blog, which &lt;a href="http://davewiner.com/" rel="noopener noreferrer"&gt;Dave Winer&lt;/a&gt; mentioned in a comment on Dan Q's post &lt;a href="https://danq.me/2023/08/23/well-known-feeds/#comment-246479" rel="noopener noreferrer"&gt;.well-known/feeds&lt;/a&gt;. But since I was already in my &lt;a href="https://github.com/kristofzerbe/kiko.io/blob/master/themes/landscape/layout/_partial/head.ejs" rel="noopener noreferrer"&gt;head.ejs&lt;/a&gt;, I tidied up and reorganised it a bit. It's like a basement: you just put things in there and at some point it looks messy.&lt;/p&gt;

&lt;p&gt;So I went through every META and LINK tag and researched it in documentaries or pseudo-documentaries and also looked at a lot of page sources from sites whose creators should know how to do it. The picture wasn't consistent, how could it be otherwise, but I did pick up a few things, including new ones, and integrated them in my head. (Nice wordplay)&lt;/p&gt;




&lt;h2&gt;
  
  
  meta name="viewport"
&lt;/h2&gt;

&lt;p&gt;This HTML5 classic has remained unchanged and is essential for a responsive web design. It tells the browser how to control the dimensions and scaling of the viewport.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag" rel="noopener noreferrer"&gt;Viewport meta tag&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="generator"
&lt;/h2&gt;

&lt;p&gt;This tag indicates which software was used to generate a website and I have noticed it in some page source texts and it is also included in the HTML5 specs. I couldn't find out what deeper meaning this information has for browsers/crawlers et al, but I'm always up for useless but descriptive things :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"generator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Hexo 7.2.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whatwg.org: &lt;a href="https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names" rel="noopener noreferrer"&gt;HTML Standard - Standard metadata names&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="canonical"
&lt;/h2&gt;

&lt;p&gt;One of the most important link tags intended for search engines, it indicates whether a page is "canonical", i.e. the only true source of information. It identifies the preferred and most representative version of a piece of content that may be available on several URLs and prevents it from slipping down the search engine index due to duplicates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"canonical"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/post/Head-Care/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wikipedia: &lt;a href="https://en.wikipedia.org/wiki/Canonical_link_element" rel="noopener noreferrer"&gt;Canonical link element&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Search Central: &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls" rel="noopener noreferrer"&gt;How to specify a canonical with rel="canonical" and other methods&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="title | description | excerpt"
&lt;/h2&gt;

&lt;p&gt;These three tags form the foundation for all automatic processing of a page, be it search engines, social media services or those from the federated web. Without them, there are no headlines or teaser texts on Google and Co. and you will probably not even be included in the index.&lt;/p&gt;

&lt;p&gt;I keep fiddling around with the excerpt again and again, because it is sometimes quite complex to generate a reasonable text excerpt from my Markdown content. I haven't found the ultimate solution yet. At the moment I'm trying to help myself with RegEx and a few libs.&lt;/p&gt;

&lt;p&gt;By the way, I have completely stopped offering meta keywords at this point. They don't really work anyway and are more of a relic from the past.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Head Care - kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Cleaning up META and LINK tags"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"excerpt"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Actually, I just wanted to add ... [more text]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whatwg.org: &lt;a href="https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names" rel="noopener noreferrer"&gt;HTML Standard - Standard metadata names&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta property="og: ..."
&lt;/h2&gt;

&lt;p&gt;Open Graph, and thus the &lt;code&gt;og:&lt;/code&gt; meta tags, is an invention of Facebook, but is now very widespread, open source and has become a near-standard. It defines attributes that make an object, in this case a web page, an object in a &lt;strong&gt;social graph&lt;/strong&gt;. We already know &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; from other meta tags, which are repeated here but enriched with additional attributes. I find &lt;code&gt;og:image&lt;/code&gt; as a very useful way of specifying a representation image that is displayed as a visual equivalent when linking to a Facebook or Mastodon post, for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:site_name"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"blog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/post/Head-Care/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Head Care - kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Cleaning up META and LINK tags"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:logo"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/images/icon-192x192.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/images/social-media/Head-Care.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:locale"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"en_US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.toMore%20Info:%0AThe%20Open%20Graph%20protocol"&gt;The Open Graph protocol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta property="twitter: ..."
&lt;/h2&gt;

&lt;p&gt;The so-called Twitter Card Tags were a kind of counter-design from Twitter to Facebook's Open Graph and generally complement it today. I don't really fancy anything that comes out of Elon Musk's social media toy shop any more, but for the sake of tradition and to remain compatible, I'm leaving them in as they are.&lt;/p&gt;

&lt;p&gt;What do you think? Should they be taken out?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"twitter:card"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"summary_large_image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"twitter:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/post/Head-Care/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"twitter:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Head Care - kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"twitter:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Cleaning up META and LINK tags"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"twitter:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/images/social-media/Head-Care.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter Developer Platform: &lt;a href="https://developer.x.com/en/docs/twitter-for-websites/cards/overview/markup" rel="noopener noreferrer"&gt;Getting started with Cards&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="license"
&lt;/h2&gt;

&lt;p&gt;I have copied these two tags from &lt;a href="https://notiz.blog/" rel="noopener noreferrer"&gt;Matthias Pfefferle's notizBlog&lt;/a&gt;. I think it's good to make it machine-readable under which conditions the output of my brain and fingers can be used by others. Until now, I only had a corresponding badge with a link on my &lt;a href="https://dev.to/photos"&gt;photos page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"license"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://creativecommons.org/licenses/by-sa/4.0/"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"license"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://creativecommons.org/licenses/by-sa/4.0/rdf"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/rdf+xml"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CreativeCommons: &lt;a href="https://opensource.creativecommons.org/ccrel-guide/" rel="noopener noreferrer"&gt;CC REL by Example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microformats Wiki: &lt;a href="https://microformats.org/wiki/rel-license" rel="noopener noreferrer"&gt;rel="license"&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="author"
&lt;/h2&gt;

&lt;p&gt;So that everyone knows who is responsible by name for what you are forced to read here, there is this tag. However, there is always discussion as to whether this should be the web creator or the author of the text. In my case, it doesn't make any difference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Kristof Zerbe"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whatwg.org: &lt;a href="https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names" rel="noopener noreferrer"&gt;HTML Standard - Standard metadata names&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="fediverse:creator"
&lt;/h2&gt;

&lt;p&gt;Here's the newest kid in town ... launched by the creators of &lt;a href="https://joinmastodon.org/" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; to make journalists and others in the writing world more visible. It allows authors who work with multiple publishers or platforms to link directly to their presence in the Fediverse and thereby connecting the users to the person itself. Syndicated posts from platforms that support the new feature will have a link to the author's handle below the post. I don't really need that thing, but it's neat and who knows what else the Fedi people will come up with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fediverse:creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"@kiko@indieweb.social"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mastodon Blog - Eugen Rochko: &lt;a href="https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/" rel="noopener noreferrer"&gt;Highlighting journalism on Mastodon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chris McLeod: &lt;a href="https://chrismcleod.dev/blog/adding-the-new-mastodon-link-attribution-meta-tag/" rel="noopener noreferrer"&gt;Adding the New Mastodon Link Attribution Meta Tag&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="me"
&lt;/h2&gt;

&lt;p&gt;I use these tags to link my blog to my other identities/profile pages on the web to show that the same person is represented on both sides. A simple identity verification, which is also the foundation for IndieWeb authentication services such as &lt;a href="https://indieauth.com/" rel="noopener noreferrer"&gt;IndieAuth&lt;/a&gt;, as used on &lt;a href="//webmention.io"&gt;webmention.io&lt;/a&gt;, for example. With the additional &lt;code&gt;authn&lt;/code&gt; I select which providers are considered for authentication. Mastodon uses this for the green tick in the profile also.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"me"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://indieweb.social/@kiko"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"me authn"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/kristofzerbe"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"me authn"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"mailto:kristof.zerbe@gmail.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IndieWeb: &lt;a href="https://indieweb.org/rel-m" rel="noopener noreferrer"&gt;rel-me&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microformats Wiki: &lt;a href="https://microformats.org/wiki/RelMeAuth" rel="noopener noreferrer"&gt;RelMeAuth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;auth.hawx.me: &lt;a href="https://auth.hawx.me/" rel="noopener noreferrer"&gt;relme-auth - Sign in with your domain&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="webmention | pingback"
&lt;/h2&gt;

&lt;p&gt;This blog is a static one and lives on GitHub Pages, i.e. there is no reactive component in the background that could respond to any kind of request, apart from retrieving pages. However, if someone wants to send me a so-called Webmention (or a slightly older Pingback), someone should answer and in my case this is the fabulous &lt;a href="https://webmention.io/" rel="noopener noreferrer"&gt;Webmention.io&lt;/a&gt; service from &lt;a href="https://aaronparecki.com/" rel="noopener noreferrer"&gt;Aaron Parecki&lt;/a&gt; and I let all requesters know this, with these two tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"webmention"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://webmention.io/kiko.io/webmention"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"pingback"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://webmention.io/kiko.io/xmlrpc"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IndieWeb: &lt;a href="https://indieweb.org/Webmention" rel="noopener noreferrer"&gt;Webmention&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jan Monschke: &lt;a href="https://janmonschke.com/adding-webmentions-to-your-static-blog/" rel="noopener noreferrer"&gt;Adding webmentions to your static blog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(myself): &lt;a href="https://kiko.io/post/Hexo-and-the-IndieWeb-Receiving-Webmentions/" rel="noopener noreferrer"&gt;Hexo and the IndieWeb (Receiving Webmentions)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="blogroll"
&lt;/h2&gt;

&lt;p&gt;Now let's get to the main reason for this post ... (see introduction above). I've had a &lt;a href="https://dev.to/notes/2024/Blogroll-Feed-Heavyweight-Championship/"&gt;lot of fun&lt;/a&gt; implementing my own &lt;a href="https://dev.to/blogroll"&gt;blogroll&lt;/a&gt; into my SSG Hexo over the last few weeks and the news that someone is trying to bring the many blogrolls on the web together is exciting. No one knows exactly what that might look like yet, but it's like Dan Q. says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now all we need is some tools that can do such detection!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"blogroll"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/blogroll.xml"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io&amp;amp;#39;s Blogroll"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dan Q.: &lt;a href="https://danq.me/2024/05/03/23615/" rel="noopener noreferrer"&gt;link rel="blogroll"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;opml.org (Dave Winer): &lt;a href="https://opml.org/blogroll.opml" rel="noopener noreferrer"&gt;About blogrolls&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="search"
&lt;/h2&gt;

&lt;p&gt;Last year I integrated a &lt;a href="https://dev.to/post/Integration-of-Pagefind-in-Hexo/"&gt;search via Pagefind into this blog&lt;/a&gt; and thus made myself a little less dependent on Google. What I forgot to do is to technically explain to a search engine how my search mechanism can be used so that the user can go directly from the search to my search results. This can be done via an XML file in OpenSearch description format&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/opensearchdescription+xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/opensearch.xml"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arthur Denner: &lt;a href="https://arthurdenner.hashnode.dev/how-to-implement-a-search-shortcut-opensearch-on-any-website" rel="noopener noreferrer"&gt;How to implement a search shortcut (OpenSearch) on any website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microsoft Learn: &lt;a href="https://learn.microsoft.com/en-us/windows/win32/search/-search-federated-search-osdx-file" rel="noopener noreferrer"&gt;Creating an OpenSearch Description File in Windows Federated Search&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/OpenSearch" rel="noopener noreferrer"&gt;OpenSearch description format&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  script type="application/ld+json"
&lt;/h2&gt;

&lt;p&gt;Of course, my head element also contains the JSON-LD script that I wrote about over a year ago and which I suspect will basically replace the meta and link tag world in the long run with regard to search engines, because it's simply better structured and can probably be processed more easily as the stuff above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://schema.org/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Central: &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data" rel="noopener noreferrer"&gt;Introduction to structured data markup in Google Search&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;(myself): &lt;a href="https://kiko.io/post/Provide-Blog-Metadata-via-JSON-LD/" rel="noopener noreferrer"&gt;Provide Blog Metadata via JSON-LD&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="alternate"
&lt;/h2&gt;

&lt;p&gt;It's always good to make things easy for your users. This also applies to finding a feed on a website, in whatever form. For the so-called &lt;em&gt;feed autodiscovery&lt;/em&gt; specified in HTML5, &lt;code&gt;alternate&lt;/code&gt; link tags with the corresponding MIME type are used. I have integrated several different &lt;a href="https://dev.to/feeds"&gt;feeds&lt;/a&gt; (Atom, RSS, JSON and HTML) on my site and placed the variants with the complete text in the autodiscovery.&lt;/p&gt;

&lt;p&gt;What appeals to me now is what Dan Q. brought up in the post in the introduction to this article: &lt;a href="https://danq.me/2023/08/23/well-known-feeds" rel="noopener noreferrer"&gt;&lt;code&gt;.well-known/feeds&lt;/code&gt;&lt;/a&gt;. Let's see if I can tinker with it tonight or tomorrow ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/atom+xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/atom.xml"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io&amp;amp;#39;s Atom Feed (Full Content)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/rss+xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/rss.xml"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io&amp;amp;#39;s RSS Feed (Full Content)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feed.json"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io&amp;amp;#39;s JSON Feed (Full Content)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/mf2+html"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://kiko.io/feeds/index.html"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"kiko.io&amp;amp;#39;s HTML Microformats Feed (Excerpt)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The WHATWG Blog: &lt;a href="https://blog.whatwg.org/feed-autodiscovery" rel="noopener noreferrer"&gt;Feed Autodiscovery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;whatwg.org: &lt;a href="https://html.spec.whatwg.org/#rel-alternate" rel="noopener noreferrer"&gt;HTML Standard - Link type "alternate"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microformats Wiki: &lt;a href="https://microformats.org/wiki/rel-alternate" rel="noopener noreferrer"&gt;rel-alternate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jim Nielsen: &lt;a href="https://blog.jim-nielsen.com/2021/automatically-discoverable-rss-feeds/" rel="noopener noreferrer"&gt;Making Your RSS Feeds Automatically Discoverable&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="manifest"
&lt;/h2&gt;

&lt;p&gt;Is a blog an app? I think so. An app for reading stuff. That's why it makes sense for me to technically raise my blog to the same level as WebApps/PWA's by offering a corresponding &lt;a href="///manifest.json"&gt;manifest&lt;/a&gt;, that shows the browser how to install kiko.io on the system, if the user want to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Manifest" rel="noopener noreferrer"&gt;Web app manifests&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="icon | apple-touch-icon"
&lt;/h2&gt;

&lt;p&gt;In addition to the information on the manifest, which already contains pretty much all the information a browser needs to display the website nicely on his OS, this classic is part of the standard repertoire and should not be missing. Nothing is duller than a browser tab without an icon. Since Apple always does everything a little differently, a PNG image should be provided for iOS.&lt;/p&gt;

&lt;p&gt;As with all resources, relative paths are useful here so that the site is also displayed correctly during local development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/images/apple-touch-icon.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;W3C: &lt;a href="https://www.w3.org/2005/10/howto-favicon" rel="noopener noreferrer"&gt;How to Add a Favicon to your Site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;webhint: &lt;a href="https://webhint.io/docs/user-guide/hints/hint-apple-touch-icons/" rel="noopener noreferrer"&gt;Use Apple Touch Icon&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="theme-color"
&lt;/h2&gt;

&lt;p&gt;This tag is similar to the icon and can also be found in the manifest, but it is not a bad idea to tell the browser the desired color for the environment in this way too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#444"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color" rel="noopener noreferrer"&gt;theme-color&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  meta name="color-scheme"
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;color-scheme&lt;/code&gt; tag points in a slightly different direction than the previous one. It can be used to specify whether and which color scheme the website supports: &lt;strong&gt;light&lt;/strong&gt; or &lt;strong&gt;dark&lt;/strong&gt; or both. I am a great advocate of &lt;a href="https://dev.to/post/don-t-be-ignorant-and-offer-a-theme-switch/"&gt;individual theme switches&lt;/a&gt;, but for this you first have to implement and offer two different themes in the CSS. This tag makes the presence of these known to the browser.&lt;/p&gt;

&lt;p&gt;I have also used the tag so far and built a theme switch around it, but I have overlooked one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Providing both keywords indicates that the first scheme is preferred (by the author), but the second is also acceptable if the user prefers it instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've defined them the wrong way round so far, as I'm not a fan of dark themes and prefer the light ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;web.dev (Thomas Steiner): &lt;a href="https://web.dev/articles/color-scheme" rel="noopener noreferrer"&gt;Improved dark mode default styling with the color-scheme CSS property and the corresponding meta tag&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="preload"
&lt;/h2&gt;

&lt;p&gt;I use special web fonts and an individual photo for each page. To preload these files before they are actually used, it makes sense to use the PRELOAD link, which tells the browser: &lt;em&gt;"Hey, I'm going to need this anyway. Load it now"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It is essential to inform the browser of the correct MIME type of the resource and to indicate the specific class via the &lt;code&gt;as&lt;/code&gt; attribute. The additional attribute &lt;code&gt;crossorigin&lt;/code&gt; is only required if the resource is not located on your own server, as it is then retrieved using a CORS request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"font"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"font/woff2"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/fonts/lexend/webfonts/Lexend-ExtraLight.woff2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/jpeg"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/images/social-media/Head-Care.png"&lt;/span&gt; &lt;span class="na"&gt;imagesrcset=&lt;/span&gt;&lt;span class="s"&gt;"/images/social-media/Head-Care.png 480w, /images/social-media/Head-Care.png 768w"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Doc: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload" rel="noopener noreferrer"&gt;rel=preload&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;whatwg.org: &lt;a href="https://html.spec.whatwg.org/multipage/links.html#link-type-preload" rel="noopener noreferrer"&gt;HTML Standard - Link type "preload"&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  link rel="stylesheet"
&lt;/h2&gt;

&lt;p&gt;Yes, of course. For the sake of completeness, I have to mention the links via which the CSS is loaded into the page as an external resource also. Without these, the web would look dull and empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/dist/asset-bundle.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More Info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link" rel="noopener noreferrer"&gt;The External Resource Link element&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;freeCodeCamp: &lt;a href="https://www.freecodecamp.org/news/how-to-link-css-to-html/" rel="noopener noreferrer"&gt;How to Link CSS to HTML – Stylesheet File Linking&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Small cause, big effect. I learnt a lot from refactoring my HEAD and I have to admit that I'm tempted to take a closer look at all those &lt;a href="https://wiki.whatwg.org/wiki/MetaExtensions" rel="noopener noreferrer"&gt;extensions&lt;/a&gt;. I would at least be satified, if the work I have done here, can also improve your header a little.&lt;/p&gt;

&lt;p&gt;Happy Coding :)&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Batman Comics with pure CSS</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Thu, 11 Jul 2024 09:46:39 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/batman-comics-with-pure-css-5ggk</link>
      <guid>https://dev.to/kristofzerbe/batman-comics-with-pure-css-5ggk</guid>
      <description>&lt;p&gt;I really can't say that I'm a big fan of TailwindCSS, because I don't like decorating my HTML with dozens of predefined classes instead of implementing a meaningful class directly in my own CSS code.&lt;/p&gt;

&lt;p&gt;However, &lt;a href="https://front-end.social/@alvaromontoro" rel="noopener noreferrer"&gt;Alvaro Montoro&lt;/a&gt; shows how you can use predefined classes in a meaningful and even hilarious way with his &lt;a href="https://alvaromontoro.com/sansjs/demos/batman-comic-css/" rel="noopener noreferrer"&gt;&lt;strong&gt;Batman-Comic.CSS project&lt;/strong&gt;&lt;/a&gt;, which enables you to create a comic without having the slightest idea about drawing comics!&lt;/p&gt;

&lt;p&gt;Define the basic structure, add CSS classes for the various facial expressions, add texts for the speech bubbles ... Done. It's so amazingly cool and I think I will use it frequently in my posts, because sometimes a comic says more than a thousand words.&lt;/p&gt;

&lt;p&gt;Here is a classic ... consisting of 10 HTML tags and a linked CSS file (and some tiny style adjustments ;):&lt;/p&gt;

&lt;p&gt;(Just a picture here on dev.to)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxe8752uxf4kuihdbty6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxe8752uxf4kuihdbty6.png" alt="Batman Comic" width="681" height="629"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;See the original post on kiko.io: &lt;a href="https://kiko.io/notes/2024/Batman-Comics-with-pure-CSS/" rel="noopener noreferrer"&gt;Batman Comics with pure CSS&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>batman</category>
      <category>comic</category>
    </item>
    <item>
      <title>IndieFediWebVerse</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Wed, 27 Dec 2023 14:53:21 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/indiefediwebverse-3hpe</link>
      <guid>https://dev.to/kristofzerbe/indiefediwebverse-3hpe</guid>
      <description>&lt;p&gt;Today I was listening to Mike McCue's Dot Social podcast where he &lt;a href="https://about.flipboard.com/inside-flipboard/eugen-rochko/" rel="noopener noreferrer"&gt;talks&lt;/a&gt; with Eugen Rochko about Mastodon and ActivityPub. At around 12:30 Eugen talked about Twitter and the fact that Tumblr is willing to join the Fediverse network and he asked a question, which he immediately tried to improve again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think that is the future, because why should we have all these different accounts ... ehh ... like ... ehh ... all these different experiences that are required to connect with different people, when we could have just one account and connect with everyone who uses different services just from one account?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://flipboard.video/videos/embed/60495342-c321-4949-9cc9-0fa1a1f2d788?start=12m30s" rel="noopener noreferrer"&gt;Video, The State of the Federation, with Mastodon’s Eugen Rochko&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This slip of the tongue and Eugens emphasis on "ONE account" hit me, because it points to something that has bothered me about Fediverse so far, as wonderfully open and forward-looking as it is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It doesn't free me from the need to be a duplicate of myself everywhere in the form of an account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;... because I'm not only a Mastodon, Pixelfed or &amp;lt;you name it &amp;gt; user. I'm me at kiko.io in the first place!&lt;/p&gt;




&lt;h2&gt;
  
  
  My &lt;del&gt;home&lt;/del&gt; website is my castle
&lt;/h2&gt;

&lt;p&gt;I consider my website &lt;a href="https://kiko.io" rel="noopener noreferrer"&gt;kiko.io&lt;/a&gt; to be the center of my digital and public persona. This is me, in terms of longer articles about this and that and an initial place for my photos, that are suitable for a wide audience. From here I transport my content to other channels like Mastodon, dev.to or photo platforms like Pixelfed and some old classic silos like Flickr or 500px.&lt;/p&gt;

&lt;p&gt;This syndication is done manually and I'm fine with that at the moment. For one thing, some platforms have no API at all and for another, they all have a slightly different post structure or even length limits. Tools like Bridgy Fed, which turn a website into its own Fediverse actor, won't help me and I don't really want that either, because I want to decide on the form of distribution myself. Maybe one day there will be an AI-driven tool that will help me to summarize my content in my own language specifically for the platform I want my content to syndicate and post it with a click. But for now I want to decide how, where and when.&lt;/p&gt;

&lt;p&gt;But my point here is a different one: I am the owner, creator and operator of this blog, or to put it in ActivityPub language: &lt;strong&gt;@me@kiko.io&lt;/strong&gt;. But I am not only interested in publishing my articles or photos, but also in communicating in other ways, away from my own content, but still with reference to myself and my main identity.&lt;/p&gt;

&lt;p&gt;On Mastodon, however, I am &lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/kiko"&gt;@kiko&lt;/a&gt;@indieweb.de&lt;/em&gt; and on Pixelfed you interact with &lt;em&gt;@kristofz@pixelfed.social&lt;/em&gt; because the name 'kiko' was already taken. To get to the point: I always appear on the platforms as someone else, away from my main identity. I chat with one identity, comment on a photo with another and like a book review with a completely different one. It's a bit of a split personality a la Dr. Jekyll and Mister Hyde, but it should be always &lt;em&gt;@me@kiko.io&lt;/em&gt;, or at least something like that.&lt;/p&gt;

&lt;p&gt;The difference between the classic web services and all Fediverse services is, that they are interoperable, so you need and get an address and not just a login name and password. But they behave in this particular way like the classic services: they create the address on their domain for you after you have logged in with your username and password. You can't bring the address with you. &lt;/p&gt;

&lt;p&gt;The only widely known interoperable service where this is not the case is email, which is often used as an example for comparison with the Fediverse. Of course you can also use a Gmail or Yahoo mail address, but in my case me@kiko.io is a working mail address, even if I rarely use it. This is not possible in the Fediverse.&lt;/p&gt;

&lt;p&gt;I recently tried to make my main identity discoverable using a WebFinger file, at least in the Fediverse, but is less than a crutch and has its pitfalls.&lt;/p&gt;

&lt;p&gt;I would like to see some sort of alias system where you can choose which alias of your main identity you want to use on each platform. Using the main identity doesn't work if you are active on more than one platform because the Fediverse inbox/outbox system requires a unique address. But &lt;em&gt;@mastodon@kiko.io&lt;/em&gt; or &lt;em&gt;@pixelfed@kiko.io&lt;/em&gt; would work. Along a pattern such as &lt;code&gt;@{Platform}@{Own Domain}&lt;/code&gt; ... or even better and more flexible by a Webfinger-like file on your (static) website which defines the endpoints of a particular alias of your main identity to the plattform.&lt;/p&gt;

&lt;p&gt;For example, if I no longer want to run Mastodon via indieweb.social (Tim, that will never happen), then I export my data there, import it to mastodon.social, for example, and just change the endpoint of the address &lt;em&gt;@mastodon@kiko.io&lt;/em&gt; in my file, deploy and I'm done. Full control.&lt;/p&gt;

&lt;p&gt;Now, if we imagine there was a tool that could collect and aggregate the interactions that occur in the inboxes of such main identity aliases under the full control of the domain owner, would feel like a dream come true, at least for me. I appreciate &lt;a href="https://brid.gy/" rel="noopener noreferrer"&gt;brid.gy&lt;/a&gt; and &lt;a href="https://webmention.io/" rel="noopener noreferrer"&gt;webmention.io&lt;/a&gt; and the work of their authors and operators Ryan Barret and Aaron Parecki, but these tools are band-aids to make the shortcomings of today's Fediverse somewhat bearable.&lt;/p&gt;




&lt;p&gt;Unfortunately, I am not in a position or capable to implement everything like this technically, even if I understand the principles behind all the stuff, but surely there is someone out there with similar ideas and the skills to go with it.&lt;/p&gt;

&lt;p&gt;This reorganization or rather evolution of the Fediverse would really be a liberation from the old ways of functioning and would actually give control back to the users, who are the real basis of social media platforms.&lt;/p&gt;

&lt;p&gt;A little more &lt;a href="https://indieweb.org/" rel="noopener noreferrer"&gt;IndieWeb&lt;/a&gt; would be good for the Fediverse. Just in the direction of a IndieFediWebVerse...&lt;/p&gt;




&lt;p&gt;See the original post on kiko.io: &lt;a href="https://kiko.io/post/IndieFediWebVerse/" rel="noopener noreferrer"&gt;IndieFediWebVerse&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fediverse</category>
      <category>indieweb</category>
      <category>identity</category>
    </item>
    <item>
      <title>Hexo, WebFinger and better discoverability</title>
      <dc:creator>Kristof Zerbe</dc:creator>
      <pubDate>Fri, 08 Dec 2023 15:25:56 +0000</pubDate>
      <link>https://dev.to/kristofzerbe/hexo-webfinger-and-better-discoverability-292f</link>
      <guid>https://dev.to/kristofzerbe/hexo-webfinger-and-better-discoverability-292f</guid>
      <description>&lt;p&gt;Recently I read the &lt;a href="https://blog.maartenballiauw.be/post/2022/11/05/mastodon-own-donain-without-hosting-server.html" rel="noopener noreferrer"&gt;blog post "Mastodon on your own domain without hosting a server"&lt;/a&gt; by &lt;a href="https://mastodon.online/@maartenballiauw" rel="noopener noreferrer"&gt;Maarten Balliauw&lt;/a&gt;, which dealt with how to become more visible in the Fediverse, more precisely in Mastodon, with &lt;strong&gt;your own domain&lt;/strong&gt;, because in contrast to the &lt;strong&gt;Indieweb&lt;/strong&gt; approach, the &lt;strong&gt;Fediverse&lt;/strong&gt; relies on Actors (&lt;code&gt;@USER@INSTANCE&lt;/code&gt;) of the respective instance/platform and can only include your own domain, if it becomes a Fediverse endpoint itself.&lt;/p&gt;

&lt;p&gt;In my case, the latter is not possible because this blog is a static site, generated via &lt;a href="https://hexo.io" rel="noopener noreferrer"&gt;Hexo&lt;/a&gt; and hosted on GitHub. It simply lacks a modifiable active server component.&lt;/p&gt;

&lt;p&gt;However, Maarten has found a trick to at least make it findable in Mastadon via his own domain. First, he explains how Fediverse platforms work in general:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;-- Mastodon (and others) use ActivityPub as their protocol to communicate between "actors".&lt;br&gt;
-- Actors are discovered using WebFinger, a way to attach information to an email address, or other online resource.&lt;br&gt;
-- WebFinger lives on /.well-known/webfinger on a server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;His idea was to simply copy the WebFinger file to his server and make it available in the same way, to allow the Fediverse server to find the correct actor, so search for &lt;code&gt;@me@mydomain.xxx&lt;/code&gt; and find &lt;code&gt;@me@my-fediverse-instance.xxx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy a file and deliver it via Hexo over &lt;code&gt;.wellknown/webfinger&lt;/code&gt;? What can be so difficult about that...&lt;/p&gt;




&lt;h2&gt;
  
  
  Download the WebFinger file
&lt;/h2&gt;

&lt;p&gt;As Maarten describes, the WebFinger file (JSON) can always be found on a URL according to the following pattern:&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://&amp;lt;your mastodon server&amp;gt;/.well-known/webfinger?resource=acct:&amp;lt;your account&amp;gt;@&amp;lt;your mastodon server&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have my Mastodon account 'kiko' at indieweb.social and the path to the download was accordingly: &lt;a href="https://indieweb.social/.well-known/webfinger?resource=acct:kiko@indieweb.social" rel="noopener noreferrer"&gt;https://indieweb.social/.well-known/webfinger?resource=acct:kiko@indieweb.social&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Solution 1 - Copy and provide as static file
&lt;/h2&gt;

&lt;p&gt;First of all, Hexo knows nothing about static files. You have to teach it, for example, using the &lt;a href="https://github.com/niahoo/hexo-generator-copy" rel="noopener noreferrer"&gt;&lt;strong&gt;hexo-generator-copy&lt;/strong&gt; plugin&lt;/a&gt;, which I did a long time ago because I deliver a lot of such files, like images, photos, manifests and so on.&lt;/p&gt;

&lt;p&gt;Including the WebFinger file in the .wellknown folder in the generation was therefore an obvious choice ... but did not work, as the plugin ignores all files and folders that begin with a dot or underscore. This doesn't matter on Windows, but it probably does on other platforms, so I wanted to leave the plugin's code untouched and wrote another generator that then generates the file separately into the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const log = require('hexo-log')({ debug: false, silent: false });
const path = require('path');
const fs = require('hexo-fs');

hexo.extend.generator.register("wellknown-webfinger", async function() {

  log.info("Processing .well-known/webfinger ...");

  const _rootPath = hexo.base_dir;
  const _path = ".well-known/webfinger";

  let content = "";

  let filePath = path.join(_rootPath, this.config.static_dir, _path);
  if (fs.existsSync(filePath)) { 
    json = JSON.parse(fs.readFileSync(filePath)); 
    content = JSON.stringify(json); // flatten JSON
  }

  let result = {
    data: content,
    path: _path
  };

  return result;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole thing is not magic, because it simply reads the local file from &lt;code&gt;/static/.wellknown/webfinger&lt;/code&gt; and returns the output path and the flattened content to be rendered as the result.&lt;/p&gt;

&lt;p&gt;But somehow it didn't feel right to first download a file manually and then bypass a generator with another generator in order to deliver the file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution 2 - Download and deliver during generation
&lt;/h2&gt;

&lt;p&gt;A short time later, I deleted the local file again and rewrote the generator, because the first solution didn't make sense to me. Generating my blog now takes less than a minute anyway, so it doesn't matter whether I fetch the WebFinger file directly from the server and write it straight to the output via &lt;code&gt;hexo.route.set&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const log = require('hexo-log')({ debug: false, silent: false });
const axios = require("axios");

hexo.extend.generator.register("wellknown-webfinger", async function() {

  log.info("Processing .well-known/webfinger ...");

  const url = 
    `https://${this.config.mastodon.server}/.well-known/webfinger?resource=acct:${this.config.mastodon.user}@${this.config.mastodon.server}`;

  const _path = ".well-known/webfinger";

  axios.get(url).then(response =&amp;gt; {
    let json = response.data;
    hexo.route.set(_path, json);
  });

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;After deploying to GitHub I was able to search on a Fediverse platform for &lt;code&gt;@kristof@kiko.io&lt;/code&gt; and the result is the corresponding account of my Mastodon instance. :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwo0btm6yp1sh3dbpnq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwo0btm6yp1sh3dbpnq5.png" alt="Search on Mastodon" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, this currently only works with one instance and the user specification is arbitrary (as Maarten also notes). For example, if I search on Pixelfed, my Mastodon account is also displayed, but not my Pixelfed account, which is also available there, because I have not integrated Pixelfeds WebFinger file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa68j3bbjatyl848005h0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa68j3bbjatyl848005h0.png" alt="Search on Pixelfed" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned above, the Fediverse works a little differently than the Indieweb. My wish would be to find something that identifies me with my domain everywhere.&lt;/p&gt;




&lt;p&gt;See the original post on kiko.io: &lt;a href="https://kiko.io/post/Hexo-Webfinger-and-better-discoverability/" rel="noopener noreferrer"&gt;Hexo, WebFinger and better discoverability&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fediverse</category>
      <category>indieweb</category>
      <category>identity</category>
      <category>hexo</category>
    </item>
  </channel>
</rss>
