Diary

Welcome! :-)

This is both a wiki (a website editable by all) and a blog (an online diary about the stuff Alex Schroeder reads and does). If you’re a friend or relative, you might be interested in reading Life instead of this page. If you’ve come here from an RPG blog, you might want to head over to RPG. There are other similar categories to be found on the SiteMap.

Für Rollenspieler gibt es ebenfalls eine eigene RSP Kategorie.

2018-09-20 RPG Campaigns Closing

A few weeks ago I noticed that we had missed a few sessions of my English RPG campaign, the one where we played through Castle of the Mad Archmage. We had made it down to level seven, the crypts. Two key players had left the campaign because of childcare issues and the remaining players had a few casuals so we missed the minimum requirement I had set at the very beginning: three players required.

And so I decided to end the campaign. No grand finale, just some emails and that was it.

I invited the two German speaking players into my German campaign. But at the same time, one of the key players in the German campaign is returning to Germany. Again, love and kids. He’s been playing in my games for about eight years. Good times!

Ah, and I ended my monthly game with the kids. The three oldest kids often had other plans on Friday evenings. That left my friend Zeno and the three younger kids, and even them it wasn’t always easy. Sessions got cancelled, and finally I decided that enough was enough.

No matter what people say, it’s the actions that count. Life goes on, people change. It’s OK. Just look at this blog! Hardly any RPG content these days!

I’m out of the loop as far as social media goes. No longer visiting Google+, and I’ve stopped visiting the forums years ago. So I’m left with Mastodon. But my main account on Octodon so much more active and my time line so much more interesting than the accounts on both Dice Camp and Tabletop Social... I just don’t get into enough RPG conversation outside of my games, and my gaming life have been fading, so I don’t know when RPG blog posts are going to come back.

😐

The blog is not going away, and hopefully the last campaign I’m in won’t close any time soon, and I’m still interested in Traveller, so who knows. I’m guessing this is just a temporary thing.

Thank you for reading and: Fight On!

Tags:

Add Comment

2018-09-20 Perl 6 Peer Review?

Does anybody feel like looking at my code and giving me some feedback? My main problem as a lone programmer is that I never know whether I’m doing it idiomatically. There’s no peer review when I’m working on my private projects away from the office.

Sometimes there’s no peer review when I’m working at the office, too. :)

So, I’m guessing there are quite a few areas where I could use some help:

  • Better ways to write things using Perl 6
  • Better ways to write things using Cro
  • Better ways to write tests
  • Better ways to organize code
  • Better ways to document code
  • Better installation instructions
  • How to make this distributable via CPAN

Code:

git clone https://alexschroeder.ch/cgit/oddmuse6

Tags:

Comments on 2018-09-20 Perl 6 Peer Review?

I made another reorganisation of the source code, trying to follow the instructions about distributing modules and thinking that the ultimate test would be to make a local installation. I somehow had the idea that this should work using zef:

zef install ./Oddmuse

But sadly, it doesn’t:

zef install ./Oddmuse
===> Testing: Oddmuse6:ver<0.0.1>:auth<kensanata>
===> Testing [OK] for Oddmuse6:ver<0.0.1>:auth<kensanata>
===> Installing: Oddmuse6:ver<0.0.1>:auth<kensanata>
===> Install [FAIL] for Oddmuse6:ver<0.0.1>:auth<kensanata>: ===SORRY!===
Could not find Oddmuse::View at line 18 in:
    /home/alex/rakudo/install/share/perl6/site
    /home/alex/rakudo/install/share/perl6/vendor
    /home/alex/rakudo/install/share/perl6
    CompUnit::Repository::AbsolutePath<94588950434432>
    CompUnit::Repository::NQP<94588953675624>
    CompUnit::Repository::Perl5<94588953675664>

===SORRY!===
Could not find Oddmuse::View at line 18 in:
    /home/alex/rakudo/install/share/perl6/site
    /home/alex/rakudo/install/share/perl6/vendor
    /home/alex/rakudo/install/share/perl6
    CompUnit::Repository::AbsolutePath<94588950434432>
    CompUnit::Repository::NQP<94588953675624>
    CompUnit::Repository::Perl5<94588953675664>

Now what?

– Alex Schroeder 2018-09-20 15:02 UTC


On #perl6 I was given the solution: my META6.json was wrong.

  1. I had a typo in the one file I had in the provides section
  2. I was missing all the other files in the provides section

I definitely will need a tool to check this!

– Alex Schroeder 2018-09-20 18:23 UTC


I kept working on that README file and I think it’s looking good! Now I’ll wait for the confirmation mail from PAUSE so that I can upload it to CPAN. At least that’s the plan. And adding more features, of course. ☺

– Alex Schroeder 2018-09-20 19:21 UTC


Heh. And I found the offline Cro documentation in the form of Markdown files deep down in the ~/.zef directory. That’s not as integrated as I would have liked but it works for me. And who knows, perhaps I’ll find the tool that allows me to browse it all.

– Alex Schroeder 2018-09-20 21:51 UTC

Add Comment

2018-09-18 Thoughts on using Perl 6

So, I’ve used Perl 6 for a few days. I’m trying to write a wiki, because that’s what I like to do, and that also what I use every day. I’ve been maintaining and running Oddmuse since 2003. I know what I want. I’ve been down every dead end, I sometimes feel. Now I want to learn the language using a real project and that’s what it is.

The current state, from a programming perspective:

  • It uses Cro as the web framework, meaning it can run stand-alone, without Apache or nginx. It’s no longer a CGI script. I expect that most people will still use a regular web server as the front end for all their applications because of caching, logging, encryption, access control and all that.
  • It uses Markdown to generate the HTML. I’ve started using Markdown more and more. I no longer recommend Wiki Creole.
  • It uses Mustache templates to generate the layout and everything around the actual wiki page “content.” No more printing as we go in order to appear faster.
  • The storage backend is a class that delegates everything to the actual storage class. Right now I’ve only implemented file-based storage, but if I keep all access going through this storage class then that will make replacing it with git or a SQLite a breeze.
  • For the moment, configuration happens via environment variables: which storage backend to use, which data directory to use if you use files as your backend, what links to show at the top, it’s all environment variables.
  • This means that translating the interface means setting a bunch of environment variables and editing the templates.
  • There are tests!
  • File locking when writing files (for the file storage backend).
  • Keeping (numbered) backups of page files.
  • Random test directories for parallel testing

