nikclayton

Mastodon: https://mastodon.social/@nikclayton

Inspired by a discussion in the joinmastodon.org Discord.

Someone's writing an app processing incoming HTTP requests with a Content-Type header. Specifically, they want to handle the case where the content type is application/json.

They noticed some clients are sending an unnecessary charset=utf-8 parameter in the content type, and wondered how they should properly handle this parameter.

They're doing this as a learning exercise so wanted to avoid leaning on an existing library if possible.

The discussion went back and forth a little bit between validation and parsing without really getting in to the meat of it, it's too long for a chat message post, and I'm not aware of a good discussion of the topic elsewhere, hence this post.

What to do?

The difference between validation and parsing

I use the terms to mean the following.

You validate incoming data to decide if your code should continue on the happy path, or branch to an error handler because the data is invalid. The incoming data will not be used again.

You parse incoming data to validate it, and to extract the information to a different type for use elsewhere in the program.

To use Pachli as an example, the code validates the content type of incoming data as application/json because if it's not then an error has occurred. The content type is not used after that.

Pachli parses a server's version string to a dedicated Version type to hold a semantic version because the version information is used elsewhere in the code to make decisions.

Parsing to the dedicated type once means the rest of the code knows the version is valid and can be reasoned about, instead of repeatedly having to re-parse the version string.

The Content-Type header

The Content-Type header specification is defined in RFC 9110 sect. 8.3. Using ABNF the grammar for the header is

token          = 1*tchar

tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
                 / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
                 / DIGIT / ALPHA
                 ; any VCHAR, except delimiters

parameters      = *( OWS ";" OWS [ parameter ] )
parameter       = parameter-name "=" parameter-value
parameter-name  = token
parameter-value = ( token / quoted-string )
  
media-type = type "/" subtype parameters
type       = token
subtype    = tokenContent-Type = media-type

Content-Type = media-type

It also notes

The type and subtype tokens are case-insensitive.

and gives these examples

text/html;charset=utf-8
Text/HTML;Charset="utf-8"
text/html; charset="utf-8"
text/html;charset=UTF-8

RFC 8259 defines the application/json media type, and is explicit that this type does not have a charset parameter, writing:

Note: No “charset” parameter is defined for this registration. Adding one really has no effect on compliant recipients.

What to do?

The original questioner can now decide whether they want to validate the header as application/json, or parse the header to a more specific set of types.

Validation

From the definition of the Content-Type header and the application/json media type we can see a sensible strategy for validation would be:

  1. Remove everything from the header value after-and-including the first occurrence of ;
  2. Trim any whitespace from the start and end of the string
  3. Perform a case-insensitive comparison of the value with application/json

Step 1 handles malformed clients or servers that emit variations of application/json;charset=utf-8 (or any other charset). While technically not correct this is unlikely to impede interoperability.

Step 2 is also for malformed clients or servers. The specification does not allow spaces before the primary type (application), and there may be spaces between the subtype and the ; that was removed in step 1. Again, removing these should be harmless and improves interoperability.

Step 3 validates the header contains the correct value.

Parsing

Parsing the header is appropriate if the application is going to branch based on the type and subtype. For example, different handlers for application/json and application/activity+json.

To do that I would create specific Python types to represent the parameters and types that can appear in a Content-Type header, and have a function that parses the header and returns one of those types.

The types should include an explicit Unknown value to indicate an unhandled type, and can either raise errors or return an additional error type.

For example:

import re
from dataclasses import dataclass

# This is an example to demonstrate parsing a value to one of a
# specific set of types, distinct from simply validating the value
# matches expectations.


@dataclass
class BaseParameter:
    """
    Base class for all parameters.

    value: Value of the parameter as it appeared in the header.
    """
    value: str


@dataclass
class Charset(BaseParameter):
    """A 'charset=...' parameter."""
    pass


@dataclass
class UnknownParameter(BaseParameter):
    """
    An unknown parameter.
    
    name: Parsed name of the parameter
    value: Parsed value of the parameter
    original: Original parameter string as it appeared in the header.
    """
    name: str
    original: str


Parameter = Charset | UnknownParameter
"""Parameters that can occur in a content-type header."""


@dataclass
class ApplicationJson:
    """The application/json content type."""
    pass


@dataclass
class ApplicationActivityPlusJson:
    """
    The application/activity+json content type

    charset: Parsed value of the 'charset' parameter, if present.
    """
    charset: Charset | None


@dataclass
class UnknownContentType:
    """
    An unknown content type.

    type: Parsed name of the type
    parameters: Parsed list of parameters, if present.
    original: Original content type as it appeared in the header.
    """
    type: str
    parameters: list[Parameter]
    original: str


ContentType = ApplicationJson | ApplicationActivityPlusJson | UnknownContentType
"""Possible content type values."""


# THIS IS NOT A PRODUCTION QUALITY PARSER
def parse_content_type(val: str) -> ContentType:
    r = re.compile('^(?P<type>[^;]+)(?:;(?P<parameters>.*))?')
    m = r.search(val)
    if m is None:
        raise ValueError
    type = m.group('type')
    if type is not None:
        type = type.lower().strip()
    match type:
        case 'application/json':
            return ApplicationJson()
        case 'application/activity+json':
            parameters = parse_parameters(m.group('parameters'))
            charset = ([e for e in parameters if isinstance(e, Charset)][:1]
                       or [None])[0]
            return ApplicationActivityPlusJson(charset)
        case _:
            parameters = parse_parameters(m.group('parameters'))
            return UnknownContentType(type, parameters, val)


# THIS IS NOT A PRODUCTION QUALITY PARSER
def parse_parameters(val: str) -> list[Parameter]:
    if val is None:
        return []

    parameter_strs = val.split(';')
    r = []
    for parameter_str in parameter_strs:
        parameter_str = parameter_str.strip()
        (name, value) = re.split(r'\s*=\s*', parameter_str, maxsplit=1)
        match name.lower():
            case 'charset':
                r.append(Charset(value.lower()))
            case _:
                r.append(UnknownParameter(name, value, parameter_str))
    return r


if __name__ == "__main__":
    print(parse_content_type('application/json'))
    print(parse_content_type('application/JSON'))
    print(parse_content_type('  application/json  '))
    print(parse_content_type('application/json ; charset=utf-8'))
    print(parse_content_type('application/activity+json'))
    print(parse_content_type('application/activity+json;charset=us-ascii'))
    print(parse_content_type('made/up; charset=utf-8; some=parameter'))

    match parse_content_type('application/activity+json;charset=us-ascii'):
        case ApplicationJson():
            print('It was some form of application/json')
        case ApplicationActivityPlusJson(charset):
            print('It was some form of application/activity+json')
            if charset is not None:
                print(f'Charset was {charset.value}')
        case UnknownContentType(type, parameters, original):
            print(f'Content type "{original}" was not recognised')

Anything else?

Additional checks could be performed.

  • Instead of ignoring the charset parameter, parse the value and confirm it is utf-8. If it is not then signal the error in some fashion so the operator of the offending client/server can be informed. For example, receiving application/json;charset=us-ascii should be a red flag somewhere.

After recent events and looking at the existing open source Android Mastodon clients I decided there's space for one more, with a specific focus.

#Pachli (website, google play, f-droid, github, mastodon)

To quote from https://pachli.app/pachli/2023/09/06/hello-pachli.html:

Pachli is an application (now) and an association (aspirational).

Pachli-the-application is a best-in-class open source Android application for Mastodon and Mastodon-like servers. If you’re familiar with Tusky then you’ll love Pachli.

Pachli-the-association is intended to provide a first class organisation to manage the development of the application under the 7 cooperative principles:

  1. Voluntary and open membership
  2. Democratic member control
  3. Member economic participation
  4. Autonomy and independence
  5. Education, Training, and Information
  6. Cooperation among Cooperatives
  7. Concern for Community

The application is a rapidly iterating fork of the popular Tusky Mastodon client, branched from version 23.0 of that application.

Pachli includes many changes from Tusky 23.0, including:

New features

  • “Load more” no more. As you scroll through your feed new posts transparently load in the background
  • Your reading position in a timeline is always preserved
  • View trending posts and links from your server
  • Use more than five tabs
  • Choose from a range of accessible fonts
  • A new media player component, supporting more video formats
  • Mark lists as “exclusive”. Posts from accounts on exclusive lists only appear in the list, not your home timeline
  • Add bookmarked posts to their own tab
  • Hide “self-boosts” from your timeline

Bug fixes

  • No more missing posts; Tusky and other apps can miss posts from your timeline, Pachli always shows the full timeline
  • Images don’t “stick” when you zoom or swipe between them
  • Search text is not deleted when you move between search tabs
  • Show server error messages, making it easier to understand why an operation failed
  • Filters now work in notifications

UX changes

  • A visual refresh and new colour scheme
  • Determine the default theme (light or dark) from your Android settings, not hardcoded to “dark”
  • Move “Clear notifications” to a menu to make it more difficult to trigger accidentally
  • Extend the list of available languages

Download Pachli from Google Play and F-Droid.

Edit to add this set of links to the posts in the series


In Update #2 on “Stepping back” I alleged that some of the public statements by the Tusky project, prompted by Stepping back from the Tusky project, were lies.

It is my responsibility to present evidence for that. This is the last of a collection of posts that do that, or otherwise respond to the public statement by the Tusky team.

[!NOTE] See also Update #3 on “Stepping back” and Update #4 on “Stepping back”

You may be reasonably concerned that the following selectively quotes from the discussion. To allay those concerns the full discussion is archived at chat.md. You might want to read that first, or have it open in another window.

I have not addressed every statement made by the project in what follows. Blame Brandolini's law. Given the volume of trivially provable lies the project's contributors made in the statement they produced I leave it to you to decide how trustworthy the remainder of their comments are.

The “Conflict with Nik Clayton” section

Nik would have expected to find documentation of the decision to hire Z, but in this case would not have been able to do so because the hiring predates our current project documentation process. - Tusky Contributor response to Nik Clayton

This is a lie. I was not asking for documentation for decision making around Mike's hiring.

I was asking why the invoice showed a payment for work that was demonstrably not done.

Even if Mike's contract was for time, not specific results, the invoice should have reflected that. E.g., “X hours spent under contract Y”.

