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-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!

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.

Missing features:

  • file locking when writing files
  • show old page revisions
  • 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
  • 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

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

2018-09-05 Toadfarm and Monit

Every now and then I wake up to a website that’s dead. When I try to restart the Toadfarm using Monit, nothing happens. I connect to the box using ssh and try to restart it from the command line:

$ ./farm reload
 * Reloading the process farm
Can't create listen socket: Address already in use at /home/alex/perl5/perlbrew/perls/perl-5.26.1/lib/site_perl/5.26.1/Mojo/IOLoop.pm line 126.
   ...fail!

So the farm is up and listening to the port but it’s not actually doing anything. Now what? They’re all asleep!

top - 07:24:55 up 150 days, 15:34,  2 users,  load average: 0.00, 0.03, 0.00
Tasks: 124 total,   2 running, 122 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.7 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.3 st
KiB Mem :  3084768 total,   336608 free,   737820 used,  2010340 buff/cache
KiB Swap:  1022972 total,       16 free,  1022956 used.  2119480 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                     
16448 alex      20   0  331468 125888   3760 S  0.0  4.1   0:00.33 /home/alex/farm             
16563 alex      20   0  378652 104660   3668 S  0.0  3.4   0:00.38 /home/alex/farm             
 6681 alex      20   0  358828  92952   3940 S  0.0  3.0   0:00.89 /home/alex/farm             
  709 alex      20   0  347792  84888   5484 S  0.0  2.8   0:01.06 /home/alex/farm             
 6730 alex      20   0  342380  74416   3940 S  0.0  2.4   0:01.13 /home/alex/farm             
  710 alex      20   0  335376  65868   5424 S  0.0  2.1   0:00.99 /home/alex/farm             
22714 alex      30  10  118320  45080   7772 S  0.0  1.5   0:00.53 perl                        
18640 alex      20   0   85272  25896   6612 S  0.0  0.8   0:00.43 perl                        
 9579 alex      20   0  343312  25568   3848 S  0.0  0.8   0:00.45 /home/alex/farm             
13534 alex      20   0  331080  25548   3848 S  0.0  0.8   0:00.09 /home/alex/farm             
 8731 alex      20   0  362272  25520   3848 S  0.0  0.8   0:00.10 /home/alex/farm             
10399 alex      20   0  330736  25500   3812 S  0.0  0.8   0:00.35 /home/alex/farm             
31772 alex      20   0  327720  25468   3848 S  0.0  0.8   0:01.46 /home/alex/farm             
 1931 alex      20   0  328808  25396   3848 S  0.0  0.8   0:00.19 /home/alex/farm             
  974 alex      20   0  329248  25380   3848 S  0.0  0.8   0:00.27 /home/alex/farm             

And so I’m doing what I usually do, without actually solving the underlying problem:

for pid in $(ps -u alex | grep /farm | cut -c 1-5); do kill $pid; done

Do you have a better idea? What’s going on, here?

Tags:

Add Comment

2018-09-05 Pick Five Video Games

This is a companion piece to Pick Five RPG Games. #Pick5Games is this: “If you had to recommend someone play 5 games to really get a feel for you/your tastes, what five would you pick?”

  • Mass Effect – I finished all three games in the series. Given how few games I actually finish, this is surely a measure of success.
  • Soul Calibur II – I think this one was the best of them all. The earlier one was clunky and the later ones are too sexualized. The women look younger and younger, the boobs look bouncier and bouncier... I don’t like that. Zeno and I played this for hours, always ending with me playing Mitsurugi and him playing the guy with the bo stick. Best beat-em-up game. Not too hard to learn but still fun.
  • Burnout: Takedown – I liked it better than the sequel (”Revenge”) and again, Zeno and I played this for hours. This was the best racer.
  • Skyrim – I’ve played this for many hours. My orc specializing in making my own enchanted weapons and armour was the best. And I finished the main quest, too. This is rare! And I still like playing it. It was much better than the previous one in the series: Oblivion got on my nerves because I was playing a bad combo (archer wood elf) and when I gained a level, everybody else kept getting stronger and stronger, and so one of the last things I did was disturb a troll at the bottom of the dungeon, then running all the way up and out onto the street until I met a guard who then proceeded to kill the troll for me. That’s when I decided that there really was no point.
  • Ultima IV – I’ve recently tried playing it again and it wasn’t as interesting as I remember it being back when I was fourteen. This was the best! Sadly, I never got the answers right at the bottom of the last dungeon (or mistyped them?) and then I decided I was done trying. I also played Ultima V for the longest time and don’t remember why I didn’t finish. I did finish Ultima VI. But really, IV was the one that showed me how great this game could be.

I spent way too much time on this silly meme! I removed Final Fantasy Tactics and Vagrant Story because Skyrim and Ultima IV were stronger influences on me. FFT reminds me: I also didn’t mention Advance Wars although I liked that a lot. Oh well.

As for racing games, I guess I also liked Project Gotham Racing. I never played any of the follow ups.

I really liked Ghost Recon Advanced Warfighter 1 + 2. Perhaps I should have put these in place of Ultima VI. Those first person shooters that moved slowly, where patience is valuable, where the number of enemies is fixed and picking them off one by one actually works. Those were good games!

I sort of liked the authentic feel of Medal of Honor: Frontline. I didn’t play any other game in the series. I remember seeing movies about World War II and finding scenes eerily similar to the game. That was well done. But the entire concept of endless waves of enemies is so stupid. That always annoys me. Same for Call of Duty8.

Tags:

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: Diary Dreams of Mythic Fantasy Alex Schroeder 🐝 (@kensanata@octodon.social) - Octodon BACK TO THE DUNGEON!: October Surprise! Ynas Midgard's RPG Blog ZENOPUS ARCHIVES Diary Gothridge Manor: What Are Words Worth? Diary