The software, from a user perspective:

  • View pages written in Markdown and rendered as HTML.
  • Edit pages as Markdown.
  • Fields available: text, summary, author, and a checkbox for minor changes
  • List recent changes.
  • A rudimentary filter for recent is available as a proof of concept: it allows you to specify how many items to see.
  • IP addresses are transformed into four octets and shown as little flag icons if no author was provided. This makes it likely that you can detect who edited a bunch of pages without revealing their IP number of host name.
  • Old page revisions can be examined.

Missing features:

  • showing only the latest change for every page
  • unified layout
  • diff with old page revisions
  • merge in case of conflicts
  • expiration of old page revisions
  • admin passwords
  • banning regular expressions
  • locking the site
  • locking pages
  • question asking for first time posters
  • rolling back changes
  • RSS feed
  • tags
  • search
  • comments

Missing big ideas:

  • a plugin system
  • markup rules which users can add, ideally by merging grammars (this enables us to have rules that protect text from further processing, such as code blocks; this requires the notion of rule precedence)
  • a backend implementation using SQLite

More to come! I’m running out of time and I have to go. Here’s an idea: draft pages. They are like real pages but they don’t show up in search or feeds. Hah!

Anyway, I was talking to @tyil and thought that I should write this stuff down. Not sure who the intended audience is for these things. I feel like I’m too confused right now to understand where the problem is to be found, how can I create an issue, and where, and with what suggestion? Perhaps my future self is the audience for this blog post. One day I’ll wonder how newbies get into Perl 6 and what their issues are and I’ll be able to come back to this blog post and see myself struggling.

So, for a start, these are the things that immediately jumped out at me:

  • missing integration of Perl 6 docs into the man system
  • missing a good Cro manual for offline use (I brought the laptop along on a trip)
  • missing good search for when I know what I want but don’t know the signal, twiggils and operators

I bought Perl 6 Deep Dive by Andrew Shitov and I’m surprised at how quickly I’m running out of information in the book. It was a good read to get started and to motivate myself. Good enough to actually pull me into starting this project. That’s cool. But the next point is the sort of documentation I’ve had trouble finding.

For example:

I’m trying to have a class called Storage which delegates all the important method calls to another class which I want to load at run time. In this case, it’s going to be Storage::File. How to do it? I searched for “delegation” and the like and ended up on documents that were deprecated. I figured it would have to involve require somewhere, and then something like that “synopsis” or whatever it was. I don’t really care about the “synopsis” and “exegesis” and all the other funny ways the language was developed now that I’m actually trying to get stuff done. I’m a different kind of user, I know – but this is the kind of user that we will have to pull in. In the end I asked on the #perl6 channel on IRC and found something that works:

class Storage {
    my $class = %*ENV<storage> || 'Storage::File';
    require ::($class);
    has $!delegate handles <get-page put-page get-template put-change get-changes> = ::($class).new;
}

This illustrates two points: I might have found out about “handles” but I didn’t find it in the book. The index doesn’t mention “delegation”. Apparently this is called a “trait”. The index doesn’t mention “traits”. But even knowing all these keywords, I can’t find out about these things by starting with p6doc objects.

And that brings me to another topic: p6doc.

  • I didn’t realize that was a separate thing that I would have to install.
  • When you run p6doc --help, it tells you stuff about p6doc build and p6doc list. When you do that, you see a lot of topics. When you then try p6doc slurp or p6doc "sub slurp" you’ll get a No such type error. How does it work? What do I need to do?
  • When I run p6doc Str because I want to learn about join I find information about the vector flag. Searching for concat also doesn’t help. It must be some operator? Try p6doc operators, search for join, and then it dawns on me that it works on lists. Python: echo 'print(",".join(["1", "2"]))' | python3. Perl: perl6 -e 'say [1, 2].join(",")'. Oh well.
  • Are there native file locks? I’m looking at p6doc IO, IO, IO::Path, and I don’t see “lock” mentioned anywhere. On the #perl6 channel somebody tells me about IO::Handle. Yay!

And then there are all those little things that bite.

  • ternary operator is x ?? y !! z instead of x ? y : z
  • regular expressions are very different now
  • map requires a comma after the block
  • here docs are different

And many other things besides. As I’m too new to the language I don’t understand why things have to be “same same but different”. I hope there’s is a grandiose pay-off, I guess?

Here’s an example of how access to the hash elements no longer uses braces but angled brackets, the regular expression is different, the braces aren’t used when creating the hash, and map takes a comma after the block:

    my $menu = %*ENV<menu> || "Home, Changes";
    my @pages = $menu.split(/ ',' \s* /);
    my %params =
	id => $id,
	pages => [ map { id => $_ }, @pages ];

The Pod is also subtly different and the documentation confused me a bit. There are many options. What the suggested way of doing it? Is this good? Should I be using #| instead?

    =head3 put-page
    =begin pod
    Pages are saved in the C<page> subdirectory with the <md> extension.
    =end pod

    method put-page (Page $page!) is export {
	my $dir = %*ENV<dir> || '.';
	my $path = "$dir/page/$($page.name).md";
	spurt $path, $page.text, :enc('UTF-8');
    }

Anyway, that’s the kind of stuff I’m thinking about when I write Perl 6 code.

Oh, and recently my application has this super aggravating tendency: Often, when making changes (and this only started showing up recently!), I get an error.

♻ Restarting edit (edit)
⚠ edit ===SORRY!===
⚠ edit This type (NQPMu) does not support associative operations

The strange thing is that no matter what change I make, that fixes it. So now I have a second shell open where I just do the following:

echo >> lib/Storage/File.pm6

And then it works!

♻ Restarting edit (edit)
📓 edit Listening at http://localhost:20000

Apparently, all I have to do is trigger a recompilation.

When I’m testing, same thing.

dir=. storage=Storage::File prove6 -l -v t/edit.t

This causes an error:

