Kleinnetz

Seit dem Aufstieg der Mastodon-Plattform wird dort vielfach eine allgemeine Renaissance von Blogs/RSS und Mailinglisten beschworen (unter anderem durch den RSS-Erfinder). Unter den optimistischen Geeks, die sich Gedanken um die Langlebigkeit ihrer digitalen Äußerungen und Werkzeuge machen, sind viele Anhängerinnen des Small Web, das sich seit 2019 auf eine moderne Gopher-Variante namens Gemini stützt und nicht an einem globalen Erfolg nach dem Muster des World Wide Web interessiert ist. Gemini strebt stattdessen mit dem Verzicht auf individuelle Gestaltung (CSS) und Dynamisierung (JavaScript) nach einer maximum power to weight ratio, die für die breite Öffentlichkeit weniger erstrebenswert ist. Außerdem legt die Spezifikation besonderen Wert auf Privatsphäre, indem Einfallstore für schamloses Tracking weitgehend ausgeschlossen werden. Mit anderen Worten: Gemini ist das Ergebnis einer Kombination von Gopher mit den Erfahrungen aus 30 Jahren WWW-Entwicklung aus der Perspektive einer überzeugten Gopher-Nutzerin. Der programmatische Minimalismus des Gemini-Protokolls geht in Bezug auf den Medientyp text/gemini bzw. die Auszeichnungssprache Gemtext sehr weit – außer Überschriften, Listen, Zitaten, Code und Links sind keine Auszeichnungselemente vorgesehen, und über die Darstellung dieser Elemente entscheidet allein die Client-Software. Im Vergleich zu Gemtext wirkt selbst Markdown barock.

Die Beschränkungen von Gemtext stellen ein gewisses Problem dar für den Transfer bestehender Weblogs in das Small Web. Mir ist die Rückbesinnung auf Dokumente, die unabhängig von einem Browser gut lesbar sind und dennoch ohne Zwischenschritte veröffentlicht werden können, natürlich sehr sympathisch, aber der Verzicht auf inline links ist nicht mit der Verlinkungspraxis in meinen Blogposts kompatibel. Erste Versuche zur Konversion dieses Weblogs mit Pandoc (pandoc --from html --to markdown) und md2gemini hinterlassen außerdem Spuren von Markup (wie {lang="en"}), die ich für hunderte von Posts entfernen (oder im Fall von <p class="code"> in die Gemtext-Syntax überführen) müsste. Den Weg zu einer vollständigen Gemini-Version seines umfangreichen Weblogs bezeichnet selbst Lionel Dricot als a lot more complex than planned – eine mehr als deutliche Warnung.

Für einen parallelen Publikationsprozess HTML/Gemtext gibt es verschiedene Systeme, von handgedengelten Skripten über spezifische Content-Generatoren bis hin zu Hugo, dessen komplexe Konfiguration meinem Django-basierten Redaktionssystem ziemlich nahe kommt. Ich entscheide mich deshalb für eine Erweiterung des Django-Modells –

# models.py class Page(models.Model): title = models.CharField(blank=True, max_length=400) content = models.TextField(blank=True) geminicontent = models.TextField(blank=True) [...]

– in Verbindung mit einer angepassten Variante meines Backrezeptes, die neben den einzelnen Posts eine Indexseite für die Gemini-Kapsel generiert:

# /djangproject/mysite/management/command/make_gemini.py def write_file(filename, content, extension='gmi'): global changed_pages filename = '{0}{1}.{2}'.format(BASEDIR, filename, extension) path = os.path.dirname(filename) # PostgreSQL sends \r\n linefeeds, Python reads \n from file content = content.replace('\r\n', '\n') changed = False if os.path.exists(filename): existing_file = io.open(filename, encoding='utf-8') existing_content = existing_file.read() existing_file.close() if existing_content != content: changed = True else: changed = True if changed: changed_pages += f'Added or changed page was written to to {filename}\n' if not os.path.exists(path): os.makedirs(path) encoded_content = content.encode('utf-8') gmi_file = open(filename, 'wb') gmi_file.write(encoded_content) gmi_file.close() def render_page(page): rendered_page = render_to_string('post.gmi', { 'requested_page': page, 'title' : page.title, 'content' : page.geminicontent, }) return rendered_page class Command(BaseCommand): help = "Create Gemini site" def handle(self, **options): global changed_pages index_content = '' for page in Page.objects.filter(mother_id=11923).exclude(geminicontent='').order_by('-create_date'): rendered_page = render_page(page) filename = reverse('blogentry_alpha', kwargs = { 'page_url' : page.url_name, 'year' : page.create_date.year, 'month' : page.create_date.month }) write_file(filename, rendered_page) index_content += f'=> {filename}.gmi {page.title}\n' if not changed_pages: changed_pages = 'No pages have been changed.' # Update index page only if pages have been changed else: rendered_index_page = render_to_string('index.gmi', { 'content' : index_content }) write_file('/index', rendered_index_page) scriptmail.send_message('Gemini Site updated', changed_pages)

