Gemini is a light-weight text protocol, a spiritual successor to Gopher.

I wrote Soweli Lukin to read both Gopher and Gemini sites. Feel free to use it, too.

If you’re an Emacs user, I have a setup that allows me to edit pages via Gemini and Titan! Editing pages is important because I wrote Gemini Wiki. 😀

This site is available via Gemini, too. The wiki raw text is transformed into Gemini format (”Gemtext”) on a best-effort basis.

2020-09-12 Play by click

Many, many years ago, I maintained and developed a play by email (PBEM) game called German Atlantis. One of the problems it had was that it was a typical “explore, build, conquer” game. You gained more units and thus you had to give more orders, reports grew longer and longer, and I ended up not liking it in the end.

Today, @jbauer wondered whether there was something similar to Astrobotany out there. Astrobotany is a game where you get a seed that grows into a weird plant if you water it every few days. In order to do that, you log in and click a link. The interface is simple enough that you can “play” the game via Gemini. Sadly, that part is extremely boring. There are two things that are interesting: the plant develops and that is interesting to see: the ASCII art and the description changes over time, and you have “enough water” to water one other plant in the community garden every time you log in, which allows you to support a friend by watering their plant.

Astrobotany - Your Garden

   /              \      
  |                |     
  |                |     
  |     R.I.P.     |     
  |                |     
  |                |     
  |                |     
. |, _\/ .. \. \ /,|_ .  
  ^      '        `    ' 

name  : "Jason"
stage : fractal young agave (deceased)
age   : 21 days
score : 490177
water : N/A

You cry over the withered leaves of your plant.

Your plant was recently watered by Samsai.

Plant Actions

[1] 💧 Water
[2] 💀 Harvest
[3] 🍄 Fertilize
[4] 🔬 Plant info
[5] 📛 Plant name

[6] 🚶 Go back

Yes, I forgot about my plant… 😭

Thank you, Samsai! 🙇

But this got me wondering. We should design something for Gemini, perhaps? Maybe each player runs a small village in a generic non-tech setting. People are born and die, marry and separate, and every year players can make one decision: ask a character from a neighbouring village for marriage, send a character to help defend a village against marauders, focus on dairy, lumber, mines, salt, etc. Thus, the story progresses slowly, on autopilot, until you intervene and make one of these decisions?

OK, second iteration. There is a hex map, like a random Alpine map as generated by Text Mapper. On the map are villages and these villages belong to the various players.

Each hex also gets a wiki page. The report is generated for every hex and is appended to the wiki page. Players can edit those pages, add more stuff, elaborate, move stuff to other pages, but that doesn’t change the internal state of the game. This is how players who want to write some more can do so.

In order to keep the user interface simple, players don’t have a lot of choice. So I’m thinking: the game is simple. On auto-pilot, your village simply degenerates to some sort of hunter-gatherer tribe, decimated by bandits. The choices involve cooperating with other players and their villages or clans.

Terrain and trade. Every hex generates something that can help others: fish, lumber, salt, cheese, iron, crops, wool, leather, stone. Simplified, we could say that a hunter-gatherer tribe can grow up to twenty members, but with every trade you establish, you can add another twenty to your village: given the nine things I mentioned above, that would increase your village up to two hundred people. Cool. So, one of the actions could be establishing of regular trade with another village, giving you access to their raw materials, and giving them access to your raw materials. This would be a high level “trade” – I’m not actually interested in gold coins and prices, all I care is for trade routes to exist.

We could add calamities to mix things up. All would suffer. If there is drought, livestock perishes and people die. If you had built granaries, however, less people would have died. If there are marauders plundering the land, people die in the ensuing famine. If you had built palisades, however, less people would die. We could also think of bigger things: the Romans coming, the Huns coming, the plague coming, and so on. The thing all these things would have in common is that they would be a constant cause of degradation and impoverishment, where as the establishing of relations would mitigate these things.

We could add family politics. People are born and die, marry and divorce, they all have names, and so on. One of the actions could be to marry off children to other clans, strengthening your ties. I’m not quite sure how exactly this would work, however. Perhaps in the case of an invasion, all the related clans would help each other fight off the invaders, reducing their losses.

We could also add a little crafting or tech tree. I’m a bit hesitant, however. Perhaps it would make for a better game if there was infrastructure one could build, but it would also decay over time, leaving ruins. The constant maintenance required would also imply relations to other clans. For example: a secure water supply with an aqueduct requires trade relations with five villages having a stone quarry. If the player of one of these five quarries drops out of the game, the trade relation disappears and all the aqueducts start to decay until a replacement village is found.

Every turn the report would list the population changes, the buildings built and the buildings falling apart, and the options:

  • establish trade route to a random nearby village
  • build some infrastructure if the prerequisites are met
  • marry off a member of your clan to build a network

And then we just keep adding to the game if we like it. What do you think?

Comments on 2020-09-12 Play by click

OK, third iteration. Looking at a random map, I remember that all the villages are placed in forest hexes because I felt that this is where villages here in Switzerland were typically built: the giant forests of central Europe. You need water to run the mills, but you need to avoid the flooding of the big rivers in spring, and you need to avoid the swamps and the Malaria they brought. I remember the Swiss valleys being locked in: they had cheese and desperately poor men that were willing to fight in other people’s wars, and they needed salt… Perhaps lumber only shows up in countries where you need to build a fleet, that is in a time of intercontinental trade (and war), which would be boring.

The longer I’m thinking about this, the more it all breaks down. Most villages didn’t need all that long distance trade, I’m sure. So... where does the need to cooperate come from? Or make it all fantastic? I’d like to keep it non-fantastic for as long as possible, though.

So... how about players getting to say where they focus their surplus production, and they all have the same options. Let’s start small.

  • grain
  • cheese
  • meat
  • fish
  • lumber
  • stone
  • fibre

Later, we can add more resources to locations at random. Perhaps people “learn” about a local secret: copper, tin, iron, coal, dyes, tulips. Or perhaps “switching” your resource focus actually means moving your village: up into the valley if you want to focus on dairy but your grain production is going to be bad; to the lake if you want to focus on fish but your stone production is going to be bad; or to a particular mine. The point is that the game isn’t about map exploration but about building resilient networks of resource sharing.

By default, about twenty people can live in an area as hunter-gatherers.