===SORRY!===
This type (NQPMu) does not support associative operations
t/edit.t .. Dubious, test returned 1
No subtests run
All tests successful.

Test Summary Report
-------------------
t/edit.t (Wstat: 256 Tests: 0 Failed: 0)
Non-zero exit status: 1
  Parse errors: No plan found in TAP output
Files=1, Tests=0,  4 wallclock secs
Result: FAILED

The fix is simple:

echo >> lib/Storage/File.pm6

The result:

    ok 1 - Status is acceptable
    ok 2 - Content type is acceptable
    ok 3 - Body is acceptable
    1..3
ok 1 - GET /edit/About
    ok 1 - Status is acceptable
    ok 2 - Content type is acceptable
    ok 3 - Body is acceptable
    1..3
ok 2 - POST /save
ok 3 - page name correct
ok 4 - page content saved
ok 5 - changes correct
ok 6 - year
ok 7 - time
ok 8 - major change
ok 9 - page name
ok 10 - code
ok 11 - summary
    ok 1 - Status is acceptable
    ok 2 - Content type is acceptable
    ok 3 - Body is acceptable
    1..3
ok 12 - POST /save
ok 13 - year
ok 14 - time
ok 15 - minor change
ok 16 - page name
ok 17 - author
ok 18 - summary
1..18
t/edit.t .. ok
All tests successful.
Files=1, Tests=18,  6 wallclock secs
Result: PASS

How weird is that? I asked on the #perl6 channel and one of the people there was curious and was interested in fixing this. Sadly, I was unable to produce a small working example. I tried for quite a while. I have no idea what causes this.

Tags:

Comments on 2018-09-18 Thoughts on using Perl 6

Sadly, I was unable to produce a small working example. I tried for quite a while. I have no idea what causes this.

I can try something without a small example, as long as you can provide any example that reproduces the issue in at least a semi-reliable fashion. Basically we have a bunch of tools that can run stuff on any given rakudo commit, and bisect if necessary. Just ping me on #perl6 if you have that (a zip file or something like that will do) and I’ll try to find out if it’s a new issue or if it existed before, and we’ll see what caused it if it’s new.

– AlexDaniel 2018-09-18 22:28 UTC


I’ll keep it in mind. It’s been working for today, with no issues except for once. Who knows.

– Alex Schroeder 2018-09-19 14:41 UTC


Here’s another strange thing: locks don’t seem to work as I’d expect them to.

use File::Temp;
my $fh = open('test.p6', :w);
say "File locked: " ~ $fh.lock;
say "Deadlock: " ~ $fh.lock;

Output:

File locked: True
Deadlock: True

I sort of expected there to be a deadlock as the second call to lock never returns. But apparently not...

So I wrote my code using empty lock directories, like I did for Perl 5.

– Alex Schroeder 2018-09-19 14:44 UTC


One of the goals of the design of Perl 6 is to be more consistent.

This is why map has a comma after the block, it is just a subroutine and the block is just a lambda.

This consistency is even more evident when you look at the pointy block.

for @a -> $i { say $i }
if $a.method() -> $result { say $result }
my &lambda = -> $a { say $a }

Note that pointy blocks works with all operators of the same form as for and if.

The reason for ?? !! is that

  1. The : is used everywhere for many things, so it is very confusable.
  2. The ? is used for a variety of features itself.
  3. There is no need for !!$a because you can just use ?$a

Frankly, it is a very odd operator because it is in a category all of its own. If it wasn’t already so widespread amongst other languages it probably wouldn’t have been included at all. (similar reason for the inclusion of loop (;;){})

The Regex sub-language got a complete overhaul as it was getting very kludgy. It was also better integrated such that regular Perl 6 language features can be used rather than create regex exclusive features. (A regex in Perl 6 is just a type of method with a different base syntax)

p6doc copies perldoc in that you use the -f flag to read up on functions.

– Brad Gilbert (b2gills) 2018-09-20 23:28 UTC


Thanks!

– Alex Schroeder 2018-09-21 21:31 UTC

Add Comment

2018-09-18 Running clears the mind

Nothing clears the mind as running in the evening sun. No matter how short, no matter the view. Be it forest or trees, single trails or sidewalks. Running is a blessing and a meditation. Listen to the birds sing in spring or to the music playing in autumn. Everything is good with the air and earth and the sun and your feet.

These thoughts brought to you by Flogging Molly, Arcade Fire, some Kurdish music and Rebetika.

Tags:

Add Comment

2018-09-18 It is that time of the year again

Slowly, the end of my summer break has been creeping closer and closer. As we approach the end of September, it’s starting to pick up speed. When we get up, the sun is still lying in wait for dawn to come and pick her up. When we go for a run in the evening, the sun is meandering somewhere behind the clouds, losing itself in the sky and shadows are seeping up from the earth. Autumn is coming!

I still work a 60% job. I take off every Friday, and about four months in summer, more or less. It’s a beautiful life. I was lucky.

What have I done?

  • I worked on the Bitlbee Mastodon Plugin, adding more features: lists and filters in particular.
  • My interest in lists for Mastodon made work on Trunk, a website where you can find like minded people to follow.
  • I tinkered with Mastodon Archive.
  • I started working on an Oddmuse variant using Perl 6.
  • I didn’t like Microsoft buying GitHub and moved my issue tracking to a wiki of my own.
  • I talked to some people about cooking sites and then decided the simplest solution to have a no-tracking site full of recipes was to just start a new wiki.
  • I dropped my English RPG campaign.
  • I dropped my RPG campaign with the kids.
  • I picked up all the tools I need for sumi-e but have only picked it up once.
  • I bought three products by Teenage Engineering, the Pocket Operator Speak and the Pocket Operator K.O. as well as the OP-1 (a portable synthesizer) and have been making funny sounds with them.
  • I’ve travelled to Japan, Sweden, visited Hamburg and Murten, Barcelona (with the company)...

And soon I will be spending a few days in Scotland. And then it’s back to work!

Tags:

Add Comment

2018-09-17 Free Software vs. Open Source