The three invoices actually said (in their entirety):

  • “First invoice submission to Tusky”, #129964
  • “Second invoice submission to Tusky”, #123650
  • “Work on 'bookmark tab' feature (#2368); Work on 'copy hashtags into reply' (#3013); Work on 'positional substitution format' (#3297)”, #125597

When he was shown the documentation of what Z had done and why Z was paid for it, he was unable or unwilling to drop the matter - Tusky Contributor response to Nik Clayton

This is a lie. I was not shown documentation of what Mike had done.

Had I been, that's still not relevant, because my concern was not “What other work has Mike been doing?” my concern was initially “Why does invoice #125597 list work that was demonstrably not done?”, followed rapidly by “And why are these other two invoices so generic?”, then followed by “Why is the response to what should be straightforward questions so hostile?”

There are three people who had admin rights to the Open Collective project and could respond; connyduck, tak, and maloki.

Of those three, the only person who responded was maloki, and the entirety of her response pertaining to the work was the following two sentences.

It's soma, and we had an agreement that he'd be paid for the first 3 months on an hourly basis with a max ceiling. Part of their work was also offloading me, and was supposed to be working on the FAQ, but you submitted your update of it while they were working on it.

If you can straight-facedly refer to that as “shown documentation of what Mike had done”, well, you're a better dissembler than I am.

Note also that the second sentence attempts to deflect blame on to me for apparently doing work that overlapped with Mike's.

Given that my questions were not answered it is not surprising that I was “unable or unwilling to drop the matter”, and I replied:

My PR that updated the FAQ (also my first project contribution) was submitted on Dec 17 2022, and merged on Dec 22 2022. There was no public indication before then that anyone else was working on it.

The three Mike Haynes invoices on OpenCollective have the following dates and descriptions (Mike Haynes – Open Collective):

  1. January 31, 2023 (“First invoice submission to Tusky”), 120964
  2. February 16, 2023 (“Invoice for first half of February”), 123650
  3. March 1, 2023 (“Work on 'bookmark tab' feature (#2368); Work on 'copy hashtags into reply' (#3013); Work on 'positional substitution format' (#3297)”), 125597

For the first two, what work was actually done? I can't find any PRs or issues (either open or closed) in the tuskyapp/tusky or tuskyapp/faq repositories that would match, and SomaRasu (Mike Haynes) · GitHub shows no apparent contributions.

I assume there is a straightforward explanation for this, I'd just like to know what it is.

But as and when there's a legal structure backing Tusky this will not fly. Invoice 3, in particular, looks fraudulent.

To be super clear — I'm 100% not saying it is fraudulent. This could be as simple as “Some work was intended to be done, it wasn't, other work was done instead, and the expense description was not updated”.

But we should be able to explain what that work was, and we should have processes in place so that this does not happen again.

I take a moment to note that some people have suggested that “I'm 100% not saying it is fraudulent” is a weasel-worded way of trying to suggest that something is, in fact, fraudulent.

With no other context, I could understand that point of view. However, this is not a context-free discussion. At this point I have been working on the project for approximately eight months, landed more than 150 PRs, replied to countless user questions from the Tusky account, and run the two most recent releases.

And the project's statement says they had “the intent of admin status being transitioned to him” and described me as the “primary code contributor”.

With that context it should be clear that raising any concerns like this are not an accusation of wrong doing, but an expression of concern that other people, without the relevant context, may look at the same information I did and assume fraud.

Maloki did not take it that way, responding:

Can You stop? Because let it be clear, this attitude would mean that all my contribution also isn't valid and shouldn't have been paid for because there wasn't any proof. This is an admin issue. The admins had agreements in place with people involved.

This is the first suggestion in the discussion that any of Maloki's contributions are questionable, and she raised it, not me.

I was not the only person to think like this. Charles (charlag) wrote in response to the above:

mal with you I think it was quite different. I don't know anything about this (which is probably my fault) and to me it also looks very odd.

if in your case it's a regular payment in this case it does really look like payment for work that wasn't done. I think big point of OC is transparency and this does not look good for us.

should it have been labelled differently? as in, “project management work” or similar (I assume you mean that by offloading).

I really don't think anyone is trying to put your contributions or payment into question but I agree that we should be transparent and should have a record of agreements

Charles has clearly understood the context in which I asked the original question (“I really don't think anyone is trying to put your contributions or payments into question”).

Maloki reiterates that she believes this information should not be transparent, replying to Charles:

When I'm trying to explain I really don't appreciate a fucking wall of text about how I'm clearly wrong. Like whatx the Fuck. And it's part of why we've worked on improving the process. But all these conversations aren't going to be for and open to the contributors.
And for a long while it was mostly me and Conny that had a quick chat about stuff and agreed to things. And then course corrected as needed.

During this discussion Mike writes:

I just want to say: this was work that was intended to be done, but unfortunately I couldn't get done, primarily due to my inability to wrap my head around getting a PR submitted. I did have code written for these issues, but I'm not sure if I still have them stashed or not.
I'm really sorry for any confusion I caused and for how I approached the OC invoice system. Conny had told me going forward if I were to submit any invoices that a PR(s) needed to be provided

I start a 1:1 discussion with him to see about getting any of the that he did do merged in to the project. That takes about two hours over two days, and one PR is merged the next day (more details in Update #3 on “Stepping back”). The work for the other two invoices is, to the best of my knowledge, still not complete.

Separately, I respond in the channel to Maloki.

Your explanation didn't (and still hasn't) addressed the “Why has an invoice been paid that describes work on three specific GitHub issues, when all available evidence shows that no work was done on those issues?”.

The explanation could be as simple as “Admin mistake, they worked on X, Y, and Z instead, we forgot to update the description in OC”.

This question is still unanswered.


but we as observers to the conversation feel that Maloki was generally only responding with the same harsh tone that she was being addressed in - Tusky Contributor response to Nik Clayton

I will let readers decide whether my tone was harsh. As already noted, the complete chat logs are at chat.md.

Multiple contributors had asked Nik to slow down or temporarily stop because they had no capacity to respond, for valid health reasons including hospitalization

The only people who could answer the questions authoratively were the project's three financial admins on Open Collective. They were:

  • ConnyDuck (stepped down)
  • Maloki (still active)
  • Tak (still active, did not respond during the discussion)

Maloki repeatedly asked for the discussion to stop, saying (over multiple messages):

Can You stop? [...] This is an admin issue. The admins had agreements in place with people involved.

these conversations aren't going to be for and open to the contributors.

it's not a question that necessarily should be posed in the contributors channel

the contracts and agreements we make with people is not for public consumption, and I don't believe they have to be, which is why I was very thorns out on this question. You don't need to know any details of the agreement whatsoever besides “this was a past agreement and it got paid for”.

As already noted, Charles was the first person to reply, and replied several times during the discussion despite health issues. Charles is not an active financial admin for the project on Open Collective, and the contributions (although helpful) were unasked for, and the decision to join the discussion was their own.


Nik insisted on his solution and that it be implemented immediately. - Tusky Contributor response to Nik Clayton

This is a lie.

While the discussion was going I hastily organised my thoughts in to a strawman proposal for an expenses policy and a grants policy, and wrote them up. I had hoped that these would stop the discussion from spiralling down the drain of what had already happened, and allow us to productively focus on active changes we could make to stop it happening in the future.

They are clearly not ready to be “implemented immediately” as there are a number of TODOs, and a significant number of open questions for discussion in the grants policy.

I presented this to the group as:

[...] Since a more specific expense and grant policy might be useful, I've thrown these together for discussion:

As you'll see, plenty of open questions on both.

and

[...] The clearest explanation you will see of this is my strawman proposals for an expenses policy (https://gist.github.com/nikclayton/5c4118c7f153b0a8ade904cfccde5fdd) and a grants policy (https://gist.github.com/nikclayton/bd1146e24e97b113c6d14f31bbd8a36c).

Since we definitely have strong differences of opinion around project governance and finance issues, and since those documents set out my current thinking, could you review those, and provide feedback?

There was no feedback on these from anyone, and I never mentioned them again in the chat.

I know I've already said it, but to be clear, any suggestion that I had a solution and insisted it be implemented immediately is a lie.


By the time the week came to a close, Maloki felt the conversation had spiraled out of her ability to de-escalate - Tusky Contributor response to Nik Clayton

I had already made multiple attempts to de-escalate the discussion, including:

  • Working with Mike to review and approve one of his contributions
  • Suggesting clear explanations for what may have happened
  • Proposing the bones of possible future policies, using what could be learned from this issue

Maloki refused to engage with any of these. As already noted, the only thing she seemed to want to do was shut the discussion down.

Mcc and Maloki were going to introduce Nik and the other contributors to L.J. within 24 hours and let her propose a process for resolving the conflict, but did not get the chance - Tusky Contributor response to Nik Clayton

I have no idea how true this is. Obviously, I had no knowledge of it at the time.

I note that Maloki's last message in the discussion (to which I did not respond) was:

We're not tone policing the issue. We're specifically asking you to stop.

This was sent 2023-08-25 (Friday) at 13:08 CEST.

Not “pause”. Not “wait until we can gather some more information”. Not “wait until I can get a mediator”.

From my perspective the discussion was over. I had raised what I thought were reasonable concerns, in the correct forum, and been given incomplete and evasive information. Clarifying questions resulted in demands that the discussion be shut down, and my attempt to redirect the conversation to a more productive “Here's how we could do things in the future” direction were ignored.

At that point further discussion or engagement seemed both futile, and was also directly contrary to Maloki's final message.

After drafting the blog post, getting some trusted friends and former colleagues to review it, and modifying it based on their feedback, I posted a brief resignation note on the contributors channel (with instructions on how to remove me from the shared project infrastructure).


The “Payments which Nik has questioned” section

We, the remaining Tusky contributors, have several issues with Nik's post. We disagree, based on the information that both we and Nik have been given, that the payments Nik objects to were inappropriate at the time. - Tusky Contributor response to Nik Clayton

I'll be (mercifully) brief here, because this section mischaracterises what I was saying, and then argues against that mischaracterisation.

So, for the nth time — I do not think that the payments were inappropriate, given the context that was provided in the discussion.

I still think:

  1. The invoices did not follow the OC policy
  2. Maloki's response, demanding that the discussion be stopped, that it was not appropriate for contributors, and that contributors should not have a role in holding the financial admins to account is completely inappropriate

We also object to how he mischaracterizes the positions of Tusky as a project - Tusky Contributor response to Nik Clayton

I am always happy to correct anything that I've written. If anyone from the team can (a) point to something specific, (b) let me know about you think is a mischaracterisation, and why, and I agree with you, I will correct it, and post about the correction.

and feel that he retroactively imposed future standards, which would apply should we move forward with the idea to incorporate the Tusky project, to past leadership’s decisions. - Tusky Contributor response to Nik Clayton

Again, a mischaracterisation. Also, a deflection.

First, my expectations of how a financial admin should behave when responding to questions is not related to past leadership decisions.

It may be what past leadership has tolerated — I was only involved with the project for eight months, so I can't speak to that.

Second, my standards are second to the legal agreements that the project entered in to when it signed the agreements with Open Collective and the Open Source Collective (OSC) as the project's fiscal host (yes, the the fact that two different organisations have such a close name is confusing).

Unfortunately, I can't tell what the agreement the project signed says, because the financial admins don't think it's appropriate. All I can do, as I have done, is point to where I think the project may have breached them either in fact or in spirit.

The “What Does Tusky Use Donor Money For?” section

we actively solicit people outside the project when there is work which needs to be done but which no one is available to do - Tusky Contributor response to Nik Clayton

I believe, but cannot prove with 100% confidence, that this is a lie.

[!NOTE] See also Update #2 on “Stepping back” — nikclayton

Demonstrating some proof of active solicitation of people outside the project should be straightforward.

For the past four and a half years the project has paid (Tusky · Expenses – Open Collective, and ignoring the payments to Mike which I have already noted):

  • 1 payment to a Slovenian translator
  • Annual payments for the tusky.app domain registration
  • 1 payment to a Brazilian translator
  • Payments for new artwork
  • Multiple payments in 2023 to a new code contributor

The remaining payments have been to Maloki, one of the financial admins.

In those same four and a half years the project did not actively solicit people outside the project by:

  • Developing a coherent expense or grants policy, despite promising to in We are setting up a way to support us! – Open Collective and Thank you very much for your donations! – Open Collective
  • Posting updates to Tusky – Open Collective asking potential contributors who needed financial support to apply
  • Mentioning that financial support for contributors was available in the project's CONTRIBUTING.md file or README.md file.
  • Asking new contributors to the project if they needed financial support
  • Mentioning the availability of financial support in the release notes, Releases · tuskyapp/Tusky (although the release notes generally mentioned the ability to donate)
  • Mentioning it in the “About” section for the project on OpenCollective. At the time of writing this briefly describes the project and its features.
  • Mentioning it at https://www.tusky.app, the project's public website.
  • Mentioning it in the Google Play Store listing
  • Mentioning it in the F-Droid listing
  • Linking to anything describing the availability of financial support from the project's “Bio” fields in the Tusky Mastodon profile
  • Posting from the project's Mastodon account telling people to apply for financial support (searching the account's public posts for “support”, “sponsor”, “contribute”, “grant”, “expense”, “opencollective” shows no posts describing work that needs to be done in exchange for financial support)

The only other place I could think to check is if the Tusky Mastodon account received DMs from people asking for contributions.

Obviously I don't have access to that, and even if the project was receiving those requests, the public payment history on Open Collective demonstrates that the project was generally not making payments to outside contributors.

I would be delighted to document the ways in which the Tusky project actively solicited people outside the project, should the project provide any.

But until that time, I believe the project is demonstrably, repeatedly, lying to you.

Edit to add this set of links to the posts in the series


Have you seen Paddington 2? I realise I'm a little late to that particular movie, but it really is a delight. The whole production nails the tone perfectly, and never wavers. It would be so easy to drop an overly large knowing wink to the parents in the audience and risk breaking the spell for the kids, but they never do. They've constructed a world on film that works exactly how I imagine an 8 year old thinks the world works, and it's marvellous.

However, you're probably not here for the one paragraph film reviews, so, onward. In Update #2 on “Stepping back” I alleged that some of the public statements by the Tusky project, prompted by Stepping back from the Tusky project, were lies.

It is my responsibility to present evidence for that. This is one of a collection of posts that do that, or otherwise respond to the public statement by the Tusky team.

And if elzoido is reading, this one is dedicated to you. Only 48,567 words missing.

The “Admin Changes & Transitions” section

People stepping down

In June 2023, first Maloki, then Conny stepped down from the project.

This is partially false. Conny stepped down, Maloki did not, but planned to by end of the year.

This is confirmed by the project update posted to Some changes going forward – Open Collective (2023-07-28) which says:

As it stands right now, Conny is resigning entirely from working on Tusky, and maloki is still on in an advisory position and as an admin until the end of the year.

In July and August Maloki was still:

  • Managing access to the GitHub repository
    • I know this, because I contacted all the recent contributors to confirm their GitHub usernames, and forwarded them to Maloki to be added to the repository.
  • Attending Tusky team meetings (she did not attend the 2023-08-15 meeting, as she was unwell)
  • Providing feedback on how those meetings were scheduled

That's all consistent with someone who is winding down their responsibilities and hasn't transferred all of them yet.

She also said (in the Tusky Contributors channel, 2023-08-17, 15:12)

And honestly atm, I'm contributing about the same amount as I always have. 😅 Minus the stress from feeling I should be doing more. 🤷‍♀️

After the meeting, when I posted some initial results from my research in to possible legal structures for the project, Maloki was the only project member who responded, saying (2023-08-21):

I think you can gather up relevant information. If you feel like it.

I think part of the problem is often that we don't have anyone with time or energy to look into these kind of things, so it's good to at least have the research

Finally, the project had three Open Collective financial admins, Conny, Maloki, and Tak.

When I asked the initial question, and through the remainder of the discussion, Maloki was the only who replied.

I did not expect a reply from Conny, he had already stepped down. I was not surprised to see replies from Maloki, as I have explained, my understanding was that she was still engaged with aspects of the project. I am surprised that there was no reply from Tak.

To summarise; Maloki had definitely announced an intention to step back. She may have been in the process of stepping back.

But to say she had “stepped down from the project” is a lie.

Transitioning responsibilities

Conny's tasks, such as releases, were handed off to Nik; Maloki's tasks were split between Nik and Andi Mcc because they volunteered. 

Partially true, but misleading. The release process “hand off” went like this:

Connyduck: – Who wants to be responsible for the next release and follow our release process? https://github.com/tuskyapp/Tusky/blob/develop/Release.md @nikclayton I think you were interested? Nik: Happy to. My schedule for the next couple of weeks is reasonably free, so I've got the time.

(“couple of weeks” turned out to be woeful underestimate on my part. The 22.0 release was rocky, but eventually successful, in no small part thanks to a handful of users who were happy to try out some test builds while I found and fixed some longstanding account corruption bugs. If you used Tusky with multiple accounts and sometimes discovered that posts from account A were showing up from account B, or you thought you were posting from account A but you were actually posting from account B, they're fixed, you're welcome.)

I was also, like some other contributors, given access to the Tusky repository so that I could approve and merge PRs from other contributors. This happened in late April 2023, long before Conny announced that he would be stepping down, and definitely not as part of any handoff.

I can't think of any other of his responsibilities that were handed off only to me, formally or informally. I am happy to be corrected on this point.

Collectively, we did add more people to the GitHub organisation so that they could help manage the queue of issues (e.g., closing them), or be added as reviewers to PRs.

I believe “Maloki's tasks were split between Nik and Andi Mcc because they volunteered” is partially incorrect. I didn't explicitly volunteer to pick up any of Maloki's responsibilities, and was never asked to.

This might be a reference to the fact that as part of managing the 22.0 release I was given the password to the @tusky@mastodon.social account so I could post information about the new beta versions and the final releases there, and respond to questions from testers.

That was part of the release process work, it certainly wasn't part of formally picking up any of Maloki's responsibilties.

The entirety of that discussion with Maloki (2023-04-26, well before any suggestion that she was stepping down, and so part of the period when posting release announcements could reasonably be expected to be part of her role) was:

Nik: Hi – do you want to post the 22-beta.1 announcement from the @tusky account, or ship the credentials to me and I'll do it? Maloki: Talk with Conny about that.

Again, this was before Conny had announced his decision to step down. And as noted earlier, there were some tasks (like adding new users to the GitHub repository) that I couldn't do, and asked Maloki to do on my behalf.

I can't speak to whatever specific responsibilities Andi picked up, except to note she definitely took over responsibility for organising and chairing the team meetings, and did so admirably.

The next three paragraphs:

As we tried to adjust... [...] At our August 15, 2023 contributor meeting... [...] In line with the responsibilities he'd been given...

Are broadly accurate, but also omit some colour that I think is important.

First, the need for a formal legal entity for the project was not first broached at the August 15th meeting.

This was first discussed in the team meeting of 2023-05-09, over three months earlier, as part of discussing the draft expense policy. The conclusion was to take no action at that time, but keep the idea in mind.

10 days before the August 15th meeting, after Conny had announced he was stepping back, I collated my thoughts in to Open infrastructure problems, shared it with the “Tusky Contributors” channel, asked for feedback ahead of the meeting, and added it to the agenda so we had an opportunity to make an informed decision on next steps.

Second, “assigned the task” and “responsibilities he'd been given” suggests a considerably weightier process than actually happened. During the meeting I volunteered to contact SFC and other organisations, as they had already come up in my research, and no one objected / wanted to do the work themselves.

Being added to the OpenCollective account

Around this same time, Nik was given member access to our OpenCollective account, with the intent of admin status being transitioned to him down the line.

For the record, I have no recollection of ever being told about this, asked if I wanted the responsibility, or given the opportunity to decline.

I have searched through discussion archives and private messages, and can find no reference to it.

If I have missed one I am happy to be corrected, but this statement from the project is the first time I am aware of this.

Edit to add this set of links to the posts in the series


In Update #2 on “Stepping back” I alleged that some of the public statements by the Tusky project, prompted by Stepping back from the Tusky project, were lies.

It is my responsibility to present evidence for that. This is one of a collection of posts that do that, or otherwise respond to the public statement by the Tusky team.

Oh, and if you're reading this and thinking “Good God, is he really going to go through the Tusky statement sentence by sentence”, no, I'm not. It's just unfortunate that the first two are sufficiently misleading that they demand an individual response.

The “TL;DR” section

In one case, this meant that there was no publicly visible work, because the contributor did not manage to get it into a usable form, despite us helping them as much as possible

This is a reference to invoice #125597 from Mike Haynes (“Z” in the project's statement, but since the invoice is public and lists Mike's name I'm using it here). Reading that invoice is what first raised my concerns.

I do not think the quoted sentence from the project statement is a lie. I think it is (a) very misleading, and (b) unfairly blames the contributor, when the project should clearly accept responsibility for the failure.

The closest the project comes to accepting responsibility for this is buried in paragraph 18 of the statement, writing “It is a failure of our project management that no one followed up on this until this month when Nik raised the issue”.

The invoice describes work on three specific GitHub issues. Reviewing the issues showed that no work had apparently been done. More accurately, no work had been done anywhere that was visible; no PRs, no comments on issues, no questions from Mike in public or private fora that I could see.

The Open Source Collective documentation provides a clear requirement for invoices:

Invoices need a clear description of services rendered and provide sufficient detail to make it clear what the collective is paying for. This is because this description may be checked by our accountant for compliance. That means that someone with no knowledge of your project, and only a limited knowledge of open source, should be able to understand what was done from the description. - Invoice and Reimbursement Examples – Open Source Collective

An invoice that lists work on three different GitHub issues, where none of those issues, or anything related to them, shows any evidence that the work was done, does not, I think, meet those requirements.

To be crystal clear: I do not think Mike intended to defraud the project, nor do I think he did defraud the project. He submitted the information he was told to submit, and the responsible people failed to follow the proper processes. I think he was failed by the project, it is terrible that he is in this position, and he deserves a public apology. I said as much in the “Tusky Contributors” channel, writing:

And to be super clear — I do not think any of this is your fault. You wrote that you had problems getting a PR submitted; when that happened I think the project should have liased with you to make that possible, possibly by escalating to any of the other contributors and asking them to assist. That's the project's failure, not yours.

During the discussion in the “Tusky Contributors” channel Mike explained:

I just want to say: this was work that was intended to be done, but unfortunately I couldn't get done, primarily due to my inability to wrap my head around getting a PR submitted. I did have code written for these issues, but I'm not sure if I still have them stashed or not.

I'm really sorry for any confusion I caused and for how I approached the OC invoice system. Conny had told me going forward if I were to submit any invoices that a PR(s) needed to be provided

I replied:

Thanks for the info. If you do still have the code, let me know, I can work with you on the PR process and getting them submitted.

Mike took me up on that offer. For approximately two hours, across two days, I worked with Mike to understand what the technical roadblocks were so that he could complete one PR and close one of the issues mentioned in the invoice. The final PR is Adding “bookmarks” tab to timeline by SomaRasu · Pull Request #3983 · tuskyapp/Tusky · GitHub, you can confirm the timeline yourself with it.

The other two issues remain open at the time I left the project.

The project's statement acknowledges this in paragraph 18, but is careful not to acknowledge that I'm the person who worked with Mike to complete the submission.

On the code side, Z wrote code toward resolving three GitHub issues; one of these was completed to the point it could become a GitHub PR, and was eventually merged this month by Nik

After working with Mike to resolve the technical issues he was having it is my opinion that the project was not “helping them as much as possible”.

First, it only took me about two hours of on-and-off discussion with Mike to resolve the problem. In my experience working with other developers on the project I'm comfortable saying any of them would also have been able to resolve the problem too.

Second, the issues were not especially difficult to resolve.

Obviously, opinions on the technical difficulty of a particular task can reasonably differ between different people. So I have placed a copy of the entirety of my conversation with Mike helping him to submit the PR below, so that you can form your own opinion about whether the project was really “helping them as much as possible” before.

The conversation is a little technical, but when viewed within the context of “Android software developers discussing a version control system” I do not think it is unreasonably so.

If you are familiar with Git and don't want to read the whole conversation the underlying issue was that Mike had cloned the project's primary repository locally, and did not have permission to push his branch to that. After repointing the origin remote in his local repository to his fork he was able to create a PR in the normal way. This was straightforward to diagnose and correct.


Wednesday, August 23rd

Mike: Hi, I'm currently trying to push my local branch to remote, but I'm currently unable due to permissions (i was sent an invitation to collaborate but the invitation expired)

Nik: That's weird. Are you trying to push to the Tusky repository, or your own fork?

Mike: For the Tusky repository. I wasn't sure which one was the preferred practice

Nik:

  1. Make your own fork of the repository
  2. Clone your fork to your computer
  3. Create a new branch (from develop) to do you work
  4. Commit the changes to your branch
  5. Push your branch to GitHub
  6. In the GitHub UI there'll be a big option to create a PR from your branch

What does git remote -v print in your local clone?

This may be faster with a video call, if you're free in about 10 minutes

Mike:

origin https://github.com/tuskyapp/Tusky.git (fetch)
origin https://github.com/tuskyapp/Tusky.git (push)
upstream https://github.com/tuskyapp/Tusky.git (fetch)
upstream https://github.com/tuskyapp/Tusky.git (data)

i cant video call at the moment, no working mic

Nik: OK. Yeah, those the wrong origin values — that means you've cloned directly from the Tusky repo.

We can probably fix this without destroying your existing work though. Give me a couple of minutes to write up some instructions.

Mike: Ahhh okay. No problem

Nik: All your work has been done on your own branch, right? Not the develop branch?

Mike: Yes, i created my own branch

Nik: In your current working copy, run ./gradlew clean. That will remove some generated files, and make the next step a lot faster.

Make sure you don't have any uncommitted changes, run git status to check. If you do, commit them to your branch (not develop).

Switch to the develop branch, make sure you've pulled the most recent changes from the Tusky repo:

git checkout develop
git pull

Make a complete copy of your current working copy directory somewhere else (e.g., if it's checked out in to a directory called Tusky, make a copy of the Tusky directory and everything in it).

The origin remote for your repo is pointing at the Tusky repo. It needs to point to your fork. To do that, run the following:

git remote set-url origin https://github.com/SomaRasu/Tusky.git

Verify that this has worked, by running git remote -v. The expected output is:

origin  https://github.com/SomaRasu/Tusky.git (fetch)
origin  https://github.com/SomaRasu/Tusky.git (push)
upstream        https://github.com/tuskyapp/Tusky.git (fetch)
upstream        https://github.com/tuskyapp/Tusky.git (push)`

Note that the origin entries have changed, the upstream entries have not.

If that worked, switch to the branch that contains your changes (git checkout ...).

Push the changes on your branch to your fork with git push.

Open https://github.com/SomaRasu/Tusky — you should see a banner saying something like “Branch X had recent pushes, do you want to create a PR?” (something like that, I forget the exact message).

(if that worked you can delete the backup of the Tusky directory)

I'm going to be away for 4 hours or so, but do let me know how you get on

Mike: Will do. I'll give it a try in a little bit. Thanks for your help!

Thursday, August 24th

Nik: How's it coming on? I'll have some more time in about 3 hours if that helps

Mike: It seems to have gone well: I was able to submit a PR to my forked copy of the repo (right now it's going through the Bitrise check). Would you like a link to the PR?

Nik: yes please

Although you shouldn't be submitting a PR to your repo. The PR should be made against the main Tusky repo.

Mike: https://github.com/tuskyapp/Tusky/pull/3983

ahh, i see. I might have misspoke

Nik: Ok, that looks like you've created the PR in the right place

On the page for your PR, there's a “Reviewers” section at the top right. You can add me as a reviewer.

You'll see a couple of things happen on the PR pretty quickly.

  1. The “CI / Build (pull_request) action will run

That's already finished. It's reported a problem with your code. You can click the “Details” link, and if you read that you'll see that the “ktlint” step has failed. Line 95 tells you exactly what the problem is.

  1. The “reviewdog-suggester” action should run.

When that completes you should see some automatic comments on your PR with suggested fixes for the code so that it will pass the lint checks. There should be a “commit suggestion” option on the comment, which will make the suggested change to your PR.

Mike: okay: I see the issue outlined in step 1: /keylesspalace/tusky/TabData.kt:122:88 Missing spacing before "}"

I just fixed that. should i commit and push that change

Nik: If you fixed it locally on your computer, yes. If you fixed it by pressing the reviewdog button then no.

Mike: okay. i didnt see the 'commit suggestion' option in reviewdog

Nik: If you open https://github.com/nikclayton/kotlin-workflow-test/pull/1, and scroll down, do you see a comment added by “github-actions”, that shows the problem and suggests the fix, with a “Commit suggestion” button?

Oh, never mind. Damn, there needs to be a config change in the Tusky repo before that will work properly

OK, yes, commit the change and push it.

In future it's a good idea to try and remember to run the ktlintCheck action to find problems, and ktlintFormat action to fix them. But don't worry too much, I forget all the time.

Mike: ahh, okay, I'll make sure to remember that

okay, just pushed. waiting on the checks to re-run

Nik: Meanwhile, up at the top right of your PR you should see a “Reviewers” section, with no reviewers listed. You can go ahead and add me.

And if this PR fixes one or more open GitHub issue you should add them as the last line of the PR description (the “...” menu at the top right of the PR description will let you edit it).

This should look like:

Fixes #1234

Or

Fixes #1234, #4567, ...

if it fixes multiple open issues.

When you do that GitHub will do two things:

  1. It adds a link from the issue back to this PR
  2. When the PR is merged it will automatically close those issues

Mike: For some reason, I dont get an option to suggest a reviewer

Nik: Ah. Today I learned that PR submitters can only add reviewers if they have write access to the repo. No worries, I'll add myself

Mike: ahh, i see

Nik: That “Fixes #2368” that you've added should be the last line on the PR description (the very first text block in the PR, not a new comment). Use the “...” menu at the top right of the description, and use the “Edit” option to change it.

Mike: Oops, sorry

Nik: No worries. And I've been able to review, approve, and merge the PR.

And if you look at https://github.com/tuskyapp/Tusky/issues/2368 you'll see that it mentions the PR, and is now marked as closed because the PR is merged.

One issue down, 454 still to go...

A handy trick is to start the names of your git branches with the ID of the issue.

So in this case something like 2368-add-bookmark-tab

That way you never have to go hunting for “Which branch did I make this particular fix on?”

Mike: Ahhh, okay. We did that at my last job. I didnt know if there was a convention for branch names

Nik: Not that the project enforces — it's purely a thing that's useful for the PR author I think, so name them however makes the most sense for you.

Mike: Gotcha

Nik: Given the discussion in “Tusky Contributors” I'd recommend a brief post in there that links to the PR too. Now that it's merged it'll be in the next Tusky nightly release, and the next full release — you don't have to do anything else to make that happen.

Edit to add this set of links to the posts in the series


Well, this is quite something. In roughly 30 years of open source contributions I have never had a group I've worked with lie about what happened.

And yet here we are. To ensure there can be no doubt about what I am alleging, the content at Tusky Contributor response to Nik Clayton – Open Collective contains multiple serious lies. Some of these are lies by omission, others are outright falsehoods.

It is my responsibility to present evidence for that. The following posts, and linked documents, is that evidence.

I will clearly indicate the statements I think are lies. Anything else is a difference of opinion at best, misleading but probably not deliberately so at worst.

The “TL;DR” section

Policy for paying contributors

The response opens with this statement.

Our OpenCollective policy has always been that we will use our budget to enable contributors who would otherwise not be able to contribute.

This is a lie.

You may want that policy to exist. It's a fine policy to have. I agree with it.

But it doesn't exist in any tangible form.

  1. In public, there is no policy potential contributors can find. Maloki and Conny Duck committed to producing a policy, but never did
  2. In private, the team acknowledged there was no policy. There was a desire to create one, but at best there was a very incomplete draft the project had not adopted

No public policy for potential contributors

There is no contributor funding policy written down, or communicated to contributors or potential contributors anywhere.

If you don't write it down, or tell anyone, it's not a policy.

In particular, there is no documented path for someone who is interested in contributing to Tusky, but unable to do so for financial reasons, to discover they can apply to the project for funding.

I looked for evidence of this policy in the following places, none of which mention a contributor funding policy.

Maloki's first post when the OpenCollective for Tusky was opened is at We are setting up a way to support us! – Open Collective (2019-01-23) and promises:

While we allow you to start supporting us immediately, we will still discuss between us and work out what people can get reimbursed for, and what is an appropriate amount etc.

which is clear that although they intend for a policy to exist, no policy exists at that time.

Conny Duck's first post to the OpenCollective is at Thank you very much for your donations! – Open Collective (2019-02-11) promises:

There will be an update on how we plan to use the money soon.

which also suggests an intent for a policy, but no policy at that time.

There were then two updates announcing the release of Tusky 5.0 and the release of Tusky 6.0. Neither of those mention a contributor policy.

There were no updates until the Some changes going forward – Open Collective post on 2023-07-28, announcing Conny Duck stepping back immediately and Maloki stepping back at the end of the year.

Which brings us to today, and the post I am responding to. Which claims there is a policy, but doesn't provide any links to it.

Finally, if this policy actually existed, I would expect that it would be mentioned to new contributors when they were making their first PRs to the project.

I can't speak to anyone else's experience, but this did not happen when I was submitting my initial PRs to the project. No policy was presented, and no one asked if I needed funding to continue contributing.

For the last several months I was also the person reviewing and merging PRs from first-time contributors. No one from the project asked me to explain an expense policy to new contributors.

In private the team knows there is no policy

Earlier this year the project started having regular meetings.

The very first meeting on 2023-03-07 was chaired by Maloki. I was taking the minutes.

The agenda (posted by Maloki) was clear there was no policy:

  1. Open Collective, and funding in general.
    1. We're missing a payment policy.

Edit to add: This was on the agenda, but we ran out of time before discussing it.

The next meeting, 2023-05-09, was organised and chaired (as all remaining meetings where) by Andi.

The agenda included this item:

  1. Connyduck: Expense policy for Open Collective, Conny and Maloki have a draft.

and the minutes note:

Draft policy is https://cryptpad.fr/pad/#/3/pad/view/60ad93fa9425cdcf36c3e8e3f953b257/

That file is probably not accessible to you. If the project acts with integrity they will agree to make it available.

In case they don't, I made a copy before I left, at https://cryptpad.fr/pad/#/2/pad/view/RsUJVNlYmFX-xw1zJgAJOr+8CIVnvAfTt66BGp6+Byc/, which should be available to you. That copy reflects the state of the file on 2023-08-27 at approximately 10pm CEST.

In case that is also not available to you I have placed a copy of the text at the bottom of this post.

The metadata for the original file shows that it was created on 2023-05-08 (i.e., the day before the contributor meeting), approximately four and a half years after Maloki and Conny Duck publicly promised there would be a policy on OpenCollective.

The “Goals” section in that document is explicit, saying “right now there is no official policy”.

I do not believe that document is the project's official policy at the time I left, for the following reasons:

  1. The filename and document title contain “In progress”
  2. The bottom of the document contains multiple open comments that have not been addressed
  3. The draft document contains content that, I think, can be read as violating the terms of the OpenCollective policy around hate and discrimination (https://docs.opencollective.com/help/about/the-open-collective-way/community-guidelines#hate-and-discrimination)
  4. The draft document contains content (around the traceability of expenses) that I believe definitely violates the OpenCollective policies (as I have previously noted)
  5. After the meeting of 2023-05-09 where was no “We have finished drafting the expense policy, here it is, please provide feedback so we can adopt it and announce it” message from Maloki or Conny Duck.

There were two more meetings, 2023-06-15 and 2023-08-15. The expense policy was not on the agenda at either of those meetings.

I made those same points in what would turn out to be my last-but-one message to the contributors channel, which was visible to all the signatories of Tusky Contributor response to Nik Clayton – Open Collective except L.J and Lola, who may or may not have been shown it by the project, I have no way of knowing.

The very best I can say is:

  1. If you were interested in contributing to Tusky, but needed financial support to do so
  2. ... and you weren't put off by the lack of any public payment policy
  3. ... and you visited the project's OpenCollective page
  4. ... and you looked at the list of expenses the project had paid
  5. ... then you may have noticed some people were being paid for contributions

That may have prompted you to contact Maloki or Conny Duck directly to ask about how to get paid for work.

That is a lot of hoops for any potential contributor to jump through, especially given the dual stigmas around (a) asking for money in the first place, and (b) asking for money for open source.

So for all those reasons I am comfortable saying:

  • The Tusky project did not have an effective expense policy for contributions at the time I left, and claims to the contrary are a lie
  • The majority of the signatories of the project's statement are fully aware that this policy did not exist, and signed on to the lie anyway

I've just written ~ 1,200 words to contradict 23. This is clearly going to need to be a series of posts rather than one. More later.


A copy of the content of https://cryptpad.fr/pad/#/2/pad/view/RsUJVNlYmFX-xw1zJgAJOr+8CIVnvAfTt66BGp6+Byc/ (original file titled “Payment policy (In progress)”)

Payment policy (In progress)

Goals:

  • Clarify what our money is used for (right now there is no official policy)

  • Limit spending so we don't run out of funds

Proposal:

We reimburse all costs necessary to keep the project running, e.g. servers and domains, and the ocassional merchandise drop to promote the project.

We also provide payment for work done on the Tusky project including, but not limited, to Coding, Codereview, Design and User support.

For a one-off contribution that is documented on GitHub, you can send us an invoice with a maximum of 100$ (needs 1 admin approval)

If you are a regular contributor, please reach out so we create an individual agreement. (needs 2 admin approval)

We encourage contributors to only request payments that are needed for them to contribute to the project. Contributors who receive a regular salary from another source should refrain from requesting payment for their work on the project.

comments:

I think a good limit is $500 per /person / month of work. (but like if you're like me and you take monye out once ever y 3 months, you still can go over.

“Payable work must be tracable”, is hard. Because I don't feel like most of my work is tracable.
Maybe we can say: tracable or otherwise agreed upon?

Not sure how to phrase something like this though, in regards to the “Contributors are encoruaged to only submit expenses if they would otherwise not be able to work on the project”: Our project works on a need over greed basis, that means if you need help to pay the rent a month we can pay for your contribution, but if you're just trying to get money out of us on top of a fulltime salary that's keeping you comfy maybe not.
I think that maybe the current phrasing is good, the one I quoted, but could be improved somehow.

Edit to add this set of links to the posts in the series


That was an interesting 24 hours or so.

First of all, if you're one of many people who boosted or commented on the original post, or left feedback appreciating any of the open source work I have done, thank you.

After the project's financial admins shutdown discussion I spent some time reassessing my concerns, making a gut-check they seemed valid. It's still a risk to bring these sorts of issues to a public forum, and I'm relieved so many of the replies and commentary I've seen elsewhere agrees that yes, this is something to be concerned about.

I said I'd collate responses to issues I'd seen raised and reply in a single post, so that's this. I'm aware it's not an ideal format, but neither is fitting each reply in to multiple 500-character posts and hoping they federate properly so everyone sees the full discussion either.

If I've missed something below please feel free to send me a DM and I'll follow up in the next update (I'm pretty certain there'll be one, if only to announce a new git repository...)


Thank you for posting this. It is indeed concerning.

Whatever you do in the future, please consider not creating your own legal entity but instead joining one of the existing (well respected) fiscal host organizations like Software Freedom Conservancy, Software in the Public Interest, etc. They can handle all of the assets, legal paperwork, liability, etc. - https://mastodon.km6g.us/@kevin/110963590931224115

Yes, that's a very important point, thanks for raising it.

To organise my thoughts about this I started framing them as pitch for a new association. The early draft is pitch.md.

There's a great deal of overlap between what I describe there and the work other organisations do, and I would prefer to work with an existing organisation than contribute to further fragmentation.

I have already contacted

After some research those seemed to be the most appropriate three, but I may have missed some, so if you, or anyone else, can recommend other organisations that would be a good fit I'd be happy to hear it.


so, to summarize, you found a payment to a contractor suspicious because you couldn't find any code from the work, and then you talked to the contractor and found the code, so your suspicions were in fact unfounded.

And also you think it's a conflict of interest for the person who does the accounting for the org to also be on the payroll for he org... like an accountant - https://social.coop/@datatitian/110964853363902591

I see several other people have already replied to you correcting the inaccurate summary in your first paragraph.

Re the second, an accountant does not generally have sole approval authority over expenses. Consider this hypothetical. A financial admin drawing significant funds from the project receives a funding proposal from someone else. Approving this proposal would reduce the amount of money available for them, so they decide to reject the proposal.

There's the conflict.


I am not certain that the first two counts are suspicious. Payment was made for work on three issues. It sounds like the person was legit, they just didn't finish the work after being paid?

The second is just silly. Project Management is a job title and job description. It is explicitly what they mean by “someone unfamiliar with your project” will recognize.

Invoices don't list specifics. There should be internal docs for that. Those docs often aren't public.

If the project (hypothetically) hired me, I'd appear on the invoice as “Policy & Regulatory Analysis”. You would NOT list the different policy issues involved on the invoice. That would be documented internally for many reasons. What the accountants need to know is “what was this money used for?”

I do not work on IT regs, so I cannot speak to specifics, but I do think that it's better to ask an attorney or accountant to look first before raising suspicions. - https://med-mastodon.com/@UncivilServant/110964976957986119

It's not “didn't finish the work”, it's “didn't do the work”. The payee may have worked on the issues in private. But if they don't push the work somewhere public, create PRs from it, link to it, etc, then it is indistinguishable from not doing the work.

As I wrote in the initial post, there was no public indication any work had been done on these issues.

In your “Policy & Regulatory Analysis” example, as an open source project, I would expect to see, at minimum, a link to the work you produced as part of that analysis. That would be “sufficient detail to make it clear what the collective is paying for”, per the OpenCollective guidelines.

I would also expect any agreements the project makes with third parties to clearly set out what the expected deliverables are.

This works even for problems where the shape of the solution is not known beforehand.

For example a bad agreement would be “Solve problem X”. After a month the payee comes back and says “Sorry, I tried lots of things, I couldn't solve it, I've got nothing to show for it, here's my invoice.”

A better agreement would be “Solve problem X. If you can't solve X, deliver detailed descriptions of the approaches you took and why they don't work, so future attempts to solve this problem can build on what you learned.”


That's a lot of book keeping and managing overhead tbh. [...]

My greatest fear of launching monetary-focused foss products is this management overhead which compared to the rest of the industry just feels so unworthy. The monetary incentives just don't add up so I can definitely see how easy it'd be to get lost here. - https://fosstodon.org/@wraptile/110965923454931957

Yes, I agree. Introducing expenses, payments, grants, etc in to a project can add a lot of overhead.

Projects should not do this unless they have the necessary people, time, and expertise to do this properly.

In other words, don't adopt processes you're not staffed to meet (a variant of a rule I described in Rules for a healthy on-call rotation as “Don't set SLOs you are not staffed to meet”).


You’re expecting the financial admins to do their work for free. If anything, you’re making yourself look bad here, and not the Open Collective. - https://eupolicy.social/@whvholst/110965996322449276

I'm definitely not expecting someone to work for free. If the only way someone can contribute to an open source project is to be paid, and the project has funds to pay them that's absolutely fine.

That does not absolve them of the responsibility to do things properly, and with accountability.


Finally, this thread:

I've said this in the past, but maybe today it's more important than it's been in a very long time to tell you.

Thank you everyone who's been supporting #Tusky over the years on open collective. You've been supporting my disabled ass through so much, and I always knew I had a little bit of money at the end of the month, even when I was the most sick. ($100 when sick, $175 when able to work)

Me and Conny worked out this agreement back sometime after I started the open collective for the project (early 2019?). I've mostly been providing support, and project managment at some points. Organizing people and texts. A lot of invisible work. . I'll always be eternally grateful for everything you've done for us and me. - https://rage.love/@maloki/110969436075202543

If this shit turns out to be a techbro devaluing the invaluable (and largely taken on by women) glue work Yet Again I will yeet my chair through my office window. - https://comicscamp.club/@nebulos/110969752596667389 (direct link broken at request of the original poster)

i mean it partially is. - https://rage.love/@maloki/110969756892267642

It really partially isn't.

I was careful in my original post not to name anyone directly, I was expecting a collective response from the Tusky project. Since Maloki has come forward in public I won't anonymise comments from her in the future. I will continue to redact the names of anyone else in the project unless they also respond separately from the project.

Having dealt with that, there's quite a lot to unpack here.

First, @maloki@rage.love is the financial admin quoted in the original post, who initially insisted the discussion be shut down, and as is apparent from these posts, also the recipient of a substantial fraction of the project's funds.

That conflict of interest should be noted.

Second, an uncharitable reading of this thread — and while I continue to assume these issues were mistakes and not a deliberate attempt to hide wrongdoing, the needle on that dial is starting to wobble — could see this as an attempt to derail this conversation away from the concerns I raised, DARVOing the discussion.

Let's not do that, please.

Third, I think a reasonable reading of “$100 when sick, $175 when able to work” is the project was paying you during months when you did no work.

That seems to be supported by invoices like #30003, “Sick-pay June-December”.

If that's a mis-read of that line I'm happy to be corrected.

If that's not a mis-read then I have a host of other questions, starting with, was the project effectively paying you a salary as an employee?

I believe you're in the UK, so even if you think you were an independent contractor, IR35 probably applies (not an accountant, but I used to run a contractor business in the UK, so I have experience with this) and this probably should have been treated as employment income.

  • Was that legal?
  • Was the project paying you at least minimum wage (GBP 10.42 an hour)
  • Was the project paying appropriate taxes and employee insurance contributions?
  • Does the project have an employee-like arrangement with anyone else?
  • Why wasn't the process for using OpenCollective to pay employees (Employment & Benefits – Open Collective Foundation) followed?

Fourth, your comment “sometime after I started the open collective for the project” prompted me to go and look at those first invoices, at Tusky · Expenses – Open Collective

Of the eight invoices on that page. two are for Tusky project expenses (domain name, stickers), one is payment to a translator, for a total of USD 129.53. The rest (USD 960) are payments to you.

An uncharitable person could think the project did not actually need the funds, and that you started the project's OpenCollective to provide a front that would allow you to collect money hoping that it wouldn't be noticed.

A charitable person could assume that is not the case, but still agree that the optics of this are terrible, especially after you repeatedly tried to shut down any discussion of the project's invoices.

Fifth, “I've mostly been providing support, and project managment at some points. Organizing people and texts. A lot of invisible work”.

Undervaluing people's contributions is an ongoing issue, and a bit of a hot-button topic. If you're new to this I recommend Being Glue — No Idea Blog.

So let me make a couple of hopefully non-controversial statements:

  1. A quid pro quo for taking money from the project for “invisible” work is it's on you to make sure the work becomes visible.

  2. If you can't describe what you're doing for the project, how is the project going to replace you when you step down?

I know what that work looks like (I'm aware of the “invisible” / “looks like” irony), I've spent years in previous roles doing project management work, I've had the good fortune to be mentored by some truly excellent project managers, and I've worked to get people successfully promoted by ensuring that the impact of their glue work was recognised.

And I've had the opportunity to learn from some epic screw ups of my own.

So here's a short list of things you could have held yourself accountable for doing:

  • Curating the list of open project issues (as in #3240 for example)
    • Following up on incomplete bug reports
    • Checking if new releases solved the reporter's problem
    • Making sure that bug reports were not being ignored
    • Applying consistent labels to make them easy to manage
  • Improving the onboarding experience for new contributors
  • Planning a release calendar, or any form of release scheduling
  • Writing the release notes for each release
  • Proactively adding new contributors to the GitHub repo
  • Finalising a complete expense policy
  • Finalising a handover document to prepare for your departure (document promised since early 2023)
  • Following up on stalled PRs
    • Had the submitter run out of time?
    • Did they not understand the review feedback?
    • Were they waiting for review and the project had slipped up?
  • Creating GitHub issues to track problems reported by users to the @tusky account
  • Communicating the state of ongoing activities to the rest of the project contributors
  • Tracking requests for comments on proposal documents
  • Liaising between the Tusky project and other Fediverse projects
    • I'm aware of your overlap with the GoToSocial project. You seemed actively hostile to any communication with the folks at Mastodon GmbH.
  • Outreach to sites comparing different Mastodon clients, ensuring their info about Tusky was accurate
  • Keeping the OpenCollective page up to date with project news
    • I asked for this multiple times — it could have been as simple as copying the release notes in to an announcement after each release.
  • Regular check-ins with each contributor to the project; how are they doing, do they have the support they need, do they have any issues that need addressing, etc
  • Responding to inaccurate or mistaken reviews on Google Play

Not all of these things were being done by the project, but the ones that were were all being done by other people. Framing your contributions as non-specific “providing support, and project managment at some points. Organizing people and texts. A lot of invisible work.” is a very effective way for other people to get the impression that you must have been involved in these things, denying the credit to the people who were actually doing the work.

I'm not for the moment suggesting you have to do all of these things. But doing one or two of them effectively would have reduced the burden on other project contributors who weren't, it seems, being paid a salary.

I'll also highlight the distinction between being accountable for something and being responsible for something.

  • Accountable – I will make sure this gets done, I will find the right people to do it
  • Responsible – I will be the person actually doing the work

You may not have been the person who could be responsible for a piece of work, but you could have been the person who was accountable for a piece of work.

The concrete things I think you did in the 8 months I was involved (often after you were prompted to do them) were:

  • Creating chat rooms
  • Organising and chairing the first team meeting (after multiple people called for one to happen)
    • Subsequent team meetings were completely handled by [redacted], who scheduled them, managed the agenda, and chaired the meetings
    • That was helpful, the first meeting was run well, and [redacted] is continuing to do an excellent job
  • When @connyduck's private message stepping down from the project was leaked I suggested the project post an announcement, and posted a draft for discussion. You wrote your own announcement and posted that, also updating OpenCollective for the first time in more than four years.
  • Occasional comments on the chat groups or on discussion documents, no different from any other contributor.
  • An attempt to form a more formal support group with [redacted] that, to the best of my knowledge, didn't achieve anything

You may also have been doing some customer support from the @tusky account at some point before early 2023. It's difficult to tell because we didn't indicate who was making each post from the account. I'm confident you did very little, or none of that, from the time that I was also posting from the account, because I would have seen it.

Sixth, most of this is the project's problem. If the agreement (can we see it?) did not state what you were expected to do beyond vague “project management” then it becomes a lot more difficult for the project to say “hey, please can you do some of the things you're being paid to do”.

But recognising that issue, and working to correct it is a responsibility I would expect you to share.

And trying to shut down any discussion is still a terrible look.

Edit to add this set of links to the posts in the series


I discovered severe lapses in how the Tusky project's donations (received via OpenCollective) were being handled. When I reported those to the project's private “Tusky Contributors” Matrix channel the financial admins tone policed the feedback, refused to engage with the concerns I and others raised, and demanded the discussion be stopped.

That's an ethical boundary I'm not going to cross, and I'm not interested in collaborating with people who have crossed that boundary and are not prepared to walk back.

Therefore I am stepping back from the Tusky project with immediate effect, and I recommend anyone who is currently donating to the project via OpenCollective reads the following, and makes their own decision on whether or not to continue financially supporting the Tusky project.

I do not have a conflict of interest; I have not taken money from the Tusky project in the past, I do not have any open expenses with the project now, and none of my income is remotely related to the Tusky project or any competing projects.

I'll be reading any replies with questions, collating the questions directed at me, and then writing a single response addressing all of them (or one per day if the questions run over multiple days).

Why were you looking in to this?

@connyduck's recent departure from the Tusky project exacerbated an ongoing issue with the ownership of project assets (domain names, website hosting, Google Play publishing account, etc). They were owned by him, sometimes entangled with his personal accounts, and would need to be transferred somewhere new so the project could continue, and to allow him to completely disengage from the project.

After discussions with other contributors I started researching options for creating a separate legal entity to own these assets on behalf of the project.

The research showed that whatever specific form the entity took it may involve some people taking on legal liability for the project's finances.

To determine what the impact of that might be I started looking into the Tusky project's OpenCollective history to see what sort of things the project was paying for, how much was being spent, what oversight there was, and so on. This would let me recommend alternatives that matched how the project was currently doing things, and highlight anything we would need to change.

What were the lapses?

OpenCollective's policies on how projects should record expenses are very clear (Invoice and Reimbursement Examples – Open Source Collective, emphasis mine):

Invoices need a clear description of services rendered and provide sufficient detail to make it clear what the collective is paying for. This is because this description may be checked by our accountant for compliance. That means that someone with no knowledge of your project, and only a limited knowledge of open source, should be able to understand what was done from the description.

Many Tusky project invoices do not do this. And in some cases they show payments for work that was demonstrably not done.

The first invoice I noticed this on was Invoice #125597, which is for:

Work on 'bookmark tab' feature (#2368); Work on 'copy hashtags into reply' (#3013); Work on 'positional substitution format' (#3297)

That's from February this year, it's now late August.

There is no publicly available evidence of any work on those issues.

  • The payee's GitHub repository (GitHub – SomaRasu/Tusky) had no indications of any work done:
    • No branches for the work
    • No commits relevant to the work
    • No PRs proposed with that work (from either this repository, or any other)
  • The three mentioned issues were still open, #2368, #3013, #3297
    • There were no comments on those issues from anyone
    • There was no indication any work was underway to resolve those issues
  • The payee had not asked for help with these issues in any Tusky public or private Matrix channels I'm a member of, or public channels I searched through

Note: After initially raising this issue I then spent approximately two hours on 2023-08-23 and 2023-08-24 mentoring the payee to fix issue #2368, and submit a PR. They did, which I reviewed, approved, and merged. The other two issues remain open at the time of writing.

Important: I do not think any of this is the payee's fault, and have told them this. I believe the project did not provide them with sufficient support, and when it became clear they could not complete the work the project could have paused the engagement, or asked one of the other contributors to assist.

Further review showed other invoices that did not “provide sufficient detail to make it clear what the collective is paying for” and allow “someone with no knowledge of your project [...] to understand what was done from the description.”.

For example, Invoice #136617. You will find many more examples at Tusky · Expenses – Open Collective.

Why is this a problem?

Obviously, there is the violation of the OpenCollective policies.

For me, it is also a violation of the project's ethical duty to responsibly and transparently handle donations and payments.

Transparency in how people are selected to receive funds, and how much to pay, is especially important in an industry rife with bias, and where women are regularly underpaid for their work.

And due to the nature of the Fediverse, Tusky attracts funding from people who have been marginalised both historically and today. I think our duty of care to be responsible and ethical stewards of the funds is even greater because of this.

Finally, a large red flag I subsequently discovered is that one of the members of the financial team managing the OpenCollective donations is a regular recipient of funds from the project. Of the USD 3,908.56 paid out by the project in the last year (2022-08-25 to 2023-08-24) (Tusky · Expenses – Open Collective) they have received USD 1,830.00 (47% of the total).

I am not suggesting those invoices are illegitimate, or the work they paid for was not done.

But I do think good financial stewardship, as well as straightforward common sense, would have recognised the obvious potential for conflict of interest, and ensured that anyone who was so dependent on the project's funds would not have any say in controlling those funds.

Raising the issue

I first flagged Invoice #125597 as a concern in a message to the private and invitation only “Tusky Contributors” Matrix channel on 2023-08-21 (Monday).

The initial response from other Tusky core contributors noted they couldn't find any information about the invoices in question, and agreeing, on the face of it, the work described in these invoices was not done.

The Tusky financial admins then responded, noted that this was for work done under a separate contract, tried to derail the conversation by raising their own expenses, and attempted to blame me, claiming that since some of my first opensource contributions to the project overlapped with work that this person under contract was supposed to be doing, my contributions caused a problem with their work

As a first time contributor to the project at the time I was unaware of this, because the work they were supposed to be doing was completely undocumented and there was no public indication that were working in the same area.

This raised more questions. In particular, I could find no traceable work from the person under contract in the Tusky project; no commits, no PRs, no issues, no comments on issues, no questions asked in the public or private channels that I'm a member of, etc.

I asked what work was actually done, flagged the risk that without more context this could appear to be fraud, and was very clear I thought this was a straightforward mistake, writing (emphasis in the original):

To be super clear — I'm 100% not saying it is fraudulent. This could be as simple as “Some work was intended to be done, it wasn't, other work was done instead, and the expense description was not updated”.

But we should be able to explain what that work was, and we should have processes in place so that this does not happen again.

The admins:

  • Told me to stop asking questions
  • Said that being required to show proof of work would call their own invoices in to question
  • Insisted this was an issue that only the admins needed to know about

Other contributors continued to question this.

In response, the admins:

  • Swore, repeatedly
  • Insisted the contracts and general process would not be available to contributors

The discussion deteriorated from there, the admins refused to answer direct but reasonable questions, insisted that the conversation should stop, and described questions as “entitled”.

At this time my specific unanswered questions are:

  • Does the project have agreements in place with anyone else?
    • If so, with who, and what are the agreements?
  • Does the project currently plan to have agreements in place with anyone else?
    • If so, with who, and what are the agreements?
  • If it is technically possible, will the project amend the notes on previous OpenCollective invoices so they meet the letter and spirit of the OpenCollective policy?
  • Will the admins put in place a formal expenses and grants policy, with a public review period before adoption?
  • Is the project paying contributors an equal equivalent hourly rate?
    • If yes, what is the rate?
    • If no, how is the effective hourly rate determined for a contributor?

If the admins had:

  • Acknowledged the concerns
  • Agreed to investigate what happened
  • Agreed to put controls in place to prevent it happening in the future
  • Agreed to not processing any further expense claims until those controls are in place

then I wouldn't be writing this now. They didn't, so I am.

Could this be an innocent mistake?

Yes. I think the Tusky financial admins have made a number of serious, but correctable mistakes. I do not think anyone set out to deliberately defraud the project, or its donors, and I was clear about that during the discussion (see earlier).

However, instead of correcting those mistakes when the issue was raised they doubled down on them.

Their position is secret agreements in place to pay out funds from the Tusky OpenCollective are OK, and project contributors and donors should not be able to discuss these agreements before, during, or after the period they run for.

Some admin quotes from the discussion:

Can You stop? [...] This is an admin issue. The admins had agreements in place with people involved.

[...]

these conversations aren't going to be for and open to the contributors.

[...]

it's not a question that necessarily should be posed in the contributors channel

[...]

the contracts and agreements we make with people is not for public consumption, and I don't believe they have to be, which is why I was very thorns out on this question. You don't need to know any details of the agreement whatsoever besides “this was a past agreement and it got paid for”.

They also said:

we've worked on improving the process

But, when asked, refused to explain what those improvements were.

As noted earlier, other project contributors also expressed their concerns in the discussion, with comments including:

to me it also looks very odd. [...] in this case it does really look like payment for work that wasn't done. I think big point of OC is transparency and this does not look good for us. [...] I agree that we should be transparent and should have a record of agreements

[...]

That said, if it doesn't comply with OC, it can't be allowed to continue to happen like that. Especially not if Tusky gets a legal entity. With likely a legally liable treasurer. Also, especially not if the original core decision makers are also stepping down.

Who are you? Why should I care about your opinions?

I'm Nik, I've been contributing to open source projects since the early 1990s. I've run large open source events with a large financial outlay (I was one of the co-founders of BSDCon Europe back in 2001). I've started open source projects, joined open source projects, been given stewardship of existing projects as maintainers move on, and handed stewardship of projects I've started over to other people.

I've been contributing to the Tusky project since December 2022. My first contribution improved the FAQ (#16). Since then I have contributed more than 150 PRs to the project. If you've been affected by bugs like:

  • The swipe sensitivity between tabs being too high (fixed: #3148)
  • Losing your place when you press “Load more” (fixed: #3000)
  • Missing notifications (fixed: #3700 (and many others))
  • Images getting “stuck” when trying to zoom or swipe between them (fixed: #3894)

then I'm the one who fixed them.

I've also improved the app's accessibility including #3003, #3121, #3272, and #3248.

As @connyduck started reducing his involvement with the project I took on the responsibility of producing new releases, managing the releases for Tusky versions 22.0 and 23.0. That includes running the release process, writing the release notes, managing the beta programme, responding to user bug reports, and so on.

At the time of writing the Tusky OpenCollective site describes me as a “Core contributor”. Per the Open Collective documentationCore contributors show up in the Team section of your page and can create events, but can't change settings or approve expenses.“, so this gives me no special access to project accounts or contracts.

For the last several months all (or very nearly all) of the posts or replies from the @tusky@mastodon.social account have been from me. And a cursory review of PRs over the last few months will show the majority of them are reviewed and merged by me as well.

In short; I know open source. I get work done. I know what good project governance looks like.

Why are you not naming names?

I think this is a collective failure of the project's financial admins, not any single individual, so the names of the individuals involved is not directly relevant.

I do not want to encourage a typical Internet pile-on of them, and any questions you have for them should be directed to the Tusky project's account, @tusky@mastodon.social (I am no longer monitoring or posting from that account).

By necessity a handful of the links above clearly show e.g., who submitted an expense, or was responsible for an approval. Again, please do not pile-on to those individuals, but send your questions about the project to @tusky@mastodon.social.

Do you have any more proof?

Yes. I have the chat logs of the “Tusky Contributors” Matrix channel that I am a member of, and where this issue was raised and dismissed.

I also have copies of the meeting minutes from the handful of Tusky contributor online meetings that have happened this year.

I will release the unredacted logs and/or meeting minutes if the the project's financial team agree, or those people make public statements that contradict what they said in private.

What's next?

I doubt the Tusky project financial admins will change direction. If they publicly commit and follow through on a transparent financial policy that meets the OpenCollective guidelines then I am prepared to rejoin the project and continue contributing. But if I expected that to happen I would not have written this.

If you are a financial contributor to the project I hope this has given you enough information to make an informed decision about where you send your money in the future.

Some Mastodon server owners are forming member-led cooperatives to host their online communities. I have been following CoSocial Community Cooperative as one example Social.Coop is another.

I think there may be space for a Mastodon app run along the same seven cooperative principles.

  1. Voluntary and open membership
  2. Democratic member control
  3. Member economic participation
  4. Autonomy and independence
  5. Education, Training, and Information
  6. Cooperation among Cooperatives
  7. Concern for Community

This incident has prompted me to investigate starting a project to do just that. More information soon.

Some people will cheerfully tell you that Mastodon is a different kind of social network because it doesn't have an algorithm.

Other people will angrily rail against this, insisting that “sorted in chronological order” is an algorithm, it's the only algorithm, it can't be changed, therefore Mastodon is bad because it doesn't provide any user controls.

Every now and then I get sufficiently annoyed by dumb takes about this to do a write-up. This is one of those times.

xkcd: Someone is wrong on the Internet

xkcd: Duty Calls

Go on then, tell me how people are wrong on the Internet

Both the positions above are wrong because they're lumping two completely different algorithms under the word “algorithm”.

Those algorithms are:

  1. How does Mastodon select the posts that will appear in your home timeline?
  2. How are the selected posts displayed?

The “Mastodon has no algorithm” crowd are actually saying “Mastodon has an algorithm, but it's fully controllable by you, nothing will show a post in your timeline that you did not explicitly ask for”.

As best as I can tell the “Mastodon absolutely has an algorithm” crowd don't care about algorithm #1 at all, and are focused on algorithm #2, insisting that “chronological ordering” is an algorithm, so that they have something to be angry online about.

There's a third algorithm too, How is an individual post on the home timeline displayed?, but people hardly ever talk about that one.

Selecting posts for your home timeline

This is the algorithm that actually matters.

You don't need to read the Mastodon code to understand how this works, you can deduce it from observation.

First:

  • Include all the top-level posts from people you follow
  • Include all the replies from people you follow
  • Include all the boosts from people you follow
  • Include all the posts containing hashtags you follow

Then:

  • Remove replies if the user has requested that
  • Remove boosts if the user has requested that
  • Remove matching boosts if the user has enabled the “Group boosts in timelines” settings
  • Remove posts from users who are actively muted
  • Remove posts from users who are blocked
  • Remove posts that match the user's filters to hide

That's at least two different ways the user can control what is initially chosen for their home timeline (users and hashtags) and at least six different ways the user can then control whether those posts appear on their timeline.

Displaying posts for your home timeline

The sort algorithm barely matters. It's “sort chronologically, newest first”. This is not modifiable by the user in Mastodon.

Mastodon does provide additional controls for the algorithm that determines how each post within the timeline can be displayed.

You can choose:

  • Whether to show media in general
  • Whether to show media that the poster marked as “sensitive”
  • Whether to automatically expand posts that have a content warning

And for posts that have been filtered at the “warn” level the user can, on a post by post basis, choose to view that post.

So, again, there's an algorithm for how each post is displayed, and the user can make choices that affect how that algorithm behaves.

Caveats

A few caveats about the above because this is the Internet and people will nit-pick to death.

First, it's describing the current observable behaviour of the Mastodon server and default web client.

Second, other clients (both web, and apps) for Mastodon servers exist, and some of those provide further controls on the presentation of your timeline.

For example, the Phanpy web client can

  • Display boosts separately to the timeline, in a carousel
  • Shows conversations in a threaded view grouped together in one block in the timeline

See Phanpy subtle UI implementations for more on that, including screenshots.

Third, Mastodon-like servers exist; federating with Mastodon servers, supporting the same basic API, but providing their own additional controls over what posts appear in your home timeline.

For example, the glitch-soc server implemented fine-grained post filters before Mastodon did. So for a while, users on a glitch-soc server had more control over their timeline than users on a Mastodon server.

In summary

  • Mastodon, and Mastodon-like servers, absolutely have an algorithm they use for selecting the items that make up your home timeline
  • The specific client you're using to access your Mastodon server might also provide its own algorithms for displaying the timeline, which you can adjust
  • Unlike other social media services (Twitter, Bluesky, Threads, etc), a Mastodon server is extremely transparent about the algorithm selecting the posts for your home timeline, and gives you full control of the parameters used in those decisions.
  • Ignore anyone arguing “Mastodon does not have an algorithm”, they lack critical thinking skills
  • Ignore anyone arguing “chronological order is an algorithm”. When it comes to social media “How are the posts you see chosen?” is a much more important question than “How are those posts ordered?”. If that's what they're focusing on then they're probably just looking for a reason to be angry about something online.
  • “I would like Mastodon to provide a new option X, which would affect how posts are chosen for my home timeline” is an interesting discussion to have. But that would require careful thought, and most people arguing online don't have time for that.

Prompted by the questions in https://hhmx.de/@nick/16376, the answer's too long to fit in a post.

Doing some monitoring how a original #Mastodon server responds, i never (!) saw the described link header! The response may include no link header or a header like this: Link: ; rel=“next”, ; rel=“prev”

You should get a Link header from any API response that returns a page of data, like https://docs.joinmastodon.org/methods/notifications/#get.

[Almost; in testing this I just discovered an edge case, which I've reported as a bug at https://github.com/mastodon/mastodon/issues/25495]

An API response for a single item, like https://docs.joinmastodon.org/methods/notifications/#get-one won't include the Link header.

I just tested this with https://restfox.dev against my mastodon.social account (https://github.com/tuskyapp/Tusky/pull/3610/files?short_path=00c65c2#diff-00c65c2349f395f9f9b78b0fe84d3638b78f3ef4abf98294364b5c4d0ef32473 has details on how to do this).

On my research i saw something in the #Tusky source, that looks like Tusky as clients seems to generate a link header? If so, why?

https://github.com/tuskyapp/Tusky/blob/develop/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsPagingSource.kt#L107-L178

That function needs to return a page of results as an HTTP Response. The code that calls that function unpacks the Response and parses the contents of the Link header, so the header must be present.

To figure out what page of results to return one of the parameters to getInitialPage is the ID of the item (in this case, a notification) that should be at the start of the page. This is the params.key value.

The Mastodon API lets you make requests like “Get me the page immediately after ID X” or “Get me the page immediately before ID X”.

But it does not let you make a request “Get me the page that includes key X”.

So the code has to do that itself, starting around line 134. It does this by making two API calls. One to retrieve the notification with ID X, and one to retrieve the notifications immediately after that.

If those calls succeed the code has two values to operate on; the notification, and the page of notifications immediately after it.

Remember that this function needs to return a single page of notifications. So it creates a fake page which contains both values. This fake page needs a Link header (because the code that calls this code expects a Link header).

So that's why it constructs one in this case.

This code is only called to get the first (or initial) page to show the user. After that the pages above and below are loaded with single calls to the Mastodon API, and return the Link header without any alterations.

Besides of details, if the header should be named “Link:” or “link:“,

HTTP headers are case-insensitive (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) so you can use either.

Tusky 22 now sometimes reports an array index out of range (-1) or similar (taking this from my memory, for exact message, i have to check it again).

That would be a bug, a detailed report with reproduction steps would be appreciated.

During development i also saw Tusky sending requests for notifications in an endless loop... wondered why.

I suspect the min_id / max_id handling in your server is not correct.

And... could someone clarify the difference between sinceid and minid? If Mastodon will handle this differently?

sinceid = String. Return results newer than this ID minid = String. Return results immediately newer than this ID

What should be the difference between “newer” and “immediately newer”?

Suppose the user has 10 notifications, numbered 1 through 10.

  1 2 3 4 5 6 7 8 9 10
  ^                 ^
  |                 |
oldest            newest

They make the request:

/api/v1/notifications?since_id=5&limit=2

This means they want two (limit=2) notifications that are newer than notification 5 (since_id=5).

The server returns notifications 9 and 10, as these are the two newest notifications.

Think of since_id as “Start at the newest notification, count backwards limit notifications, and return from there”.

Same setup, but the request is now:

/api/v1/notifications?min_id=5&limit=2

This means they want two (limit=2) notifications that are immediately newer than notification 5 (min_id=5).

The server returns notifications 6 and 7, as these are the two notifications immediately newer than 5.

Think of min_id as “Start at min_id, then return the next limit notifications”.

Given that min_id was added to the API after since_id was my working theory is that this was a design flaw in the API, which min_id fixed after the API was first released.