With each food connection, you can add another twenty people:

  • grains
  • cheese
  • meat
  • fish

Non-food connections offer other benefits:

  • one lumber connection allows you to build a palisade to defend against marauders
  • one lumber connection allows you to build a mill to double your grain production one stone connection allows you to build a city wall to defend against invasion
  • one fibre connection improves your chances of surviving harsh winters

I need to experiment with the numbers. Perhaps each village can offer two outgoing connections at most? We’d get this long term situation:

  • village 1 focuses on grain and shares with village 2 and 3
  • village 2 focuses on lumber and shares with 1 and 3
  • village 3 focuses on grain and shares with village 1 and 2
  • this allows villages 1 and 3 to build a mill each
  • village 1 has 60 people (20 + 20 from the mill + 20 due to grain from village 3)
  • village 2 has 60 people (20 + 40 due to grain from villages 1 + 3)
  • village 3 has 60 people (20 + 20 from the mill + 20 due to grain from village 1)

But with random events, things get more interesting: when the marauders come, all the mills are burnt down and all the grain is stolen and all three villages are reduced to 20 hunter-gatherers again.

So now village 1 decides they need a palisade. That means no mill, and thus it only has 40 people. The trio of villages is now better prepared to resist marauders. Perhaps we can start modelling how people from the “safe” village marry into the surrounding clans after the pillaging to replenish those other clans?

I still think that with just two outgoing economic ties, we’re often going to end up in trios? So perhaps we need to have a model that says what people are doing. If we have 20 people they just feed themselves. If we have 40 people, we can have one outgoing connection. If we have 60 people, we can have two outgoing connections, and so on? Perhaps more than four outgoing connections needs some sort of infrastructure investment, again: building bridges requiring lumber or stone connections.

I’m going to try and not model “be rich” but I guess we could: seven lumber connections allows you to maintain a ship which gives you a luxury level; five fibre connections allows your clan leaders to dress in fancy silks and gives you another luxury level. Luxury levels are essentially useless but could be a player goal? I don’t know. Maybe I don’t want to make a game about amassing wealth.

– Alex 2020-09-12 21:25 UTC

L-0: food, clothing, shelter. L-1:defense, education, industry, L-2: bond with other communities, L-3:fiefdoms - require nominating a player as chief

– Kevin 2020-09-12 22:16 UTC

I’ve thought about it some more. I think the problem is that I have a lot of ideas for programming. We could make the number of outgoing connections proportional to the population size and so the reactions to calamities would have to be a prioritisation of which connections to maintain; we could have invading troops on the map, and clans could move to different villages, fleeing from the troops, or hand over part of their produce, diminishing their resources… We could have colonisers arrive, and regimes collapse, and through it all the villages would try to survive, children are born, move away if there are no opportunities, or build the settlement; we could go all dwarf fortress here and simulate how people that are too closely related are not allowed to have children; and on and on.

But the question is, I think: would I want to program the game, or would I want to play the game? I’m starting to suspect that I wouldn’t actually play the game for long. I’m too interested in programming. And that in turn makes me think I should postpone the programming: let the enthusiasm run its course, and then do nothing. Why work on a game that I wouldn’t want to play.

– Alex 2020-09-13

I just can’t drop it... Got started with a little “orc village simulator browser game” that I’m planning on integrating with my Gemini wiki, Phoebe. I’m calling the game Anthe (another of Saturn’s moon, and one of the daughters of the giant Alcyoneus that got turned into a kingfisher).

– Alex 2020-09-14 21:03 UTC

OK, Anthe has basic population growth! Orcs procreate and reports are written to a Phoebe wiki! ♥

– Alex 2020-09-18 22:27 UTC

Add Comment

2020-08-31 Summer's gone

Like every summer, I enjoyed a long summer break. Two months of staying at home, doing whatever I fall into. This year, it was Gemini, and Phoebe, my Gemini wiki.

Sadly, summer’s gone and that means going back to work!

Comments on 2020-08-31 Summer's gone

It turns out I made a mistake! I knew that I had wanted to start a week earlier, and had told my project manager, but then I forgot about it but remembered that I wanted to start a week earlier, so when I showed up in the call today they were surprised. Alex is back one week earlier! Whaaaaaat!?

OK, I really got confused this time. Which is a good sign regarding summer break. If you can’t remember things from before your holidays, then those are good holidays in my book! I did remember my password, though, so the mind wipe wasn’t complete... 😀

I’m thinking of taking off two weeks in October in order to make up for it. So far, so good!

– Alex 2020-08-31 10:08 UTC

Did I tell you that at my wife’s office the people working in Human Resources (HR) think that we should talk about “Life Balance” instead of “Work-Life Balance” since work is also part of life... That is such a clumsy way of trying to rework the language in order to reframe the mess we’re in, it makes me cringe. “Fremdschämen” is the German word to use when we feel shame for the mistakes of others.

Incredibly, I’ve since learned that they are not alone in their attempt to use “life balance.”

I think they’re trying to get away from the whole discussion of Work-Life Balance, all the books and papers and feelings on the subject. Instead of trying to fight the idea that work is something many and me included wouldn’t be doing if the consequences wouldn’t be so terrifying, they are trying to establish a new mind space that is still terra nullius. There, no opposition exists.

Which is why I like to fight this change. If we keep calling it Work-Life Balance, we have the existing literature in our back. And when they say that “work is not the opposite of life” we can either argue that an established concept is an established concept, even if the exact wording don’t match their expectations; we can’t just ignore the existing literature on the idea even if we don’t like the name of the idea. Or we can argue that “life balance” means even less: life in balance with what? A well balanced life in terms of what aspects? Like, how much walking we do instead of driving? Of watching TV instead of reading? We need to name the things we’re going to be looking at. Or are they going to just hand-wave it and claim, “everything?” In which case, the concept seems pretty meaningless. If we’re going to talk about how much time we spend away from the family and our relations, if we’re going to talk about the hours we spend at the office, if we’re going to talk about burnout, then let’s call a spade a spade. We’re talking about work.

– Alex 2020-08-31 10:21 UTC

Your post has reminded me to create my own! I’d love to see other geminauts report their summers too.

– Timur 2020-08-31 18:52 UTC

The Cure remains one of the best bands. 😄

– Alex Schroeder 2020-09-01 12:00 UTC