Recently, Rémi Rampin (@remram44) said we should probably merge the Free Software and FLOSS lists on Trunk. I remember we used to have only FLOSS and reintroduced a separate Free Software category because of user feedback and then @aminb wrote two separate introductions to the two lists. The description for the Free Software list said: “This list aims to cater to the free software philosophy and ethical concerns around proprietary software.” The description for the FLOSS list said: “This list is for people interested in development/discussion about Free/Libre and Open Source Software.”

Rémi Rampin was not convinced. He said he wrote free software for a living and that he didn’t understand what the difference was supposed to be.

I linked to Why Open Source misses the point of Free Software by Richard Stallman, but Rémi Rampin argued that the difference between the two terms was minimal and that both the people and the discussions happening would not differ significantly as one list would be strictly included in the other.

I suggested we test this by simply looking at the overlap between these two lists. Rémi Rampin found the following: 22 people are on both lists, 5 are only in the Free Software list, and 33 are only in the FLOSS list. I left it at that.

Today I stumbled on a post by @bjoern where links to an old copy of the Open Source Initiative FAQ from the Internet Archive:

The Open Source Initiative is a marketing program for free software. It’s a pitch for “free software” on solid pragmatic grounds rather than ideological tub-thumping. The winning substance has not changed, the losing attitude and symbolism have.

And he linked me to a message by Bruce Perens from 1999:

Most hackers know that Free Software and Open Source are just two words for the same thing. Unfortunately, though, Open Source has de-emphasized the importance of the freedoms involved in Free Software. It’s time for us to fix that.

I still feel that Free Software remains a separate and important topic to talk about. It’s why I keep using the term Free Software even when many people about me use Open Source. It helps spot the scam of source-available software. There, the problem is worse: you might not have the legal rights to use, share, modify or even to compile it, and if you look at the source code, you might not be able to write similar code in order to solve the same problem because you might get sued. The necessity of clean room design tells me everything I need to know about the state of copyright these days. 🤮

Tags:

Add Comment

2018-09-12 OAuth and Mastodon

This page is mostly about trying to debug OAuth for Mastodon. Here’s the thing: I have a web application called Trunk. When you use it, you can mass-follow a number of accounts.

Sadly, I see OAuth issues piling up. I get the impression that my app mostly works, but every day I see reports by users getting “Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method”. The 3 instances I looked at reported their version to be 2.5.0 according to /api/v1/instances. What do you think I should do? How to debug this?

This is why I started writing this page. I’m hoping that some Mastodon client developer can look at the flow of things and tell me where I’m doing it wrong. Or perhaps they are running into similar issues and found a way around it? Perhaps register the app every single time? Who knows. All I know is that I’m seeing multiple reports.

If you want to look at the source code while you read this, you can find it in my repository.

The basic overview of the OAuth flow is this:

  1. Create your app in the instance and make sure you use the same scopes and redirect URL later. This gives you the client credentials client_id and client_secret.
  2. Redirect your user to the instance using the same scopes, redirect URL, and client credentials.
  3. If all went well, you’ll your redirection URL gets visited with a code parameter. Transform it into an access token ASAP (using the same parameters as before).
  4. Use the token as bearer token for each call to the mastodon API.

Here’s what this looks like in my app.

You start out with /follow_confirm. The web site says:

Please confirm
We’re going to use kensanata@octodon.social to follow all the accounts in Test.
Let’s do this
If you’d like to help us find the authorisation problems we’ve been having, please use the following link instead:
Let’s log this

When you click the link, you’re sent to the /auth route. There, we’re going to get ourselves a client. We check if the client is already registered. We do this by checking our file with all the client credentials and look for the octodon.social credentials. If we don’t find them, we’re going to register the client. in this case this is not necessary.

The /auth route now has a client. We’re going to authorise it. This means storing a bunch of cookies in the browser and then redirecting to the oauth URL of octodon.social. If the browser has the octodon.social cookie set, then no action is required. Otherwise, I need to click the big Authorize button. octodon.social then redirects back to our application, i.e. the / route, with an authorisation code.

I think this is where people are getting the error: “Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method”. They get the error from the instance, not from my web application, after all.

Screenshot of the error message

Anyway, assuming everything worked as intended... We’re back at / and we need to figure out what to do. If we are being called without a code, then we’re just going to show our main page. But in this case, we did get a code. So now we look at our cookies in order to discover what it was I was trying to accomplish. Ah! I was trying to follow the Test list, right. So we’re going to redirect to /do/follow.

The /do/follow route looks at the cookie and the parameters, again. Yep, got an account, got a list name, got a code... So now we’re going to get ourselves a client! You know how it is. The HTTP protocol is stateless. So we have to redo it all: get the client credentials from the file. This time we have a code, which is cool, so authorise the client again. Now we get the real deal: the authorisation token. Well, in my case the Mastodon::Client library handles all the painful details. But here we are, now. We have a client with the token. We’re good to go!

Now the application starts remote-following the accounts in the list. We end our journey on the /do/follow route. The URL actually contains the code but not the token. I still replaced the client_id and the authorisation code in the log with XXX.

Here’s what the log says:

[Wed Sep 12 22:58:24 2018] [debug] GET "/follow_confirm" (4fa2b1fd)
[Wed Sep 12 22:58:24 2018] [debug] Routing to a callback
[Wed Sep 12 22:58:24 2018] [debug] Rendering cached template "follow_confirm.html.ep" from DATA section
[Wed Sep 12 22:58:24 2018] [debug] Rendering cached template "layouts/default.html.ep" from DATA section
[Wed Sep 12 22:58:24 2018] [debug] 200 OK (0.003705s, 269.906/s)
[Wed Sep 12 22:58:32 2018] [debug] GET "/auth" (9f1d8f22)
[Wed Sep 12 22:58:32 2018] [debug] Routing to a callback
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social wants to authorize the 'follow' action for the Test list
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social found client credentials for octodon.social: id yes, secret yes
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social has a client: yes
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social is being redirected to https://octodon.social/oauth/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000&response_type=code&client_id=XXX&scope=follow+read+write
[Wed Sep 12 22:58:32 2018] [debug] 302 Found (0.024886s, 40.183/s)
[Wed Sep 12 22:58:32 2018] [debug] GET "/" (8a14f8db)
[Wed Sep 12 22:58:32 2018] [debug] Routing to a callback
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social is authorized for the 'follow' action using the Test list, redirecting to /do/follow?code=XXX&account=kensanata%40octodon.social&name=Test&logging=ok
[Wed Sep 12 22:58:32 2018] [debug] 302 Found (0.000799s, 1251.564/s)
[Wed Sep 12 22:58:32 2018] [debug] GET "/do/follow" (b79fbbdb)
[Wed Sep 12 22:58:32 2018] [debug] Routing to a callback
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social found client credentials for octodon.social: id yes, secret yes
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social has a client: yes
[Wed Sep 12 22:58:32 2018] [debug] kensanata@octodon.social authorizing using a code: yes
[Wed Sep 12 22:58:33 2018] [debug] kensanata@octodon.social begins to follow 2 accounts from list Test
[Wed Sep 12 22:58:33 2018] [debug] kensanata@octodon.social followed 2 accounts from list Test
[Wed Sep 12 22:58:33 2018] [debug] Rendering template "follow_done.html.ep" from DATA section
[Wed Sep 12 22:58:33 2018] [debug] Rendering cached template "layouts/default.html.ep" from DATA section
[Wed Sep 12 22:58:33 2018] [debug] 200 OK (0.762821s, 1.311/s)