Der Einsatz eines komplexen Web-Frameworks zur Erstellung einfacher Textdokumente entspricht nicht unbedingt dem Geist der Gemini-Spezifikation, andererseits ist die Nutzung vorhandener und gut funktionierender Technologie viel nachhaltiger als eine Investition in immer neue Werkzeuge. Dafür nehme ich sogar die redundante Speicherung für jedes Crossposting in Kauf.

Die Motivation für eine Gemini-Kapsel ist rein technischer und philosophischer Natur. Die FAQ-Sammlung des Projektes und verschiedene Posts heben stets die Vorteile eines nahezu ausschließlich textbasierten Mediums hervor, was den Nischencharakter des Small Web dauerhaft sicherstellt – auch die Gemini-Kurzanleitung von Jason McBrayer formuliert bei aller Euphorie recht defensiv:

The main thing to know is that you’re going to get a much more stripped-down experience compared to the modern WWW, but that’s okay! Some of the choices made to keep Gemini simple may seem too extreme, compared to even a bare-bones web site, but there are hidden benefits that won’t be obvious at first.

Viele neue Leserinnen wird mein – ohnehin vor allem von Bots frequentiertes – Blog auf diesem Weg also nicht finden. Lionel Dricot betrachtet die Beschränkung auf einen kleinen Personenkreis und wenige Kapseln als einen wesentlichen Vorteil:

That’s why on Gemini, we have no content. We have conversations. We have humans writing because they want to. Because they need to. We may have answers. We may start conversations over email. We are slowly interacting. The purpose is to write, to express, not having the feeling of being read.

Etwas weniger weihevoll ausgedrückt: Es besteht mit etwas Disziplin eine gewisse Chance, das gesamte Small Web durchzulesen und dabei nette, gleichgesinnte Menschen kennenzulernen. Oder unverschlüsselt die sensibelsten Informationen zu publizieren, unterhalb des Radars aller staatlichen und kommerziellen Datensammler – Schreiben unter implizitem Ausschluss der Öffentlichkeit.

Wie nicht anders zu erwarten, gibt es ähnlich viele Software-Projekte für Gemini wie Nutzerinnen. Als Clients verwende ich Lagrange (GUI/Desktop), Amfora (CLI/Desktop) und Elaho (iOS). Auch wenn mir die GUI von Elaho sehr gut gefällt – Lagrange erinnert etwas an NeXTSTEP – ist Amfora natürlich die angemessene Art, sich im Small Web zu bewegen.

Serverseitig entscheide ich mich für Agate, das ich als Binärpaket installiere und manuell als systemd-Service konfiguriere:

# /etc/systemd/system/gemini.service [Unit] Description=Agate gemini server [Service] User=www-data WorkingDirectory=/var/gemini/ ExecStart=/bin/sh -c "agate --hostname eden.one --lang de-DE" Restart=always RestartSec=1 StandardOutput=syslog StandardError=syslog SyslogIdentifier=gemini [Install] WantedBy=multi-user.target # /etc/rsyslog.d/gemini.conf if $programname == 'gemini' then /var/log/gemini.log & stop # /etc/logrotate.d/geminilogs /var/log/gemini.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts }

Nach dem üblichen –

systemctl daemon-reload systemctl restart rsyslog systemctl enable gemini systemctl start gemini

– liefert Agate die Kapsel aus. Meine Pendelei zwischen alten und neuen Netztechnologien ist im Stadium der Synthese angelangt.