Add Comment

2020-08-29 Restarting Phoebe

I have trouble restarting Phoebe and other services I run. I guess I don’t understand how these forking processes work. These processes fork for every request they get, and that works. When they get a lot of requests, however, they enter some sort of failed state. And by “a lot” I mean more than a hundred or so. I know that it’s not a lot but I want these services to work for the small net and so I don’t want to spend any effort in trying to make them handle more. I don’t want to start caching HTTP requests, for example. What irks me most of all is that this doesn’t happen because hundreds of visitors want to know about my stuff. No, I share a link on Mastodon, and my post gets federated, and then every single server on the fediverse tries to get a preview image to display.

I’ve solved this issue for all my services behind Apache by blocking all the fediverse user agents, but Phoebe runs without a web server front-end. I’ve tried to abort as soon as possible, using the same regular expression, but that doesn’t seem to work.

But I have a second layer of defence: Monit watches over my processes. Please forgive the huge start program option. Maybe it’s time to move some of that into a config file. Please just scroll down. I also didn’t want to shorten it, because I think it’s an interesting snapshot of a non-trivial Phoebe setup.

Let’s go through this.

First we have a PID file, where the process ID is going to be. This is how Monit identifies the parent process responsible for the service. The --pid_file option is what tells Phoebe to write the same file. So far so good.

check process phoebe with pidfile /home/alex/farm/
    start program = "/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe
 --setsid --user=alex --group=alex
 --log_level=3 --log_file=/home/alex/farm/phoebe.log
 --wiki_dir=/home/alex/phoebe --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/ --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/ --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/ --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/ --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/ --cert_file=/var/lib/dehydrated/certs/ --key_file=/var/lib/dehydrated/certs/
 --wiki_main_page=Welcome --wiki_pages=About
 --wiki_mime_type=image/png --wiki_mime_type=image/jpeg

OK, with that out of the way, let’s talk about the important stuff: stopping and restarting the process, and determining when to restart the process.

    # leave enough time after a stop for the server to recover before starting
    stop program = "/bin/bash -c 'kill -s SIGKILL `cat /home/alex/farm/`; sleep 120'"
    if failed
	port 1965
	type tcpssl
        send "gemini://\r\n"
	expect "20 .*"
	for 5 cycles
	then restart
    if totalmem > 100 MB for 5 cycles then restart
    if 6 restarts within 15 cycles then stop

Monit checks the service using a regular request, once every cycle (5min). If it fails five times in a row (25min), it restarts. It also restarts when total memory is more than 100MB five times in a row. And when it had to restart six times in 15 cycles (75min), then the process gets stopped.

What happens on a restart? First the program is stopped and then it is started. But yesterday for example:

[CEST Aug 29 00:39:05] error    : 'phoebe' total mem amount of 190.0 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 29 00:39:05] info     : 'phoebe' trying to restart
[CEST Aug 29 00:39:05] info     : 'phoebe' stop: '/bin/bash -c kill -s SIGKILL `cat /home/alex/farm/`; sleep 120'
[CEST Aug 29 00:39:35] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 00:44:42] error    : 'phoebe' process is not running
[CEST Aug 29 00:44:42] info     : 'phoebe' trying to restart
[CEST Aug 29 00:44:42] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 00:49:57] error    : 'phoebe' process is not running
[CEST Aug 29 00:49:57] info     : 'phoebe' trying to restart
[CEST Aug 29 00:49:57] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 00:55:02] error    : 'phoebe' process is not running
[CEST Aug 29 00:55:02] info     : 'phoebe' trying to restart
[CEST Aug 29 00:55:02] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 01:00:12] error    : 'phoebe' process is not running
[CEST Aug 29 01:00:12] info     : 'phoebe' trying to restart
[CEST Aug 29 01:00:12] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 01:05:19] error    : 'phoebe' process is not running
[CEST Aug 29 01:05:19] info     : 'phoebe' trying to restart
[CEST Aug 29 01:05:19] info     : 'phoebe' start: '/usr/bin/perl -I/home/alex/phoebe/lib /home/alex/farm/phoebe --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/phoebe.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/phoebe --cert_file=/va...'
[CEST Aug 29 01:10:25] error    : 'phoebe' service restarted 6 times within 6 cycles(s) - stop

Why isn’t the process running? Here’s a selection from the other log:

2020/08/29-00:39:38 App::Phoebe (type Net::Server::Fork) starting! pid(19496)
2020/08/29-00:39:38 Cannot connect to SSL port 1965 on [Address already in use]
2020/08/29-00:44:43 App::Phoebe (type Net::Server::Fork) starting! pid(20808)
2020/08/29-00:44:43 Cannot connect to SSL port 1965 on [Address already in use]
2020/08/29-00:49:59 App::Phoebe (type Net::Server::Fork) starting! pid(22125)
2020/08/29-00:49:59 Cannot connect to SSL port 1965 on [Address already in use]
2020/08/29-00:55:05 App::Phoebe (type Net::Server::Fork) starting! pid(23449)
2020/08/29-00:55:05 Cannot connect to SSL port 1965 on [Address already in use]
2020/08/29-01:00:15 App::Phoebe (type Net::Server::Fork) starting! pid(27704)
2020/08/29-01:00:15 Cannot connect to SSL port 1965 on [Address already in use]
2020/08/29-01:05:21 App::Phoebe (type Net::Server::Fork) starting! pid(15897)
2020/08/29-01:05:21 Cannot connect to SSL port 1965 on [Address already in use]

When I returned to the server this morning, that was the state it was in, and when I tried to restart it, same problem. There was a process still running, but the PID file was gone, and so Monit couldn’t stop the process, but the process was also not serving the port it was using.

ps aux | grep phoebe

So what’s the best solution, here? I’m thinking of a variant of “killall”, perhaps? The example below uses “[p]hoebe” to avoid the grep command from listing itself.

ps aux | grep '[p]hoebe' | awk '{print $2}' | xargs kill

What do you think?

Add Comment

2020-08-16 Mastodon kills Gemini Wiki

Yeah, same old story. I keep forgetting that linking to my sites that aren’t behind the Apache filter that blocks all the Fediverse user agents is basically a distributed denial of service attack because my server is so small and I’m not serving static pages – and I hate the fact that I have to be thinking about blocking, caching and hundreds of connections all at the same time only to defend against Mastodon.