Tags:

Comments on 2018-09-12 OAuth and Mastodon

Did you try with scope written with %20 instead of +?

"&scope=follow+read+write"

Other than that this call fail:

redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=code&client_id=xxxxxxxxxxxxxxxxxxx&scope=follow+read+write

If it’s not the scope, your client_id is no longer known by the instance. Did you try to register the app each time to see if the problem comes from your way of storing app clients?

https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#apps

– tom79 2018-09-13 05:41 UTC


It also fails with a fresh client id due to a server side error on this callback for code:

https://communitywiki.org/trunk?code=XXXXXXXXXXXXXXXXXXXXXXX

Header:

Location: /trunk/do/follow?code=XXXXXXXXXXXXXXXXXXXXXXX&account=tom79%40ins.mastalab.app&name=Biology&logging=

– tom79 2018-09-13 05:54 UTC


Hm, I need better logging at that point: I see in my logs that you got a 500 error:

[Thu Sep 13 07:51:40 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/do/follow?code=XXXXXXXXXXXXXXXXXXXXXXX&account=tom79%40ins.mastalab.app&name=Biology&logging= 500 0.1443s

But that’s it. As far as I can tell from here, the flow looks correct: you did get authorised or the instance would not have sent you back to the redirect URL with a code. But it did, so that worked. So now the code is about to do this:

  1. get the authorisation token using the code it just got
  2. use the token to create the list, follow people and add these people to the list

– Alex Schroeder 2018-09-13 07:03 UTC


OK, I found a problem! I currently have 159 client credentials but when I look for duplicates, there’s a problem:

qoto.org 4
mastodon.social 23
ins.mastalab.app 2
mastodon.art 3
mstdn.io 2

This is obviously a problem because only one of them is going to be the correct client_secret.

Well, at least here’s a clue!

– Alex Schroeder 2018-09-13 07:16 UTC


OK, here’s something else: I while ago I tried to follow the Test list using kensanata@octodon.social and got the error “Authorisation failed. Did you try to reload the page? This will not work since we’re not saving the access token.” And just now I tried again and it worked. Clearly not the same error (since the authorisation seems to work) but disconcerting none the less.

– Alex Schroeder 2018-09-13 07:50 UTC


Improved logging, tried following the Test list using my account on humanities.one.

“Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method”

The log:

[Thu Sep 13 10:12:49 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/follow_confirm?account=kensanata%40humanities.one&name=Test 200 0.0223s
...
[Thu Sep 13 10:12:54 2018] [debug] GET "/trunk/auth" (8d6b9c08)
[Thu Sep 13 10:12:54 2018] [debug] Routing to a callback
[Thu Sep 13 10:12:54 2018] [debug] kensanata@humanities.one wants to authorize the 'follow' action for the Test list
[Thu Sep 13 10:12:54 2018] [debug] kensanata@humanities.one found client credentials for humanities.one: id yes, secret yes
[Thu Sep 13 10:12:54 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 10:12:54 2018] [debug] kensanata@humanities.one is being redirected to https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=code&client_id=XXX&scope=follow+read+write
...
[Thu Sep 13 10:12:54 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/auth?account=kensanata%40humanities.one&action=follow&logging=ok&name=Test 302 0.0516s
...

And that’s the end of it! OK, let’s compare this with the flow I described: We have a client and we want to authorize it. We’re going to redirect the user to their instance. If they have the instance’s cookie set, they’re authorized immediately and should get back to our redirect URL (the / route).

But this is not what happens. We’re not being sent back to the redirect URL and the error is shown instead.

– Alex Schroeder 2018-09-13 08:27 UTC


I’m looking at some info @NicolasConstant kindly sent me and the URLs he uses are:

create new app:
    FORM POST
    https://[instance]/api/v1/apps

    form data: 

    client_name
    redirect_uris
    scopes
    website


redirect sign-in url:        
     https://[instance]/oauth/authorize?scope=[scopes]&response_type=code&redirect_uri=[redirect_uri]&client_id=[client_id]


access_token retrieval 
    POST
    https://[instance]/oauth/token?client_id=[client_id]&client_secret=[client_secret]&grant_type=authorization_code&code=[code]&redirect_uri=[redirect_uri]

API access : 
    In the headers:
    'Authorization': 'Bearer [access_token]'

In this case, we’re not yet at the access_token retrieval stage. We’re at the “redirect sign-in URL” stage: https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=code&client_id=XXX&scope=follow+read+write matches https://[instance]/oauth/authorize?scope=[scopes]&response_type=code&redirect_uri=[redirect_uri]&client_id=[client_id].

So why is the server rejecting it?

Let’s try the following:

Just do it again. A temporary fluke? → fail

[Thu Sep 13 11:06:46 2018] [debug] Routing to a callback
[Thu Sep 13 11:06:46 2018] [debug] kensanata@humanities.one wants to authorize the 'follow' action for the Test list
[Thu Sep 13 11:06:46 2018] [debug] kensanata@humanities.one found client credentials for humanities.one: id yes, secret yes
[Thu Sep 13 11:06:46 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 11:06:46 2018] [debug] kensanata@humanities.one is being redirected to https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=co
de&client_id=XXX&scope=follow+read+write
[Thu Sep 13 11:06:46 2018] [debug] 302 Found (0.04456s, 22.442/s)
[Thu Sep 13 11:06:46 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/auth?account=kensanata%40humanities.one&action=follow&logging=ok&name=Test 302 0.0488s

Delete the client credentials and do it again. Perhaps the client_id is no longer valid? → fail

[Thu Sep 13 11:07:21 2018] [debug] kensanata@humanities.one wants to authorize the 'follow' action for the Test list
[Thu Sep 13 11:07:21 2018] [debug] kensanata@humanities.one found no client credentials for YYY
[Thu Sep 13 11:07:22 2018] [debug] kensanata@humanities.one registered client and got new credentials for YYY: id yes, secret yes
[Thu Sep 13 11:07:22 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 11:07:22 2018] [debug] kensanata@humanities.one is being redirected to https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=code&client_id=XXX&scope=follow+read+write
[Thu Sep 13 11:07:22 2018] [debug] 302 Found (0.182976s, 5.465/s)

Oops! What’s YYY doing here? I wonder. My log writing got confused. Will rewrite this section and try again.

– Alex Schroeder 2018-09-13 09:13 UTC


Still borked:

[Thu Sep 13 11:28:07 2018] [debug] GET "/trunk/auth" (a8dae4dc)
[Thu Sep 13 11:28:07 2018] [debug] Routing to a callback
[Thu Sep 13 11:28:07 2018] [debug] kensanata@humanities.one wants to authorize the 'follow' action for the Test list
[Thu Sep 13 11:28:07 2018] [debug] kensanata@humanities.one found client credentials for humanities.one: id yes, secret yes
[Thu Sep 13 11:28:07 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 11:28:07 2018] [debug] kensanata@humanities.one is being redirected to https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=code&client_id=XXX&scope=follow+read+write
[Thu Sep 13 11:28:07 2018] [debug] 302 Found (0.043568s, 22.953/s)
[Thu Sep 13 11:28:07 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/auth?account=kensanata%40humanities.one&action=follow&logging=ok&name=Test 302 0.0520s

And after removing the credentials – it worked‽

[Thu Sep 13 11:29:38 2018] [debug] GET "/trunk/auth" (353e6e43)
[Thu Sep 13 11:29:38 2018] [debug] Routing to a callback
[Thu Sep 13 11:29:38 2018] [debug] kensanata@humanities.one wants to authorize the 'follow' action for the Test list
[Thu Sep 13 11:29:38 2018] [debug] kensanata@humanities.one found no client credentials for humanities.one
[Thu Sep 13 11:29:38 2018] [debug] kensanata@humanities.one registered client and got new credentials for humanities.one: id yes, secret yes
[Thu Sep 13 11:29:38 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 11:29:38 2018] [debug] kensanata@humanities.one is being redirected to https://humanities.one/oauth/authorize?redirect_uri=https%3A%2F%2Fcommunitywiki.org%2Ftrunk&response_type=co
de&client_id=XXX&scope=follow+read+write
[Thu Sep 13 11:29:38 2018] [debug] 302 Found (0.591784s, 1.690/s)
[Thu Sep 13 11:29:38 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/auth?account=kensanata%40humanities.one&action=follow&logging=ok&name=Test 302 0.6006s
[Thu Sep 13 11:29:42 2018] [debug] GET "/trunk" (2ca628ab)
[Thu Sep 13 11:29:42 2018] [debug] Routing to a callback
[Thu Sep 13 11:29:42 2018] [debug] kensanata@humanities.one is authorized for the 'follow' action using the Test list, redirecting to /trunk/do/follow?code=XXX&account=kensanata%40humanities.
one&name=Test&logging=ok
[Thu Sep 13 11:29:42 2018] [debug] 302 Found (0.001252s, 798.722/s)
[Thu Sep 13 11:29:42 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk?code=XXX 302 0.0078s
[Thu Sep 13 11:29:42 2018] [debug] GET "/trunk/do/follow" (1d4bad6a)
[Thu Sep 13 11:29:42 2018] [debug] Routing to a callback
[Thu Sep 13 11:29:42 2018] [debug] kensanata@humanities.one found client credentials for humanities.one: id yes, secret yes
[Thu Sep 13 11:29:42 2018] [debug] kensanata@humanities.one has a client: yes
[Thu Sep 13 11:29:42 2018] [debug] kensanata@humanities.one authorizing using a code: yes
[Thu Sep 13 11:29:42 2018] [debug] kensanata@humanities.one begins to follow 4 accounts from list Test
[Thu Sep 13 11:29:45 2018] [debug] kensanata@humanities.one followed 4 accounts from list Test
[Thu Sep 13 11:29:45 2018] [debug] Rendering template "follow_done.html.ep" from DATA section
[Thu Sep 13 11:29:45 2018] [debug] Rendering cached template "layouts/default.html.ep" from DATA section
[Thu Sep 13 11:29:45 2018] [debug] 200 OK (3.888279s, 0.257/s)
[Thu Sep 13 11:29:45 2018] [info] 178.209.50.237 GET http://communitywiki.org:8080/trunk/do/follow?code=XXX&account=kensanata%40humanities.one&name=Test&logging=ok 200 3.8928s

Hm. I am convinced that my rewriting just changed the way things are being logged. Just to be sure, though, I will delete all the client credentials (except for the one I just saved) and we’ll see whether the situation improves.

– Alex Schroeder 2018-09-13 09:38 UTC


I tested and it now works as expected.

– tom79 2018-09-13 11:15 UTC


Amazing. 🤷‍♂️

– Alex Schroeder 2018-09-13 13:51 UTC

Add Comment

2018-09-12 Copyright Fanatismus

Die Idioten haben wieder eine Schlacht gewonnen. Mehr hierzu auf Netzpolitik.

Wer war auf unserer Seite? Die Piratin Julia Reda und alle Sorte von engagierten Menschen, Parlamentarier aller Lager, Wissenschaftler. Redas Statement gibt’s auf ihrem Blog. Und trotzdem: Der bescheuerte Vorschlag von Axel Voss (CDU) wurde mit 438 zu 226 Stimmen angenommen. Ich fasse einfach nicht, wie so viele Leute dafür sein konnten.

Und was ist der nächste Schritt? Jean-Claude Juncker schlägt vor, bla bla bla Terrorbekämpfung bla bla Upload-Filterpflichten gegen „terroristische Inhalte“ bla bla bla. Kann man sich ja vorstellen.

😞

Tags:

Comments on 2018-09-12 Copyright Fanatismus

Naja, es gibt ja noch Rückzuggefechte: der genaue Text steht noch nicht fest. Die Mitglieder dürfen auch noch selber mitgestalten, und so weiter und so fort. Aber die Enttäuschung bleibt.

– Alex Schroeder 2018-09-12 12:59 UTC

Add Comment

2018-09-10 Trunk Explosion

Trunk A while ago, I wrote a little web app called Trunk. It was supposed to help beginners get started. Beginners see a bunch of people in lists and if they’re interested in the topic, they can follow all of these people. And if it failed, or if you wanted to pick and choose, no problem: you could always just click through and pick and choose yourself.

I’ve always asked for people to volunteer for being on the lists because I felt that adding people to lists without them knowing or agreeing to it is rude. It’s a surprise to be suddenly exposed like that! So people had to volunteer: message me and ask to be added to this list or that list. This was also to serve as some sort of minimum quality control: if your timeline is full of spam, I’d be able to deny that request.

Soon, a bunch of friendly people volunteered to help out. This is great. The task doesn’t require any programming, it’s all about getting messages, adding people to lists, and talking with the other admins about this list or that list. Recently one or two accounts with more reach mentioned Trunk and we were flooded with new requests.

If you’re interested in helping out, please contact me and volunteer for the admin team!

I just posted the following call for help:

We’re looking to add volunteers to help out with #Trunk administration. Mostly that means getting messages by people requesting to be added to a bunch of lists. There’s a simple admin interface where you then get to do that. There’s also a decent amount of discussion trying to figure out whether we need to add lists, what to call them, how to describe them, that sort of thing. It sometimes feel like a strange mix of philosophy and the Muppet show.

Some of the things we talk about:

  • What is the best name for a list? I had never heard of Wrenching before, for example.
  • How granular do we want this to be: do we need to have Knitting and Sewing and Crocheting or do we just need Fibre Arts? Do we list Dada or will we end up adding every art movement in history? Early on I was pointed at lumpers and splitters on Wikipedia. This discussion never ends and we might have to go back on them as the fediverse changes. We create lists for programming languages and sports now, but we don’t want to have lists for all the various diet trends: low carb, keto, paleo, etc. Perhaps that will have to change.
  • Do we add NGOs, and if we do, do we add them to a list of NGOs or do we add them to the topics they toot about? We decided to add them to a separate list of Organisations, NGOs & Associations because we want this Trunk instance to be about people connecting to other people.
  • What if the NGO wants to create a list for the NGO itself: FSF, FSFE, EFF, ACLU, Greenpeace, and what about other organisations: political parties, companies? We decided that in this case we’d like these organisations to run their own Trunk instances.
  • What about news bots? Given that we want this instance to be about newbies finding other people, we don’t want add bots.
  • What about people just boosting a lot of other people? Same argument: we’d prefer the people being boosted to volunteer for some of the lists. That would foster the people finding people aspect of it all.

That’s not to say that having lists of boosters and bots could not be useful, too. But it’s not what we want to do. The same is true for foreign languages.

I think that non-English speakers should start separate Trunk instances. Let me know if you’re interested and I’ll help you get it installed and everything. Right now I’m thinking that it doesn’t really make sense to add people that mostly toot in a foreign language. I just wonder about the numbers. It’s easy to say now that we don’t mind a French account tooting about Gamedev even if we don’t speak French, for example. But what will we do when 70% of the people in the Gamedev list are Japanese? So I suspect we’d need to have a “Gamedev 日本語” and “Gamedev Français” and so on. Maybe we’d need to translate “Gamedev” as well: “Spieleentwicklung auf Deutsch”. Soon we’re talking about lists we don’t understand to people we don’t understand... This will not scale.

I’ll try to make Trunk as easy to translate into other languages as I can. Please volunteer if you’re interested in this! 😅

Tags:

Comments on 2018-09-10 Trunk Explosion

The newer edition of Trunk now comes with a queue and a bot.

I installed the bot as @trunk and every ten minutes it connects to see if anybody mentioned it using the formula “Please add me to a comma separated list of names.” If so, it adds the request to a queue stored in the Trunk app. There, admins have an easy way to accept the request.

What this lacks is feedback! I’ll have to think of something. Right now, the bot will accept direct messages which the admins might not even see. Ideally, the admin’s action would then prompt the bot to reply, I guess.

– Alex Schroeder 2018-09-11 13:06 UTC

Add Comment

2018-09-08 What would a new Oddmuse be?

I was at the Swiss Perl Workshop and bought Perl 6 Deep Dive by Andrew Shitov. I’m again enthused by the idea of writing an application in Perl 6.

What would a new Oddmuse be like? If I’d write a new wiki using Perl 6 instead of Perl 5, I’d like to keep the name. It’d be Oddmuse 6 for a while, and then it’d simply be Oddmuse again.

Here’s how I would start:

  • install rakudo-star
  • install cro
  • create a stub ✓
  • implement a view action ✓
  • use Markdown::Text to render text ✓
  • text is loaded from text files in a page directory ✓
  • implement an edit action ✓
  • text is saved in text files ✓

I guess this is how far I got with Oddµ: A Small Wiki Written in Go, more or less. At the time I still believed in WikiCreole. But there’s more.

  • old revisions are kept in a a keep directory, like today
  • write an OpenAPI Specification as we go along
  • a log file for recent changes is still used, don’t use git or the like because I want data to be ephemeral and old revisions to fade away into oblivion
  • meta data is kept in a separate file, not mixed with the text file
  • implement a changes action
  • implement a feed action
  • implement a history action
  • implement a rollback action
  • implement a diff action including the highlighting of changed words
  • implement merging of files
  • implement error handing for merge conflicts, unlike now where the conflict is saved
  • add HTTPS
  • add passwords
  • implement banning of URL fragments and regular expressions matching text for admins
  • implement page locking and site locking for admins

This would result in a small, functional wiki. The user interface wouldn’t be template based, just like it isn’t template based right now. I think this is necessary to make it easy to mix and match extensions.

I think that, like before, we should move as much as possible into modules. So once we’re at this stage, we need to think about ways people can “compose grammars” for their text rendering.

Tags:

Comments on 2018-09-08 What would a new Oddmuse be?

Hey! :)

So it has been a while since I touched Oddmuse, but here are some of my (previous) random realizations.

  • The user interface can be wiki-based. That is, a top page can be used to render everything, as if that page was a template by itself. It can be markdown or whatever, as long as you can extend it enough to be able to call internal-ish functions from it. This is similar to sidebar / gotobar oddmuse extensions, just one tiny step ahead.
  • Another potentially crazy idea is to make any editable thing a page, e.g. settings and what not. That page does not have to be editable, e.g. it can be hardcoded internally to always be the same, but that can open a lot of potential for other awesome things.
  • Cro is the right choice, yes.
  • Grammar-based parsing would be ideal, but parsing wiki markup with grammars is a pain (one challenge is that any input is valid)… I actually experimented with it for quite a bit and even managed to do multiple grammars (e.g. basic Markdown and basic Creole) which can then be combined to achieve Markdown + Creole parsing. It does work, it can be done, but it needs quite some manpower which I currently can’t provide.
  • As of today, avoid DBIish and keep file-based db. There are multiple stability issues with DBIish, so even if you wanted to use a proper DBMS you’d regret it…
  • Maybe JSON for storing pages? I don’t know… I’m slightly worried about attacks focusing on edge cases in json parsing. But that’d be an easy and good start I guess.
  • Make page store backend as a separate module/plugin (can be in the same file though, that doesn’t matter). Just so that those who need Git backend can easily plug it in when, or so that eventually people can use a DBMS if they want to.

FWIW, I’m quite happy with most of the things in oddmuse5. So if it was the same thing, but with biggest pain points (html5, CSP, etc.) resolved, I’d be a happy user again :)

Ah, also you’d probably need caching just like in oddmuse5 (because Rakudo is currently slow-ish). It’s a useful mechanism for other things too.

– AlexDaniel 2018-09-09 16:59 UTC


Thanks for the feedback! Now all I need to do is find some time. :)

– Alex Schroeder 2018-09-10 10:25 UTC


Oddmuse 6, first commit! :)

– Alex Schroeder 2018-09-12 16:22 UTC


Rewriting it to be based on Template::Mustache. Still 100% Text::Markown. No revisions, no recent changes, no CSS. Not even page editing works. But there’s already and edit link and it shows the raw text if you click on it.

Screenshot

– Alex Schroeder 2018-09-13 15:59 UTC


Hi Alex,

I am proud I sold the book to you at the Swiss Perl Workshop 2018. Interesting blog, interesting plan. I hope you succeed and will give a good presentation about it next year at the Swiss Perl Workshop 2019.

I hope you don’t mind, but my happiness lead me to writing about you on Facebook, and I posted in the Perl6 group (which is open, no need for an account to read; an account is needed to write though).

– Wendy van Dijk 2018-09-13 18:50 UTC


Thanks! :)

– Alex Schroeder 2018-09-13 21:54 UTC


Very interesting! A small, tractable wiki in the spirit of oddmuse would be fab!

I checked out the repo, and noticed that the first paragraph of the README is quite confusing: “...based on Perl 5 ... instead of Perl 5...”

– Robert Lemmen 2018-09-14 12:03 UTC


Hah! Thanks for the heads up. Fixed!

– Alex Schroeder 2018-09-14 17:05 UTC


Editing pages works. CSS added. Tests added.

– Alex Schroeder 2018-09-17 09:02 UTC


Sadly, Git::PurePerl only does checkout and clone, Git::Simple is archived by the author and all the forks are lagging behind instead of forging ahead, Git::Wrapper leads to a 404, wow, the situation is bad for Perl 6 Modules about git. Maybe LibGit2 will do it?

sudo apt install libgit2-dev
zef install LibGit2

This fails with an error... but this works:

zef install https://github.com/CurtTilmes/perl6-libgit2.git

– Alex Schroeder 2018-09-17 11:06 UTC


I ran out of steam on LibGit2 a while back. It has the basics, but LibGit2 is really, really big (and complicated), so not everything is in there yet. – if it is missing anything you need, file an issue, and I may find some time to implement some more. It actually has a lot more functionality than is completely documented, if you can’t figure something out, file an issue and I’ll try to write some more docs. The tests can be helpful to figure out how to use it too.

I’m also trying to add more Perl 6-ish ways to do things conveniently, so if something seems harder than it should be, file an issue..

I’ll try to figure out why the install failed for you.

If you do want a database, I agree DBIish has issues, but I’m using DB::Pg with PostgreSQL pretty heavily and it has been solid for me. If anything breaks with it for you, please file an issue. I recently fixed a bug with embedded NULLs in arrays I haven’t yet released, so there will be a new release of it soon.

– Curt Tilmes 2018-09-18 01:19 UTC


Thanks!

– Alex Schroeder 2018-09-18 12:49 UTC


Anyway, I am happy to report that I have a form of recent changes:

Screenshot of Recent Changes

As you can see, the IP numbers are pseudo-anonymized and colour coded.

– Alex Schroeder 2018-09-18 13:20 UTC

Add Comment

More...

Comments


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 this page if you need to fix typos. You can subscribe to updates by email without leaving a comment.

To save this page you must answer this question:

Please say HELLO.

Referrers: Alex Schroeder 🐝 (@kensanata@octodon.social) - Octodon ZENOPUS ARCHIVES Gothridge Manor: What Are Words Worth? Diary Planet Emacsen Diary HERR ZINNLINGS ARBEITSZIMMER Dreams of Mythic Fantasy Diary