These are services for the Smolnet, not for all the people that keep thinking about scaling all the time.

Here’s what Monit tells me a few minutes after I linked to The Transjovian Council:

[CEST Aug 16 19:25:21] error    : 'gemini-wiki' total mem amount of 185.8 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 16 19:30:26] error    : 'gemini-wiki' total mem amount of 185.8 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 16 19:35:36] error    : 'gemini-wiki' total mem amount of 185.8 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 16 19:40:47] error    : 'gemini-wiki' total mem amount of 125.5 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 16 19:45:52] error    : 'gemini-wiki' total mem amount of 125.5 MB matches resource limit [total mem amount>100 MB]
[CEST Aug 16 19:45:52] info     : 'gemini-wiki' trying to restart
[CEST Aug 16 19:45:52] info     : 'gemini-wiki' stop: '/bin/bash -c kill -s SIGKILL `cat /home/alex/farm/`; sleep 10'
[CEST Aug 16 19:46:02] info     : 'gemini-wiki' start: '/usr/bin/perl -I/home/alex/gemini-wiki/lib /home/alex/farm/gemini-wiki --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/gemini-wiki.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/gemini-wiki --host=transj...'
[CEST Aug 16 19:51:09] error    : 'gemini-wiki' process is not running
[CEST Aug 16 19:51:09] info     : 'gemini-wiki' trying to restart
[CEST Aug 16 19:51:09] info     : 'gemini-wiki' start: '/usr/bin/perl -I/home/alex/gemini-wiki/lib /home/alex/farm/gemini-wiki --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/gemini-wiki.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/gemini-wiki --host=transj...'
[CEST Aug 16 19:56:15] error    : 'gemini-wiki' process is not running
[CEST Aug 16 19:56:15] info     : 'gemini-wiki' trying to restart
[CEST Aug 16 19:56:15] info     : 'gemini-wiki' start: '/usr/bin/perl -I/home/alex/gemini-wiki/lib /home/alex/farm/gemini-wiki --setsid --user=alex --group=alex --log_level=3 --log_file=/home/alex/farm/gemini-wiki.log --pid_file=/home/alex/farm/ --wiki_dir=/home/alex/gemini-wiki --host=transj...'
[CEST Aug 16 20:01:21] error    : 'gemini-wiki' service restarted 3 times within 3 cycles(s) - stop

Why is this? Because all the Fediverse instances connect to the linked site in order to try and generate a preview! The original server federates the toot with the URL, but not the preview it generated and therefore ever other fediverse server attempts to generate that preview.

The way I solved this for all the sites behind Apache is using a user agent block:

RewriteEngine on
RewriteCond "%{HTTP_USER_AGENT}" "Mastodon|Friendica|Pleroma" [nocase]
RewriteRule ^(.*)$ - [forbidden,last]

Yeah, it’s not a lot of requests. A few hundred, maybe? But as you can see the memory shoots up and I don’t want to have RAM to spare, nor caches. I really need to look into better shutdown and then restarting super slowly. Also making sure the old process doesn’t hang around, blocking the port, and so on. Gah!

Comments on 2020-08-16 Mastodon kills Gemini Wiki

Slightly more elegant than returning 403 would be to redirect to a static page with just the Facebook/Twitter card metadata that Mastodon uses. Don’t know if you have the resources for that, though.

– elpher7HI6ms 2020-08-17 00:43 UTC

Interesting idea!

– Alex 2020-08-17 09:06 UTC

Add Comment

2020-08-14 How to comment using Elpher

If you visit this site using Elpher, you can leave a comment using Gemini. The comment can’t be too long. I think the total URL should be limited to about a thousand bytes, some of that is the URL without the query, and the query itself is going to be encoded… Who knows. Perhaps this server doesn’t actually check URL size? I have no idea and I have never tried. Ooops!

Here’s a video demonstrating it:

(Emacs using the Brutalist Dark theme…)

The video was recorded using Peek. It was edited and the audio was added using Pitivi. The audio was made on a tiny toy, the PO-35 speak. Basic recording was done using Audacity.

Comments on 2020-08-14 How to comment using Elpher

dogs are good

– elpherGiUS7i 2020-08-15 04:42 UTC

very cool, nice! great wiki 🙂

– elpherbqnGZw 2020-08-15 15:14 UTC

Hi from a patched Elpher! ~ michel

– 2020-08-15 23:24 UTC

Hah, I think most people don’t use the certificate’s Common Name to identify themselves but to identify the site they intend to use it for. Perhaps that’s some sort of usability issue: you want to create a new permanent certificate with your nickname as the Common Name, and save it using a filename identifying the site (or sites) you want to use it for. 😀

– Alex 2020-08-16 12:12 UTC

Nice. 🙂

– elpherzbK550 2020-08-16 12:30 UTC

Add Comment

2020-08-12 Mozilla fires 250 engineers

In a blog post, Mozilla announced that it was firing 250 engineers: “Sadly, the changes also include a significant reduction in our workforce by approximately 250 people.” Ouch.

Then again, as @aral recently said:

We could fork Firefox and have an independent EU org develop it. No need for the rest of Mozilla.

The criticism stems from the fact that 90% of Mozilla’s revenue is paid for by… Google!

Or, as Katyanna Quach writes in The Register:

Mozilla gets the vast, vast majority of its funding from Google, Yandex, and Baidu, who pay to be the default search engine in Firefox in their regions. In 2018, Moz had a $451m cash pile, 95 per cent of which, some $430m, was provided by these web giants. Those deals will expire in November 2020 unless renewed or renegotiated.

With an ever decreasing market share, those deals are surely going to be renegotiated.

But, I guess what I’m more concerned about is this: think about how big Mozilla is. They had a thousand engineers!

What fascinates me about seemingly retro tech is the dream of having these tools be feasible in the human realm. People like us can use them and make them, without having to form a company, without business plans and lawyers and project managers. The web browser project is so big, so monstrous, it needs hundreds of people to get right, to implement all the features, because we kept adding them and adding them, letting corporations out-organize us.

This is what happened. We used to have so many browser engines. Now you need a thousand engineers to compete, apparently.

And yes, I know, this is never going to stop: people see business opportunities and jump in, and with venture capital it is possible to out-organize us, again and again. Welcome to capitalism. But there’s hope: the grim reaper that cuts down enterprises in a pandemic, in an economic crisis. When profits are gone, we’re still there. Gopher is still there. RSS is still there. Plan 9 is still there. Emacs is still there. (Vim, too.) Forth is still there. Email is still there.

True, a crisis is never the tabula rasa one might wish. Many companies are better off after the war. As @sqwishy reminded me:

Business are affected negatively but some few benefit; Zoom has almost become a household name for conference calls in the way that the word “Powerpoint” is used in place of “slideshow”. That is not better for tech.

And yet, remember IBM? There were huge. Huge! “Nobody ever got fired for buying IBM equipment.” And yet, here we are. IBM is a shadow of its former self. DB2 is still around. I can’t think of any other IBM product these days. The giants will come down one day.

The resistance is never big. In the movies, underdogs are the main characters. We never see the sea of people that’s going along with the system. The resistance is small. The resistance isn’t pure. I still use Firefox. I still use the web. But I use it less. I use text browsers when I can. I use simple Mastodon clients. I know IRC, Bitlbee, Brutaldon, Lynx and Gemini are not strictly “better” than the modern web; to my coworkers all these things seem very retro. But they’re my scale. I am not a thousand developers. I’m human scale.

Or, as @Shufei puts it:

Stepping back, I see I hardly go on the mainstream web anymore. Half of my Wikipedia reading is via Gopherpedia. Libgen. Git-tub. What else is there? The rest is corpo caca. And yet, that is what most people seem to like. Why else would they be on FB? I do think it’s time to recognize that what we have is a demimonde, a resistance, and invest energy in it accordingly. This is it.

Indeed. I still use Wikipedia on the web. But I also feel like I’m using less and less of the mainstream web. It’s all corpo caca.

If we’re the resistance, then we need to think in terms of propaganda. What are our posters? Our jokes? What new names do we give the things we like, the things we dislike? In German, we call this process “Wortschöpfung”, word creation.

Let’s not be coprophages, let’s not be dung eaters. We don’t want the corpo caca.

Comments on 2020-08-12 Mozilla fires 250 engineers

I’ve noticed this too! You know how with search engines you can filter out a word by putting a minus sign in front of it? Is there a way to filter out websites that have javascript from search results? Because that’d do the trick. Bye bye corporate top 10 lists of weird tricks, hello somebody’s weird passion project homepage.

– Anonymous 2020-08-13 02:25 UTC

I think that’s basically why Gemini is not simply HTTP1.0 with a simple HTML subset and no scripts: because you can never tell which links go back to the corpo caca web. Perhaps there’s an extension that unlinks the biggest corporate sites from all web pages? That would be interesting.

– Alex 2020-08-13 07:25 UTC

Sounds like is what you’re talking about.

– Anonymous 2020-08-13 08:18 UTC

Wiby sounds very interesting! Thanks.

– Alex 2020-08-13 09:17 UTC

Good comment regarding standards growing in complexity. Whenever a committee meets to talk about new developments, there’s the danger of capture. They start adding features because they can, and everybody adds them because they must. And over the years, the number of competitors starts to dwindle. How many C++ compilers are there?

– Alex 2020-08-13 18:01 UTC

I really enjoyed this post! I am absolutely enjoying the longform gemini posts lately 🙂

– elphermVSbpm 2020-08-14 09:44 UTC


– Alex Schroeder 2020-08-14 15:07 UTC

“As a non-profit open-source operation, Mozilla spends as much as it receives; its 2018 staffing bill was $286m with a headcount of about 1,000, or about $286,000 per person, on average.”

Well, they’re certainly making more money than I am. But so do the Google engineers, of course.

– Alex Schroeder 2020-08-14 15:08 UTC

Add Comment

2020-07-20 Does a Gemini certificate need a Common Name matching the domain?

It’s a rhetoric question. I think it does not because the Gemini world uses “trust on first use” (TOFU). That is, a Gemini client visits a Gemini server for the first time, and if it doesn’t know the certificate, it silently stores a fingerprint of said certificate. The next time the Gemini client visits the same Gemini server, the client verifies that the fingerprint still matches. If it does, then nobody has been meddling with the encryption. If it has changed, a warning is usually shown to the user.

The benefit is that we can use self-signed certificates. No promises are made, and you might run into a trap on your first use, but once you begin to trust a site, you can be sure that nobody is meddling with your encryption as long as the fingerprints stay the same.

Now, when you create a new, self-signed certificate, you are asked a few questions. Here’s an example. First, the openssl command to create new certificates, then the openssl output, and at the end, when it comes to the list of questions, I leave them all blank (entering a dot) except for the Common Name (CN). There, I provide any name I want:

openssl req -new -x509 -newkey ec \
-pkeyopt ec_paramgen_curve:prime256v1 \
-days 1825 -nodes -out cert.pem -keyout key.pem
Generating an EC private key
writing new private key to 'key.pem'
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:Gemini Wiki
Email Address []:.

Now, if I were using this certificate for a traditional website, I would have to use my domain name as the common name or as the “alt subject name”, and I would have to get the certificate signed by a certification authority, and so on. We don’t want to do all this in the Gemini World, so we’re free to pick any common name. You could pick a “fully qualified domain name” (FQDN) or you could pick “YOUR name” – that’s it. Just any name you want, actually. These questions and their significance is regulated by X.509.

An X.509 certificate contains a public key and an identity (a hostname, or an organization, or an individual), and is either signed by a certificate authority or self-signed.

Thus, it’s true that on the web, we need use appropriate certificates. But for Gemini, we can pick the identities we want: our organisations, ourselves as individuals, it doesn’t matter. All that matters is that clients (and their users) “trust on first use” (TOFU).

So now we’re getting to the question I have: I have two websites, and; on the web, browsers get the respective certificates when visiting one or the other. On Gemini, I thought I’d save myself some time and just use one certificate for both sites. And now some Gemini clients are complaining: they’re visiting the first site and getting the certificate for the second site, and the common name doesn’t match the hostname, and so they raise an error.

What do you think?

I think they’re wrong, for the reasons explained above. I could have used a certificate with the common names “Alex Schroeder” or “The Transjovian Council” and I decided to use the certificate with the common name “” – in the TOFU world, I don’t think it should matter whether this certificate “belongs” to The mismatch is irrelevant. All that matters is whether you trust it.


Comments on 2020-07-20 Does a Gemini certificate need a Common Name matching the domain?

On the mailing list, Gary Johnson said he was under the impression that the Gemin specification already made it mandatory to make CNs match the requested domain name, and in support of this position he quoted the following:

Use of the Server Name Indication (SNI) extension to TLS is also mandatory, to facilitate name-based virtual hosting.

But my thinking is this: SNI is how the client tells the server which host it wants to reach in case multiple hosts are hosted on the same IP. Thus, the spec says: in order to facilitate name-based virtual hosting, we want SNI to be part of the standard. That is, both users Alex and Gary could host their sites on a single server, and they could both use their own server certificates. The server would receive requests from clients for either Alex’s site or Gary’s site, know which certificate to send back to the client, and serve the appropriate content. This is the benefit of SNI.

In my mind, this doesn’t require hostname verification, though! Alex’s certificate could have used the common name “Alex Schroeder” and Gary’s certificate could have used the common name “Gary Johnson”. All the client sees is a certificate, which it trusts on first use (TOFU). Users might decide to look at the common name of the certificate, or not. There’s not even the need to provide a common name – we could have generated our certificates using nothing but our email address, or our organization name.

In short, I’m claiming all of these are valid certificates for a Gemini site at, even if they are invalid certificates for a HTTPS site at (maybe? I’m ignoring alt subject name):

openssl x509 -in cert.pem -noout -sha256 -text | grep Subject
        Subject: emailAddress =

openssl x509 -in cert.pem -noout -sha256 -text | grep Subject
        Subject: CN = Gemini Wiki

openssl x509 -in /var/lib/dehydrated/certs/ \
    -noout -sha256 -text | grep Subject
        Subject: CN =

What do you think? Do you think SNI inherently mandates that the subject have a common name field that matches the domain? It would have to be implied in the X.509 spec somewhere, I guess?

– Alex

Add Comment

2020-07-13 Client Certificates and IO::Socket::SSL (Perl)

Yes… I’ve been struggling for about five evenings with this and now I finally have example code that works.

The context is my Gemini Wiki. Now, Gemini specifies a peculiar way to do authentication. It’s supported by the specification but it’s rare to see because the world of browsers uses TLS in a different way. Browsers use the concept of certificate authorities. That is, each browser comes with a list of these, and if you’re visiting a website that uses HTTPS, the server presents your browser with its own certificate, which is signed by a certificate authority. The browser trusts the site’s certificate if it has the certificate authority in its trust store. More or less, in any case. Usually there are intermediaries, and therefore sites usually present a chain of certificates, and browsers just know about the roots, and on and on.

Gemini uses TLS differently: certificates are self-signed, that is they don’t depend on a certificate authority. Thus, there’s no way of knowing whether to trust the certificate a site presents you with. What should happen is that the client uses “trust on first use” (TOFU). If this is the first time it visits the site, it simply saves the fingerprint, and if it returns to the same site (same host, same port) at a later date, it compares the fingerprint with the one it saved. If they match, no problem, no tampering. If they don’t match, the Gemini client is supposed to alert the user: “Hey! This site has a new certificate! Check out this fingerprint: gobbledigook gack gack! Do you trust it?” If you’re a security-conscientious user, you’ll look for the new fingerprint on social media, or in email signatures, or some other websites, trying to at least see whether different sources agree, or you’ll make an educated guess based on expiry dates (if the old certificate is about to expire, getting a new certificate makes sense). It’s a bit of trouble, but if certificates are valid for many years, this can work. This is how it works in the SSH world, for example.

Now, to further complicate matters, this is just half the story. In both variants, we’re talking about clients (Gemini clients or web browsers) trying to decide whether they trust the server. Has this server been tampered with? Are criminals trying to install trojan horses, ransomware, or some other malware on my machine? But sometimes servers need to decide whether they trust the clients, too. If the server is a wiki, for example, it might want to know whether the client attempting to edit a page is one of the clients that it trusts.

Again, the model we know from the web browser world is different from how Gemini handles it. In the web browser world, administrators set up a local certificate authority, put it in the server’s trust store, and then they use it to sign client certificates, which they then distribute to their trusted clients. Those clients then present these certificates to the server as they try to connect, the server tries to verify these client certificates, sees that they’ve been signed by a trusted certificate authority (namely the one the administrators set up), and off they go.

In the Gemini world, you can do this, but you can also do TOFU. Clients could present a self-signed certificate, which the server simply accepts for a particular purpose (such as a URL used to create an account), and all it does is it saves the account information and the certificate fingerprint. Now, whenever an account is required, the server looks at the client certificate, computes the fingerprint, and looks up the account it saved for this fingerprint. If there’s a match, that account is used. If there’s no match, the server complains and maybe tells the client to go register an account, first.

For the longest time, there has been one server that did this: Astrobotany 🌱. Amazing! Also, that’s how I knew it must be possible. What it did was offer a registration URL where it asked for a client certificate, and if it had a common name (CN) that it didn’t know, it would create an account for that name, and associate it with the fingerprint of your client certificate. From then on, nobody could use that account without having the appropriate client certificate. Beautiful!

This is what I was trying to do for Gemini Wiki. I wanted the option of limiting editing to known client certificates (a list of trusted fingerprints, basically), and the option of people “signing” their contributions with a username of their choosing.

All of that required my server to be able to access the client certificates, however. And all these days it simply didn’t work. Part of the problem was also that I just knew half of what I wrote above. Hopefully this write-up helps somebody.

Once I knew what I wanted, however, things still weren’t easy. By default, server code using the Perl library stack I’m using (Net::Server::SSL which uses IO::Socket::SSL which uses Net::SSLeay which has bindings into the openssl library or something like that) didn’t check client certificates because by default, “peer verification” is off in “server mode”. If verification is off, the server doesn’t ask the client for its certificate during the handshake, the client certificate isn’t sent, and then there’s nothing to fingerprint and no common name to extract.

Once I knew that I wanted peer verification I ran into another problem: the default operation of peer verification uses the browser model I described above. As my Gemini client was presenting a self-signed certificate, this verification failed. 😢

Yesterday, I got a hint of what I needed from a kind person on the #perl IRC channel: I needed to provide my own verification callback code which would allow me to tell the library that yes, I did want to verify my peer (those pesky Gemini clients), and yes, I did have my own way of verifying them, and then I simply wanted to return “true” for all certificates, just to get the damn code working. My first goal was to simply print the fingerprint of the client certificate!

It still didn’t work… 😭

Today, I decided to try and implement a simpler server and client, based on examples in the IO::Socket::SSL man page, and based on the hint I had gotten, and without using Net::Server::SSL – and that works!

Here’s how.

First, we need to create server and client certificates. These use eliptic curves and are valid for five years.

openssl req -new -x509 -newkey ec \
    -pkeyopt ec_paramgen_curve:prime256v1 \
    -days 1825 -nodes -out server-cert.pem -keyout server-key.pem

openssl req -new -x509 -newkey ec \
    -pkeyopt ec_paramgen_curve:prime256v1 \
    -days 1825 -nodes -out client-cert.pem -keyout client-key.pem

Answer the questions, or don’t, but do provide a common name for both.

Here’s a simple server:

use Modern::Perl;
use IO::Socket::SSL;

sub verify_fingerprint {
  warn "Verifying: @_";
  return 1;

my $srv = IO::Socket::SSL->new(
  LocalAddr => 'localhost',
  LocalPort => 1234,
  Listen => 10,
  SSL_cert_file => 'server-cert.pem',
  SSL_key_file => 'server-key.pem',
  SSL_verify_mode => SSL_VERIFY_PEER,
  SSL_verify_callback => \&verify_fingerprint,
    ) or die "failed to listen: $!";
while (1) {
  my $cl = $srv->accept;
  if (!$cl) {
    warn "failed to accept or ssl handshake: $!,$SSL_ERROR";
  my $input = <$cl>;
  print $input;
  print $cl->get_fingerprint() . "\n";
  print $cl->peer_certificate('cn') . "\n";
  print $cl "hanging up\r\n";
  last if $input eq "quit\r\n"

And here’s a simple client:

use Modern::Perl;
use IO::Socket::SSL;

my $cl = IO::Socket::SSL->new(
  PeerHost => 'localhost',
  PeerPort => 1234,
  SSL_cert_file => 'client-cert.pem',
  SSL_key_file => 'client-key.pem',
  SSL_verify_mode => SSL_VERIFY_NONE)
    or die "error=$!, ssl_error=$SSL_ERROR";
print $cl->get_fingerprint(), "\n";
print $cl "hello\r\n";
print <$cl>;

I start the server:


And I start the client:


The client output is not surprising:

hanging up

This is the fingerprint of the server certificate, followed by the message the server prints back to the client.

Here’s how to confirm the fingerprint of the server certificate:

$ openssl x509 -noout -fingerprint -sha256 -inform pem -in server-cert.pem 
SHA256 Fingerprint=34:7E:31:C3:B1:1A:9B:53:6E:5E:8E:88:53:3E:98:57:45:F2:AC:79:D3:1A:0C:5E:94:52:CB:12:9C:55:0D:84

But on to the server output. That’s the part that I kept failing at!

Verifying: 0 94052960936576 /CN=Alex/CN=Alex error:00000012:lib(0):func(0):reason(18) 94052958116368 0 at line 5.
Verifying: 1 94052960936576 /CN=Alex/CN=Alex error:00000012:lib(0):func(0):reason(18) 94052958116368 0 at line 5.

Oh yes! My callback is printing the verification information! It’s getting the “hello” message from the client! It gets the fingerprint from the client certificate! It can extract the common name from the client certificate! 💯

Now I just need to get this working with Net::Server::SSL! 😅

I’m still using more or less the same client. I’m using my Gemini Wiki as the server, now. It supports a config file where I can add any Perl code I want to the request processing.

I’m basically using this:

if ($url =~ m!/do/test$!) {
  say "20 text/plain\r";
  say "Test";
  say $self->{server}->{client}->get_fingerprint();
  say $self->{server}->{client}->dump_peer_certificate();
  say $self->{server}->{client}->peer_certificate('cn');
  return 1;

The server is created using something like the following:

my %args = (
  proto => 'ssl',
  SSL_cert_file => 'cert.pem',
  SSL_key_file => 'key.pem',
  SSL_verify_mode => SSL_VERIFY_PEER,
  SSL_verify_callback => \&verify_fingerprint, );
for (grep(/--(key|cert)_file=/, @ARGV)) {
  $args{SSL_cert_file} = $1 if /--cert_file=(.*)/;
  $args{SSL_key_file} = $1 if /--key_file=(.*)/;
die "I must have both --key_file and --cert_file\n"
    unless $args{SSL_cert_file} and $args{SSL_key_file};

my $env = {};
my $protocols = 'https?|ftp|afs|news|nntp|mid|cid|mailto|wais|prospero|telnet|gophers?|irc|feed';
my $chars = '[-a-zA-Z0-9/@=+$_~*.,;:?!\'"()&#%]'; # see RFC 2396
$env->{full_url_regex} ="((?:$protocols):$chars+)"; # when used in square brackets

my $server = Gemini::Wiki->new($env);

sub verify_fingerprint {
  my ($ok, $ctx_store, $certname, $error, $cert, $depth) = @_;
  return 1;

The new code is right there, at the top:

SSL_verify_mode => SSL_VERIFY_PEER,
SSL_verify_callback => \&verify_fingerprint,

And the verification simply returns 1. Sadly, this won’t work. When I try to connect with my client, it reports:

error=Connection refused, ssl_error=IO::Socket::IP configuration failed at line 5.

And the server reports:

Can’t load application from file “/home/alex/src/gemini-wiki/gemini-wiki”: Could not finalize SSL connection with client handle (SSL accept attempt failed error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed)

It’s as if I had requested validation but my callback wasn’t being used!

After many hours of desperation 😭 I finally ended up looking at the source code of Net::Server::Proto::SSL. And what do I see?

my @ssl_args = qw(

Notice how SSL_verify_callback is missing in this list! And when I add it, my code works!

The client says:

20 text/plain
Subject Name: /CN=Alex
Issuer  Name: /CN=Alex


But, what now? 🤔

Pull request made. But there are now ten of them... 😓

In the end, all this does is it allows us to limit editing to people with known client certificates: the fingerprints of their client certificates are very long passwords (unlike the tokens) but in the end, just as easily passed on to friends (even though we usually don’t pass both certificate and private key on to others that’s still an option). The documentation now comes with an example setup.


Comments on 2020-07-13 Client Certificates and IO::Socket::SSL (Perl)

I left a comment on the mailing list.

This is the short summary I provided:

In the TOFU world, the default OpenSSL setup doesn’t quite work. The default is that clients don’t send their client certificates to the server unless the server asks for them during the handshake. In the OpenSSL world, the server can be told to do this by telling it to verify the client certificate. When you do that, the server will then reject the connection because the client certificate is self-signed. In order to force the server to still accept it, you need to provide your own verification callback which simply returns 1 for all certificates in the chain of certificates the client presents you with.

“SSL_set_verify() sets the verification flags for ssl to be mode and specifies the verify_callback function to be used. If no callback function shall be specified, the NULL pointer can be used for verify_callback. In this case last verify_callback set specifically for this ssl remains.”

Keywords to look for in your SSL or TLS library’s documentation are “peer verification”, “verification mode”, “verification callback”, etc.

Once you have all that, then you can get the fingerprint of the client cert on the server side and compare it to the list of fingerprints you know (if you’re trying to only allow some people access), or save the combination of fingerprint and common name in your database if you want to create an account (like astrobotany does), or send a 60 code back if there is no certificate, or a 61 code if the fingerprint doesn’t match anything in your database, or a 62 if you decide to do further tests such as checking the validity start date or the expiry date of the client certificate.

Hope that helps somebody

Only partially related, but here’s an interesting question: on the mailing list and on IRC, Luke Emmet said his clients was rejecting server certificates because the hostnames does not verify, the x509 didn’t match up.

What do other people think about this? My personal impression was that in a trust on first use (TOFU) world, the common name (CN) of a certificate could be anything. It could be “Alex Schroeder”, for example. Or it could be “”. Even if it was served as the certificate for another domain, like After all, the question is only whether you “trust on first use”.

My impression is that a client that tries to verify that CN and domain match is doing it wrong. What do you think? Sadly, my SSL know-how is weak, so any pointers to how things ought to work in a TOFU world are appreciated.


Add Comment

2020-07-08 Trip to the mountains

I’ve been chilling in the mountains and posting images on my @kensanata account on Mastodon. After activities and sauna, before and after lunch, I sit on the balcony or the hotel desk and hack away at Gemini Wiki. Such a happy little project!

Source code is available, of course, and it comes with what I hope is good documentation for people who would like to get started. A web view of my own installation is available for you to browse through.

I don’t feel like reading role-playing game source books up here and so I don’t. Just light hacking. 🙂


Add Comment

2020-07-04 Is Gemini from North Korea?

I was hanging out on the #gemini channel on and people noticed that Hacker News was talking about Gemini, and this got posted in a thread about a toffelblog post, A look at the Gemini protocol: a brutally simple alternative to the web.

lukee: another juicy quote from HN to entertain you “In a way Gemini could have been published by writer of European Union, North Korea or Soviet Union laws, I can’t belive this is a US products, as it contains too much to liberty constrain 😉 “

There’s more in that Hacker News thread. I don’t know what to say. How do you respond to somebody who says that there is something in common between the European Union, North Korea, the Soviet Union, and the Gemini protocol? It’s a waste of time. So how about something different... How about imagining this being a good response! How could we rephrase it and have an interesting conversation?

How about translating it into “Why is minimalism a thing? Why does it have to be mandated? Minimalism in the web has meaning because you can do otherwise. Brutalism in architecture because you could be hiding it all. The decision to show that concrete only has meaning because you could be hiding it. To mandate minimalism is simply to mandate poverty. It has no meaning.”

So, let’s imagine that; let’s reply to this imagined intelligent challenge, and hope for a good conversation.

I think the first question would be: who does the choosing? In a world where we all use the web, some authors can choose to use minimalism in web design: little CSS, simple HTML, no Javascript, no content from other domains, a focus on documents instead of imagining the web as a generic interface to all applications. It’s possible to do. The choice lies with the web author.

The choice does not lie with the reader, though! The reader who tries to surf the web with text browsers such as w3m, lynx, or eww, or with very limited browsers such as dillo, soon runs into problems. Sometimes the text itself can be read, but interaction is difficult. Headers and footers are often terrible. The list of links at the top of pages is terribly long and messy. It’s hard to find the text of a tweet if you visit the URL on Twitter. There’s too much garbage on that page! It’s also hard to find the beginning of that README if you visit a project’s page on GitHub. So many links!

The choice also does not lie with developers, surprisingly. The developer who tries to create a different browser, the developer who tries to extend HTML, the developer who tries to extend Javascript, all developers on the web soon realise that everything is fucking huge. The code base for Firefox is huge. The code base for Chromium is huge. The number of web standards is huge. The Javascript APIs are huge. The number of open issues in the bug trackers is huge. The Mozilla Developer Network (MDN) wiki is huge. Even the processes one has to follow to propose new things are huge. The hoops one has to jump through to propose new Emojis for Unicode.

And how much weight there is to all these things. How much business, money, capital, investments, jobs. Unicode is going to be forever and ever as long as we use text, I think. All those stupid pictures of technology that we no longer use are going to stay with us for the next ten thousand years. It boggles the mind.

Therefore, individual developers effectively have no choice. The number of people and the amount of capital required to create a new fully-featured web browser is unimaginable.

That’s why people like @alcinnz working on the Odysseus browser have very specific opinions on what to implement and what to drop.

That is why Gemini matters. Gemini allows many developers to write clients and servers. It gives users back choice.

I’d love to read your take on it.

Here’s what I’ve found so far:

@solderpunk wrote Why not just use a subset of HTTP and HTML?

“… to create a clearly demarcated space where people can go to consume only that kind of content in only that kind of way, which is what I think we really want.”


Add Comment



Please make sure you contribute only your own work, or work licensed under the GNU Free Documentation License. Note: in order to facilitate peer review and fight vandalism, we will store your IP number for a number of days. See Privacy Policy for more information. See Info for text formatting rules. You can edit the comment page if you need to fix typos. You can subscribe to new comments by email without leaving a comment.

To save this page you must answer this question:

Just say HELLO